### Example Scraper Implementation in Ruby
Source: https://context7.com/openaustralia/morph-cli/llms.txt
A complete Ruby scraper that demonstrates how to extract development application data from a government website and store it in an SQLite database using the ScraperWiki library. This example showcases web scraping techniques, data cleaning, and conditional saving of records.
```ruby
require 'scraperwiki'
require 'mechanize'
starting_url = 'https://www2.bmcc.nsw.gov.au/datracking/Modules/applicationmaster/default.aspx?page=exhibit'
comment_url = 'http://www.bmcc.nsw.gov.au/sustainableliving/developmentapplicationsinnotification'
def clean_whitespace(text)
text.gsub("\r", ' ').gsub("\n", ' ').squeeze(" ").strip
end
def scrape_table(doc, comment_url)
doc.search('table tbody tr').each do |tr|
tds = tr.search('td')
h = tds.map { |td| td.inner_html }
record = {
'info_url' => (doc.uri + tds[0].at('a')['href']).to_s,
'comment_url' => comment_url,
'council_reference' => clean_whitespace(h[1]),
'on_notice_from' => Date.strptime(clean_whitespace(h[2]), "%d/%m/%Y").to_s,
'on_notice_to' => Date.strptime(clean_whitespace(h[3]), "%d/%m/%Y").to_s,
'address' => clean_whitespace(h[4].split('
')[0]) + ", " + clean_whitespace(h[5]) + ", NSW",
'description' => clean_whitespace(h[4].split('
')[1..-1].join),
'date_scraped' => Date.today.to_s
}
# Check if record already exists
if (ScraperWiki.select("* from data where `council_reference`='#{record['council_reference']}'").empty? rescue true)
puts "Saving: #{record['council_reference']}"
ScraperWiki.save_sqlite(['council_reference'], record)
else
puts "Skipping already saved record #{record['council_reference']}"
end
end
end
# Initialize Mechanize with SSL verification disabled
agent = Mechanize.new do |a|
a.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
doc = agent.get(starting_url)
scrape_table(doc, comment_url)
# Output: Data saved to data.sqlite
```
--------------------------------
### Morph CLI Command-Line Interface Examples
Source: https://context7.com/openaustralia/morph-cli/llms.txt
Provides examples of common commands used with the Morph CLI. These commands allow users to run scrapers, enable development mode, specify directories, and access help information. The Thor-based CLI is designed for flexible and efficient scraper development.
```bash
# Run scraper in current directory
morph
# Run against development server
morph --dev
# Specify directory explicitly
morph --directory=/path/to/scraper
# Show version
morph version
# Output: Morph CLI 0.2.5
# Get help
morph help
```
--------------------------------
### Morph CLI Logging Example
Source: https://context7.com/openaustralia/morph-cli/llms.txt
Demonstrates how the Morph CLI handles internal logging and outputs messages to standard output (stdout) and standard error (stderr). This is useful for understanding how to debug and monitor scraper execution within the Morph environment.
```ruby
internal_line = '{"stream":"internalout","text":"Container started"}'
MorphCLI.log(internal_line)
# Outputs to $stdout: "Container started"
```
--------------------------------
### Find All Scraper Files with Ruby
Source: https://context7.com/openaustralia/morph-cli/llms.txt
Recursively discovers all files within a specified directory, excluding hidden directories (those starting with a dot). It returns an array of relative paths suitable for packaging into an upload archive. This function is essential for identifying all necessary files for a scraper. It requires the 'morph-cli' gem.
```ruby
require 'morph-cli'
directory = "/path/to/scraper"
all_paths = MorphCLI.all_paths(directory)
# Returns array of relative paths:
# ["scraper.rb", "lib/helpers.rb", "config.yaml", "README.md"]
# Verify scraper file exists
scraper_file = all_paths.find { |file| /scraper\.[\S]+$/ =~ file }
if scraper_file
puts "Found scraper: #{scraper_file}"
else
puts "Error: No scraper file found (expected scraper.rb, scraper.py, etc.)"
exit(1)
end
```
--------------------------------
### Configuration Management with Ruby
Source: https://context7.com/openaustralia/morph-cli/llms.txt
Manages user configuration, including API keys and server URLs, storing them securely in `~/.morph` with 0600 permissions. This function allows loading existing configurations, modifying them, and saving the updated settings. It requires the 'morph-cli' gem.
```ruby
require 'morph-cli'
# Load existing or default configuration
config = MorphCLI.load_config
# Default structure:
# {
# development: { base_url: "http://127.0.0.1:3000" },
# production: { base_url: "https://morph.io" }
# }
# Add API key and save
config[:production][:api_key] = "sk_live_abc123xyz789"
MorphCLI.save_config(config)
# Configuration file is created at ~/.morph with permissions 0600
config_path = MorphCLI.config_path
puts "Config saved to: #{config_path}"
```
--------------------------------
### Create Tar Archive with Ruby
Source: https://context7.com/openaustralia/morph-cli/llms.txt
Creates a tar archive from a specified directory and list of file paths for uploading to the Morph server. It handles temporary file creation and ensures proper cleanup. This function requires the 'morph-cli' and 'archive/tar/minitar' gems. The output is a `File` object ready for HTTP upload.
```ruby
require 'morph-cli'
require 'archive/tar/minitar'
directory = "/path/to/scraper"
paths = ["scraper.rb", "lib/helpers.rb", "config.yaml"]
# Create tar archive
tar_file = MorphCLI.create_tar(directory, paths)
# Result is a File object ready for upload
# The tar is created at /tmp/out
puts "Archive created: #{tar_file.path}"
puts "Archive size: #{File.size(tar_file.path)} bytes"
# Use in HTTP upload
RestClient::Request.execute(
method: :post,
url: "https://morph.io/run",
payload: {
api_key: "your_api_key",
code: tar_file
}
)
```
--------------------------------
### Calculate Directory Size with Ruby
Source: https://context7.com/openaustralia/morph-cli/llms.txt
Computes the total size of all files specified by a list of paths within a given directory and formats this size into a human-readable string (e.g., KB, MB). This provides user feedback during the upload process. It requires the 'morph-cli' gem.
```ruby
require 'morph-cli'
directory = "/path/to/scraper"
paths = ["scraper.rb", "lib/parser.rb", "data/seeds.json"]
size = MorphCLI.get_dir_size(directory, paths)
puts "Uploading #{size}..."
# Example output: "Uploading 45.3 KB..."
# or: "Uploading 2.1 MB..."
```
--------------------------------
### Execute Morph Scraper with Ruby
Source: https://context7.com/openaustralia/morph-cli/llms.txt
Executes a scraper by uploading its code to the Morph.io server and streaming back the results in real-time. It handles authentication, error handling for unauthorized access and connection issues, and requires the 'morph-cli' and 'rest-client' gems. Input is the directory of the scraper, a boolean for development mode, and environment configuration. Output streams to stdout/stderr.
```ruby
require 'morph-cli'
# Execute scraper in current directory
directory = Dir.pwd
development = false
env_config = {
base_url: "https://morph.io",
api_key: "your_api_key_here",
timeout: 600 # 10 minutes
}
begin
MorphCLI.execute(directory, development, env_config)
rescue RestClient::Unauthorized
puts "Invalid API key - please check your credentials"
rescue Errno::ECONNREFUSED => e
puts "Cannot connect to Morph server at #{env_config[:base_url]}"
end
# Output streams to stdout/stderr in real-time:
# {"stream":"stdout","text":"Scraping started..."}
# {"stream":"stdout","text":"Found 25 records"}
# {"stream":"internalout","text":"Scraper completed successfully"}
```
--------------------------------
### Stream and Parse Log Output with Ruby
Source: https://context7.com/openaustralia/morph-cli/llms.txt
Processes streaming log output received from the Morph server. It parses each line as JSON and routes the content to the appropriate output stream (stdout or stderr). This function is crucial for displaying real-time feedback and errors during scraper execution. It requires the 'morph-cli' and 'json' gems.
```ruby
require 'morph-cli'
require 'json'
# Example log line from server
log_line = '{"stream":"stdout","text":"Processing record 1 of 100"}'
MorphCLI.log(log_line)
# Outputs to $stdout: "Processing record 1 of 100"
error_line = '{"stream":"stderr","text":"Warning: Rate limit approaching"}'
MorphCLI.log(error_line)
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.