Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[QUIZ] Current Temperature (#68)

12 views
Skip to first unread message

Ruby Quiz

unread,
Feb 24, 2006, 9:13:37 AM2/24/06
to
The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Caleb Tennis

Write a Ruby program such that given a certain argument to the program it
will return the current temperature of that location. People living in
the United States may be interested in temperature by ZIP code:

$ ruby current_temp.rb 47201
The temperature in Columbus, Indiana is 32 degrees F.

Other locales may want to use their own mailing codes, or city names:

$ ruby current_temp.rb madrid
The temperature in Madrid, Spain is 12 degrees C.

Which arguments you support is up to you.


semm...@gmail.com

unread,
Feb 26, 2006, 8:51:25 AM2/26/06
to
# Author: Shane Emmons
#
# Allows retrieval of current temperature information. Pretty simple
# and straight forward. Uses match to extract data from the xml
document
# that is returned. Sorry about the long lines, but I like putting as
# much code in one line as possible.
#
# usage: ruby current_temp.rb [zipcode|other]
#
# zipcode: US zipcode
# other: country code information. example: SPXX0050 for Madrid,
Spain

require 'net/http'

begin
weather_info = Net::HTTP.get( "xml.weather.yahoo.com",
"/forecastrss?p=".concat( ARGV[ 0 ] ) )
print "The temperature in ", weather_info.match( /Yahoo! Weather
for (.*)</ )[ 1 ], " is ", weather_info.match(
/<yweather:condition.*temp="(\d+)"/ )[ 1 ], " degrees ",
weather_info.match( /<yweather:units temperature="(.)"/ )[ 1 ], ".\n"
rescue
print "Information for ", ARGV[ 0 ], " was not found or
unavailable."
end

Dave Burt

unread,
Feb 26, 2006, 10:15:28 AM2/26/06
to
Ruby Quiz wrote:
> Write a Ruby program such that given a certain argument to the program it
> will return the current temperature of that location.

I still haven't done last week's metakoans, but I thought to myself, "Dave,"
(because I always address myself by name in my thoughts). "Dave," I thought,
"a universal Ruby-based indicator of the current temperature that works in
any location on any Ruby platform would be a great boon not only to you, but
to the entire Ruby community. In fact, why stop at the temperature? Ruby has
the power to turn almost any device into a fully functional weather station,
measuring rain, wind and snow. The world will be amazed."

So, thinking of you all, I wrote the code I now humbly present.

Cheers,
Dave.

#!/usr/bin/ruby
#
# Current Weather
#
# A response to Ruby Quiz #68 [ruby-talk:181420]
#
# This script basically turns your Ruby device into a weather machine. It
# leverages the latest technology to enable most laptops, PDAs, etc. to
capture
# meterorological metrics.
#
# WARNING: this program has a bug resulting in an infinite loop on
non-portable
# platforms.
#
# Please ONLY EXECUTE THIS PROGRAM ON PORTABLE DEVICES.
#
# Author: Dave Burt <dave at burt.id.au>
#
# Created: 23 Oct 2005
#

require 'highline/import'

# Work around bug
agree("Are you using a portable Ruby device? ") or
abort("Sorry, this program has not yet been ported to your platform.")

# Calibrate instrumentation
begin
say "Go outside."
end until agree("Are you outside now? ")

# Ascertain cloud cover
if agree("Is your Ruby device casting a defined shadow? ")
say "It's sunny."
else
say "It's overcast."
end

# Capture rainfall
if agree("Are your Ruby device or your umbrella wet? ")
say "It's raining."
else
say "It's fine."
end

# Weigh other precipitation
if agree("Is your Ruby device becoming white? ")
say "It's snowing."
else
say "It's not snowing."
end

# Discern current temperature
if agree("Are your fingers getting cold? ")
say "It's cold."
else
say "It's warm."
end

# Measure wind speed
if agree("Do you feel lateral forces on your Ruby device? ")
say "It's windy."
else
say "It's calm."
end

say "This weather report has been brought to you by Ruby, the letter D,"
say "and the number 42."


Ross Bamford

unread,
Feb 26, 2006, 10:14:58 AM2/26/06
to
I wanted to allow UK postcodes, and I've had some mad insomnia the past
couple of nights. This is some of the nastiest code I've written in a
long time, which is pretty liberating if I'm honest...

====[CUT HERE]====
#!/usr/local/bin/ruby
# Run a query for temperature based on place name or UK Postcode.
#
# Uses the BBC weather service (data from the MET office).
# See: http://www.bbc.co.uk/weather/
#
require 'net/http'
require 'uri'

unless $0 == __FILE__
raise LoadError, "You don't wanna require this..."
end

if ARGV.detect { |e| e =~ /--?h(elp)?/ }
puts <<-EOM

Syntax: ruby weather.rb [-h] [-f] [-x] [-a[select-ids]] search query

Options:

-h Show this help text.
-f Display temperatures in degrees Farenheit (default: Celsius)
-x Show eXtended report.
-a[ids] Automatically select [ids] where a search returns multiple
results. Avoids user input at runtime. Examples:

-a - Show temperature for all results
-a1 - Show the first result
-a'1 3' - Show results 1 and 3

Search Query:

The search query is constructed from all non-option arguments, and
may be one of:

* UK postcode (partial or full)
* UK town
* UK or International city
* Country

Examples:

ruby weather.rb -f ilkeston - Temp in farenheit for Ilkeston, UK
ruby weather.rb -a76 italy - Celsius temp in Rome, Italy
ruby weather.rb -a3 de7 - Celsius in Derby, UK
ruby weather.rb london - Temp in interactively-selected result
for query 'london'
ruby weather.rb -f -x -a new york - Extended report in Farenheit for all
'new york' results

EOM
exit(1)
end

RESULT_TITLE = /5 Day Forecast in (\w+) for ([^<]+)<\/title>/
MULTI_RESULT_TITLE = /Weather Centre - Search Results<\/title>/
NO_LOCS = /No locations were found for "([^"]*)"/
FIVEDAY = /5day.shtml/

# Extract result from multiple result page
EX_RESULT = /<a href="\/weather\/5day(?:_f)?.shtml\?([^"]*)" class="seasonlink"><strong>([^<]*)(?:<\/strong>)?<\/a>/

# Extract from 5day result page
EX_OVERVIEW = /">(\w+)<\/span>\s*\d+<abbr title="Temperature/
EX_TEMP = /(\d+)\s*\<abbr title="Temperature in degrees[^"]*"\>/
EX_WIND = /<br \/>(\w+) \((\d+) <abbr title="Miles per/
EX_HUMIDITY = /title="Relative humid[^:]*: (\d+)/
EX_PRESSURE = /title="Pressure in[^:]*: ([^<]+)/
EX_VISIBILITY = /Visibility<\/strong>: ([^<]+)/

# validate input
SELECT_INPUT = /^([Aa]|\d+(\s*\d+)*)$/

FARENHEIT = if ARGV.include? '-f'
ARGV.reject! { |e| e == '-f' }
true
end
AUTOSELECT = if ARGV.detect(&asp = lambda { |e| e =~ /-a([Aa]|\d+(?:\s*\d+)*)?/ })
a = $1 || 'A'
ARGV.reject!(&asp)
a
end
EXTMODE = if ARGV.include? '-x'
ARGV.reject! { |e| e == '-x' }
true
end

# Fetch and process a single URI (either search, results or 5day)
def fetch_process(uri)
case r = fetch(uri)
when Net::HTTPSuccess
process_result(r.body)
else
r.error!
end
end

# Actually fetches data from the web. All results ultimately come from
# 5day pages (new_search.pl redirects us there). We handle redirects
# here and also do URL rewriting to support Farenheit mode.
def fetch(uri_str, limit = 10)
raise ArgumentError, 'HTTP redirect too deep' if limit == 0

if FARENHEIT and uri_str =~ FIVEDAY
uri_str = uri_str.dup
uri_str[FIVEDAY] = '5day_f.shtml'
end

response = Net::HTTP.get_response(URI.parse(uri_str))
case response
when Net::HTTPSuccess then response
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
else
response.error!
end
end

# Collects multiple results from a "Search Results" page into an
# array of arrays e.g [["Some Place", "id=3309"], ["Etc", "id=2002"]]
def collect_results(body)
a = []
body.scan(EX_RESULT) { |s| a << [$2, $1] }
a
end

# The main result processing function. This handles all responses.
# If it's given a single result (a 5day page) it extracts and outputs
# the current temp. If it's a multi result page, the results are
# extracted and the user selects from them, with the resulting URL
# (a 5day) then passed to fetch_process to handle the fetch and pass
# the result back here.
def process_result(body)
if body =~ RESULT_TITLE
# this is a result
units, place = $1, $2
if body =~ EX_TEMP
temp = $1
out = if EXTMODE
overview = ((m = EX_OVERVIEW.match(body)) ? m[1] : '?')
wind_dir, wind_speed = ((m = EX_WIND.match(body)) ? m[1,2] : ['?','?'])
humidity = ((m = EX_HUMIDITY.match(body)) ? m[1] : '?')
pressure = ((m = EX_PRESSURE.match(body)) ? m[1] : '?')
visibility = ((m = EX_VISIBILITY.match(body)) ? m[1] : '?')

"\n#{place}\n" +
" Temp : #{temp} degrees #{units}\n" +
" Wind : #{wind_dir} (#{wind_speed} mph)\n" +
" Humidity (%) : #{humidity}\n" +
" Pressure (mB): #{pressure.chop}\n" +
" Visibility : #{visibility}"
else
"#{place} - #{temp} degrees #{units}"
end

puts out
else
puts "No data for #{place}"
end
elsif body =~ MULTI_RESULT_TITLE
# multiple or no result
if body =~ NO_LOCS
puts "No locations matched '#{$1}'"
else
a = collect_results(body)

if a.length > 0
unless n = AUTOSELECT
puts "Multiple results:\n"
puts " [0]\tCancel"
a.each_with_index do |e,i|
puts " [#{i+1}]\t#{e.first}"
end

puts " [A]\tAll\n\n"

begin
print "Select (separate with spaces): "
n = STDIN.gets.chomp
end until n =~ SELECT_INPUT
end

if n != '0' # 0 is cancel
n.split(' ').inject([]) do |ary,i|
if i.upcase == 'A'
ary + a.map { |e| e.last }
else
ary << a[i.to_i - 1].last
end
end.each do |id|
fetch_process("http://www.bbc.co.uk/weather/5day.shtml?#{id}")
end
end
else
puts "No usable results found"
end
end
else
puts "Unknown location"
end
end

def display_temp(q)
fetch_process("http://www.bbc.co.uk/cgi-perl/weather/search/new_search.pl?search_query=#{q}")
end

display_temp(URI.encode(ARGV.empty? ? 'ilkeston' : ARGV.join(' ')))


--
Ross Bamford - ro...@roscopeco.REMOVE.co.uk

Nola Stowe

unread,
Feb 26, 2006, 10:39:43 AM2/26/06
to
hahaha, thats great. Very entertaining Dave. Sometimes I wish I was
that funny :)


--
http://PhpGirl.blogger.com
http://CodeSnipers.com


James Edward Gray II

unread,
Feb 26, 2006, 11:37:12 AM2/26/06
to
On Feb 26, 2006, at 7:53 AM, semm...@gmail.com wrote:

> Sorry about the long lines, but I like putting as much code in one
> line as possible.

Repeat after me:

"Horizontal scrolling is bad... Horizontal scrolling is bad...
Horizontal scrolling is bad..."

:D

Seriously, thanks you for the submission.

James Edward Gray II

James Edward Gray II

unread,
Feb 26, 2006, 11:38:16 AM2/26/06
to
On Feb 26, 2006, at 9:18 AM, Dave Burt wrote:

> # Calibrate instrumentation
> begin
> say "Go outside."
> end until agree("Are you outside now? ")

This step could prove very difficult for some programmers. ;)

James Edward Gray II

Ross Bamford

unread,
Feb 26, 2006, 12:11:33 PM2/26/06
to
On Mon, 2006-02-27 at 00:18 +0900, Dave Burt wrote:

> So, thinking of you all, I wrote the code I now humbly present.

Very cool :D

Rudolfs Osins

unread,
Feb 26, 2006, 12:16:38 PM2/26/06
to
Hello everyone !

Here is my solution, actually it's my first ruby program, I just
started learning... Suggestions on how to improve my code are
welcome :)

This is how it looks when executed, I couldn't find where to turn
these warnings off :/

------------------------------------------------------------------

root@black /# ./weather.rb madrid
ignored element: {http://schemas.xmlsoap.org/wsdl/http/}binding
ignored element: {http://schemas.xmlsoap.org/wsdl/http/}operation
ignored element: {http://schemas.xmlsoap.org/wsdl/http/}urlEncoded
ignored element: {http://schemas.xmlsoap.org/wsdl/mime/}mimeXml
ignored element: {http://schemas.xmlsoap.org/wsdl/mime/}content
ignored element: {http://schemas.xmlsoap.org/wsdl/http/}address
The temperature in Madrid / Cuatro Vientos is 3 C

------------------------------------------------------------------

#!/usr/local/bin/ruby
require 'soap/wsdlDriver'
require 'rexml/document'

URL = 'http://www.webservicex.net/globalweather.asmx?WSDL'

# process the comandline arguments
if ARGV[0] == nil
abort("Usage: weather.rb city")
else
city = ARGV.join(' ')
end

soap = SOAP::WSDLDriverFactory.new(URL).create_rpc_driver
begin
weather = soap.GetWeather(:CityName => city, :CountryName => "")

# strip the first line with <? ?> stuff, else REXML wont parse
xml = weather.getWeatherResult.gsub(/(<\?.*?>\n)/, '')
data = REXML::Document.new(xml)

# celsius degrees are in parentheses
data.elements["//Temperature"].text[/\((.*)\)/]; temp = $1
data.elements["//Location"].text[/^(.*),/]; loc = $1

# show the gathered data
puts "The temperature in " + loc + " is " + temp
rescue
puts "Could not find data for your supplied city: " + city
end

------------------------------------------------------------------

Best regards,
Rudolfs


Aditya Mahajan

unread,
Feb 26, 2006, 12:27:46 PM2/26/06
to
<--- On Feb 24, Ruby Quiz wrote --->

> by Caleb Tennis
>
> Write a Ruby program such that given a certain argument to the program it
> will return the current temperature of that location. People living in
> the United States may be interested in temperature by ZIP code:
>
> $ ruby current_temp.rb 47201
> The temperature in Columbus, Indiana is 32 degrees F.
>
> Other locales may want to use their own mailing codes, or city names:
>
> $ ruby current_temp.rb madrid
> The temperature in Madrid, Spain is 12 degrees C.
>
> Which arguments you support is up to you.

This quiz finally got me going to read Ruby's Net API. It turned out
to be very pleasing. Here is my solution.

I use the wunderground website to get the weather. The program
supports all kinds of search arguments that are supported by
wundergroud. I pass on the input arguments to wu website. If a city is
found, the search results contain a link to rss feed. Instead of
parsing the html document, I get this rss feed and parse it. This
method fails sometimes, for small cities outside US.

Here is the code

# Examples:
# $ current_temp.rb 48105
# > The temperature in Ann Arbor, MI is 34 degrees F / 1 degrees C
#
# $ current_temp.rb Ann Arbor, MI
# > The temperature in Ann Arbor, MI is 34 degrees F / 1 degrees C
#
# $ current_temp DTW
# > The temperature in Detroit Metro Wayne County, MI is 36 degrees F / 2 degrees C
#
# $ current_temp New Delhi, India
# > The temperature in New Delhi, India is 77 degrees F / 25 degrees C
#
# $ current_temp DEL
# > The temperature in New Delhi, India is 77 degrees F / 25 degrees C
#

#--------------------------------------%<--------------------------------------
require 'net/http'
require 'uri'
require 'rexml/document'

if ARGV.length == 0
puts "Usage: ruby current_temp.rb [city, state | zipcode | city,
country | airport code]"
exit
end
urlbase =
"http://www.wunderground.com/cgi-bin/findweather/getForecast?query="
zipcode = ARGV.join('%20')

# Search for the zipcode on wunderground website
response = Net::HTTP.get_response URI.parse(urlbase << zipcode)

# Parse the result for the link to a rss feed
rss_feed = String.new
# Get the line with rss feed
response.body.each do |line|
if line.include?("application/rss+xml") then
stop_pos = line.rindex('"') - 1
start_pos = line.rindex('"',stop_pos) + 1
rss_feed = line.slice(start_pos..stop_pos)
break
end
end
# Get the feed and parse it for city and weather information
# The response is different for US cities and places outside US.
# Use appropritate regular expression to parse both simultaneously
if rss_feed == "" then
puts ARGV.join(' ') << ": No such city"
else
feed = Net::HTTP.get_response(URI.parse(rss_feed))
document = REXML::Document.new feed.body
title = document.elements.to_a("//title")[0].text
channel =
document.elements.to_a("//channel/item/description")[0].text
city = title.gsub(/\s*(Weather from)?\s*Weather
Underground\s*(-)?\s*/,"")
temp = channel.gsub(/(^Temperature:|\|.*$|\W)/,"")
temp = temp.gsub("F", " degrees F / ").gsub("C", " degrees C")
# For exact format as asked in the quiz, uncomment the following
# temp = temp.gsub("F.*$", "F")
puts "The temperature in #{city} is #{temp}"
end
#--------------------------------------%<--------------------------------------


Thanks for the nice quiz.

Aditya

--
Aditya Mahajan, EECS Systems, University of Michigan
http://www.eecs.umich.edu/~adityam || Ph: 7342624008


Ryan Leavengood

unread,
Feb 26, 2006, 12:33:41 PM2/26/06
to
Here is mine. It only provides temperatures for US zip codes. I've
been doing some HTML scraping like this lately for some utilities of
my own, so this was pretty easy (i.e. the techniques were fresh in my
mind.) Though for my other utilities I've been using WWW:Mechanize and
in this case I decided to go a little lower level.

One problem with this or any other HTML scraping solution, is minor
changes to the HTML can totally break things.

Beware of wrapping, especially on the "parse_list":

require 'open-uri'

if $0 == __FILE__
if ARGV.length < 1
puts "Usage: #$0 <zip code>"
exit(1)
end
parse_list = [[/<B>Local Forecast for (.* \(\d{5}\))<\/B>/, 'Local
temperature for #$1: '],
[/<B CLASS=obsTempTextA>([^&]*)&deg;(.)<\/B>/, '#$1 degrees #$2 '],
[/<B CLASS=obsTextA>Feels Like<BR> ([^&]*)&deg;(.)<\/B>/, '[It
feels like #$1 degrees #$2]']
]
# Blessed be the internet, the great provider of information
open('http://beta.weather.com/weather/local/'+ARGV[0]) do |io|
html = io.read
parse_list.each do |p|
# We don't need no steenkin' HTML parser
if html =~ p[0]
print eval(%Q{"#{p[1]}"})
end
end
puts
end
end

Ryan


Christian Neukirchen

unread,
Feb 26, 2006, 12:38:16 PM2/26/06
to
Rudolfs Osins <dev.r...@gmail.com> writes:

> Here is my solution, actually it's my first ruby program, I just
> started learning... Suggestions on how to improve my code are
> welcome :)
>
> This is how it looks when executed, I couldn't find where to turn
> these warnings off :/

$-w = nil should work.

Actually, I tried to use this webservice too, but I got so mad at it
and it's freaking dumb semantics that I dropped that for health
reasons. Looks like you did it, though. ;-)

--
Christian Neukirchen <chneuk...@gmail.com> http://chneukirchen.org


gordon

unread,
Feb 26, 2006, 12:48:19 PM2/26/06
to
> Write a Ruby program such that given a certain argument to the program it
> will return the current temperature of that location. People living in
> the United States may be interested in temperature by ZIP code:

This is my first submission to rubyquiz. I've been learning ruby for
about 6 months now, and it's my first foray into programming. I'd love
some feedback.

Thanks,

# current_temp.rb

require 'net/http'
require 'rexml/document'
require 'optparse'

class CurrentTemp
include REXML

def initialize(loc,u='f')
uri = "http://xml.weather.yahoo.com/forecastrss?p=#{loc}&u=#{u}"
@doc = Document.new Net::HTTP.get(URI.parse(uri))
raise "Invalid city, #{loc}" if /error/i =~
@doc.elements["//description"].to_s
end

def method_missing(methodname)
XPath.match(@doc,"//*[starts-with(name(), 'yweather')]").each
do|elem|
return elem.attributes[methodname.to_s] if
elem.attributes[methodname.to_s]
end
Object.method_missing(methodname)
end

def unit
self.temperature
end

def state
self.region
end

def to_s
"The current temperature in #{self.city}, #{self.state} is
#{self.temp} degrees #{self.unit}."
end

end

opts = OptionParser.new
opts.banner = "Usage:\n\n current_temp.rb city [-u unit]\n\n "
opts.banner += "city should be a zip code, or a Yahoo Weather location
id.\n\n"
opts.on("-uARG", "--unit ARG","Should be f or c", String) {|val| @u =
val }
opts.on("-h", "--help") {puts opts.to_s ; exit 0}

loc = opts.parse!
@u ||='f'

begin

puts CurrentTemp.new(loc,@u)

rescue
puts $!
puts opts.to_s
exit 1
end

hornd...@gmail.com

unread,
Feb 26, 2006, 12:51:58 PM2/26/06
to
My solution uses yahoo weather like another of the solutions here. It's
easy to use with US zip codes, but for international cities you have to
know the yahoo weather location id. I looked around for a big list of
these but couldn't find it. Anyways it would be nice to have some sort
of searching mechanism to turn a city name into a location id.

Also I used rexml to parse the rss feed. I tried to use the rss library,
but couldn't figure out how to pull out the 'yweather' tags.

Anyways, fun quiz. I enjoyed it a lot.

-----Jay Anderson


require 'rexml/document'
require 'open-uri'

#Returns a hash containing the location and temperature information
#Accepts US zip codes or Yahoo location id's
def yahoo_weather_query(loc_id, units)
h = {}
open("http://xml.weather.yahoo.com/forecastrss?p=#{loc_id}&u=#{units}")
do |http|
response = http.read
doc = REXML::Document.new(response)
root = doc.root
channel = root.elements['channel']
location = channel.elements['yweather:location']
h[:city] = location.attributes["city"]
h[:region] = location.attributes["region"]
h[:country] = location.attributes["country"]
h[:temp] =
channel.elements["item"].elements["yweather:condition"].attributes["temp"]
end
h
end

if ARGV.length < 1 then
puts "usage: #$0 <location> [f|c]"
exit
end
loc_id = ARGV[0]
units = (ARGV[1] || 'f').downcase
units = (units =~ /^(f|c)$/) ? units : 'f'

#An improvement would be to allow searches for the yahoo location id
#loc_id = yahoo_loc_search(loc_id)
weather_info = yahoo_weather_query(loc_id, units)
city = weather_info[:city]
region = weather_info[:region]
country = weather_info[:country]
temp = weather_info[:temp]

puts "The temperature in #{city}, #{region}, #{country} is #{temp}
degrees #{units.upcase}"

--
Posted via http://www.ruby-forum.com/.


Rudolfs Osins

unread,
Feb 26, 2006, 12:57:43 PM2/26/06
to
> $-w = nil should work.
>
> Actually, I tried to use this webservice too, but I got so mad at it
> and it's freaking dumb semantics that I dropped that for health
> reasons. Looks like you did it, though. ;-)
>
> --
> Christian Neukirchen <chneuk...@gmail.com> http://chneukirchen.org
>

Yay, that works, no warnings anymore :) Where can I read more about this magic
$-w variable ?

Regards,
Rudolfs


Ryan Leavengood

unread,
Feb 26, 2006, 1:37:14 PM2/26/06
to
On 2/26/06, gordon <gthie...@sbcglobal.net> wrote:
>
> This is my first submission to rubyquiz. I've been learning ruby for
> about 6 months now, and it's my first foray into programming. I'd love
> some feedback.

I think this is pretty darn slick Gordon, and you should continue in
programming.

My only suggestion would be that you check for an empty argument list
explicitly so that someone will a slow internet connection does not
have to wait for the request to go to Yahoo to get an error.

Ryan


Stephen Waits

unread,
Feb 26, 2006, 1:56:42 PM2/26/06
to

On Feb 26, 2006, at 9:53 AM, gordon wrote:

> I've been learning ruby for
> about 6 months now, and it's my first foray into programming. I'd
> love
> some feedback.

As Ryan said, excellent job! Keep at it.

--Steve

Jeff McNeil

unread,
Feb 26, 2006, 2:32:40 PM2/26/06
to
This is also my first Ruby Quiz submission.

I was going to neglect this one as well, but I got to thinking how
nicely lazy.rb might work with some web services. Once someone
actually came across a working SOAP service (xmethods lists quite a
few that are iffy),
I decided to give it a go.

Thanks =)

Jeff


#!/usr/bin/env ruby

require 'lazy'
require 'soap/wsdlDriver'
require 'rexml/document'
$-w = nil

$wsdl_loc = "http://www.webservicex.net/globalweather.asmx?WSDL"
class WeatherState
def initialize(city, country)
stub = SOAP::WSDLDriverFactory.new($wsdl_loc).create_rpc_driver

@keep_me = promise do
conditions = stub.getWeather(:CityName
=>city, :CountryName=>country)
data = REXML::Document.new(conditions.getWeatherResult.gsub(/<
\?.*?>\n/, ''))
{ :temp => data.elements["//Temperature"].text, loc =>
data.elements["//Location"].text }
end
end

def temp
demand(@keep_me)[:temp]
end

def loc
demand(@keep_me)[:loc]
end
end

if ARGV.length != 2
abort("Usage: weather.rb city country")
end

# Create Weather Object
weatherProxy = WeatherState.new(ARGV[0], ARGV[1])
puts "Location: " + weatherProxy.loc
puts "Current Temp: " + weatherProxy.temp.strip


Adam Shelly

unread,
Feb 26, 2006, 3:19:44 PM2/26/06
to
On 2/24/06, Ruby Quiz <ja...@grayproductions.net> wrote:
> Write a Ruby program such that given a certain argument to the program it
> will return the current temperature of that location. People living in
> the United States may be interested in temperature by ZIP code:

I used Yahoo Weather's RSS feed. net/http and simple-rss did most of
the work for me.
But Yahoo keeps some interesting data in the attributes of some custom
tags, like <yweather:condition temp="99"... \>. SimpleRSS wasn't
returning the attribute values (I'm not even sure if Yahoo's method is
compliant RSS). So I extended SimpleRSS to give me the values I want.
Then I added an basic RSSFeeder class which puts Net fetch and RSS
parse together, and adds caching, so that you can run it continuously
without hammering the server. The script takes a zipcode and an
optional -f to get today's forecast, too

-Adam

#------ weatherman.rb -------------
require 'net/http'
require 'simple-rss'

class Object
def metaclass; class << self; self; end; end
end #thanks, _why

#Extends Simple RSS to add tag attributes as methods to the tag object
# given <sometag var="2">hello</sometag>,
# allows item.sometag ==> hello
# and item.sometag.var ==> 2
class SimpleRSSwAttributes < SimpleRSS
def clean_content(tag, attrs, content)
s= super
while n= (attrs =~ /((\w*)="([^"]*)" )/mi)
attr_name = clean_tag($2)
s.metaclass.send(:attr_reader, attr_name)
s.instance_variable_set("@#{attr_name}",unescape($3))
attrs.slice!(n,$1.length)
end
s
end
def method_missing meth
nil
end
end

#Simple RSS feed reader.
# takes url, array of custom tags, and optional filename for caching results
# provides #each_item and #item(title) methods

class RSSFeeder
def initialize feed_url, extra_tags=[], cache=nil
raise 'Invalid URL' unless feed_url =~ /(.*\w*\.\w*\.\w*)(\/.*)/
#separate host, rest
@url,@feed = $1, $2
@cache = cache
extra_tags.each{|tag| SimpleRSSwAttributes.feed_tags << tag}
end

#tyields [item,channel] for item with title matching name
def item name, &block
fetch
i=@data.items.find{|item| item.title =~ name} if @data
yield [i,@data.channel] if i
end
def each_item &block
fetch
@data.items.each{|item| yield item}
end

private
def time_to_fetch?
@timestamp.nil? || (@timestamp < Time.now)
end

def fetch
#read the cache if we don't have data
if !@data && @cache
File.open(@cache, "r") {|f|
@timestamp = Time.parse(f.gets)
@data = SimpleRSSwAttributes.parse(f)
} if File.exists?(@cache)
end
#only fetch data from net if current data is expired
time_to_fetch? ? net_fetch : @data
end

def net_fetch
text = Net::HTTP.start(@url).get(@feed).body
@data = SimpleRSSwAttributes.parse(text)
#try to create a reasonable expiration date. Defaults to 10 mins in future
date = @data.lastBuildDate || @data.pubDate ||
@data.expirationDate || Time.now
@timestamp = date + (@data.ttl ? @data.ttl.to_i*60 : 600)
@timestamp = Time.now + 600 if @timestamp < Time.now

File.open(@cache, "w+"){|f|
f.puts @timestamp;
f.write text
} if @cache
end
end


if __FILE__==$0
exit(-1+puts("Usage #{$0} zipcode [-f]\nGives current temperature
for zipcode, "+
"-f to get forecast too").to_i) if ARGV.size < 1
zipcode = ARGV[0]

yahoo_tags = %w(yweather:condition yweather:location yweather:forecast)
w = RSSFeeder.new("xml.weather.yahoo.com/forecastrss?p=#{zipcode}",
yahoo_tags, "yahoo#{zipcode}.xml")
w.item(/Conditions/) { |item,chan|
puts "The #{item.title} are:\n\t#{chan.yweather_condition.temp}F and "+
"#{chan.yweather_condition.text}"
}
w.item(/Conditions/) { |item,chan|
puts "\nThe forecast for #{chan.yweather_location.city}, "+
"#{chan.yweather_location.region} for #{chan.yweather_forecast.day}, "+
"#{chan.yweather_forecast.date} is:\n"+
"\t#{chan.yweather_forecast.text} with a high of
#{chan.yweather_forecast.high} "+
"and a low of #{chan.yweather_forecast.low}"
} if ARGV[1]=~/f/i
#catch errors
w.item(/not found/) { |item,chan| puts item.description }

#Alternate feed
#w2 = RSSFeeder.new("rss.weather.com/weather/rss/local/#{zipcode}?cm_ven=LWO&cm_cat=rss&par=LWO_rss")
#w2.item(/Current Weather/){|item,rss|
# puts item.title,item.description.gsub(/&deg;/,248.chr)}
#w2.item(/10-Day Forecast/){|item,rss|
# puts item.title,item.description.gsub(/&deg;/,248.chr)} if ARGV[1]=~/f/i

end


Hal Fulton

unread,
Feb 26, 2006, 6:16:36 PM2/26/06
to
Dave Burt wrote:
> Ruby Quiz wrote:
>
>>Write a Ruby program such that given a certain argument to the program it
>>will return the current temperature of that location.
>
>
> I still haven't done last week's metakoans, but I thought to myself, "Dave,"
> (because I always address myself by name in my thoughts). "Dave," I thought,
> "a universal Ruby-based indicator of the current temperature that works in
> any location on any Ruby platform would be a great boon not only to you, but
> to the entire Ruby community. In fact, why stop at the temperature? Ruby has
> the power to turn almost any device into a fully functional weather station,
> measuring rain, wind and snow. The world will be amazed."

Amazing, Dave. A boon to hackers everywhere.

But what is this "Outside" you speak of? I don't think I have
that installed...


Hal

George Ogata

unread,
Feb 26, 2006, 5:59:21 PM2/26/06
to
Hal Fulton <hal...@hypermetrics.com> writes:

> But what is this "Outside" you speak of? I don't think I have
> that installed...

You need to "require 'pants'" first.

Christian Neukirchen

unread,
Feb 27, 2006, 8:17:58 AM2/27/06
to
George Ogata <g_o...@optushome.com.au> writes:

Alternatively, require 'rubypants' ;-)

semm...@gmail.com

unread,
Feb 27, 2006, 11:36:05 AM2/27/06
to
# Author: Shane Emmons
#
# Allows retrieval of current temperature information. Pretty simple
# and straight forward. Uses match to extract data from the xml
# document that is returned. Adjusted for easier horizontal reading.
;-)

#
# usage: ruby current_temp.rb [zipcode|other]
#
# zipcode: US zipcode
# other: country code information.
# example: SPXX0050 for Madrid, Spain

require 'net/http'

ARGV[ 0 ] = '48601'

begin


info = Net::HTTP.get(
"xml.weather.yahoo.com", "/forecastrss?p=".concat( ARGV[ 0 ] )
)

location = info.match( /Yahoo! Weather for (.*)</ )[ 1 ]
temperature = info.match( /<yweather:condition.*temp="(\d+)"/ )[ 1
]
measured_in = info.match( /<yweather:units temperature="(.)"/ )[ 1
]

print "The temperature in ",

location, " is ", temperature, " degrees ", measured_in, ".\n"
rescue
print "Information for #{ ARGV[ 0 ] } was not found or
unavailable.\n"
end

semm...@gmail.com

unread,
Feb 27, 2006, 11:37:42 AM2/27/06
to
** Sorry had my test code still in there for my location

# Author: Shane Emmons
#
# Allows retrieval of current temperature information. Pretty simple
# and straight forward. Uses match to extract data from the xml
# document that is returned. Adjusted for easier horizontal reading.
;-)
#
# usage: ruby current_temp.rb [zipcode|other]
#
# zipcode: US zipcode
# other: country code information.
# example: SPXX0050 for Madrid, Spain

require 'net/http'

begin

Harley Pebley

unread,
Feb 27, 2006, 11:46:56 AM2/27/06
to

>Write a Ruby program such that given a certain argument to the program it
>will return the current temperature of that location. People living in
>the United States may be interested in temperature by ZIP code:
>
> $ ruby current_temp.rb 47201

I've been lurking for a bit over 2 months as I've been reading
here and there about Ruby.

Anyway my first quiz submission (which is also my second Ruby script):

require 'net/http'
require 'rexml/document'

zip = ARGV[0].to_s

if zip.length > 0 then
h = Net::HTTP.new('rss.weather.com', 80)
resp, data = h.get('/weather/rss/local/'+zip, nil)

doc = REXML::Document.new data
doc.elements.each('rss/channel/item/title') { |element|
if element.text[0,7] == 'Current' then
puts element.text
desc = element.get_elements('../description').to_s.strip
puts desc.slice(22,desc.length-57).sub('&deg;', 'degrees')
end
}
else
puts 'Need a ZIP code as a command line parameter.'
end

James Edward Gray II

unread,
Feb 27, 2006, 12:01:24 PM2/27/06
to
On Feb 27, 2006, at 10:46 AM, Harley Pebley wrote:

> I've been lurking for a bit over 2 months as I've been reading
> here and there about Ruby.
>
> Anyway my first quiz submission (which is also my second Ruby script):

Let me be one of the first to welcome you then! Script looks great
for the second on, by the way.

James Edward Gray II


Harley Pebley

unread,
Feb 27, 2006, 12:30:07 PM2/27/06
to

>>I've been lurking for a bit over 2 months as I've been reading
>>here and there about Ruby.
>>
>>Anyway my first quiz submission (which is also my second Ruby script):
>
>Let me be one of the first to welcome you then! Script looks great
>for the second on, by the way.

Thanks! It's lacking a bunch of error checking, input validation and the
like, but it suffices for now. And I can look at some of the other entries
to get some of those details.

The quiz is a great idea.

Regards,
Harley Pebley

David Tran

unread,
Feb 27, 2006, 4:23:57 PM2/27/06
to
require 'uri'
require 'open-uri'
require 'rexml/document'

class Weather
attr_reader :location, :temperature, :unit

def initialize(zip_or_city, unit='f')
raise "Error: Unit must be 'C' or 'F'." unless unit =~ /^[cf]$/i
id = get_id(zip_or_city)
url = "http://xml.weather.yahoo.com/forecastrss/#{id}_#{unit.downcase}.xml"
xml = open(url) { |f| f.read }
doc = REXML::Document.new(xml)
@temperature =
doc.elements['/rss/channel/item/yweather:condition/@temp'].to_s.to_i
@unit = unit.upcase
end

private
def get_id(location)
location = URI.escape(location)
url = "http://xoap.weather.com/search/search?where=#{location}"
xml = open(url) { |f| f.read }
doc = REXML::Document.new(xml)
locations = doc.elements.to_a("/search/loc")
raise "Cannot find the location." if locations.size <= 0
# raise "Please more specific:\n#{locations.map {|e|
e.text}*"\n"}" if locations.size > 1
@location = locations[0].text.sub(/\s*\(\d+\)\s*$/, '')
locations[0].attributes['id']
end
end

if __FILE__ == $0
if ARGV.size <= 0 || (ARGV[1] && ARGV[1] !~ /^[cf]$/i)
puts "Usage: #$0 city_or_zip_code [c|f]"
exit(1)
end

begin
w = Weather.new(ARGV[0], ARGV[1] || 'f')
puts "The temperature in #{w.location} is #{w.temperature} degress
#{w.unit}."
rescue
puts "Information for #{ARGV[0]} was not found or unavailable."
end
end


__END__
www.doublegifts.com


gordon

unread,
Feb 27, 2006, 4:54:47 PM2/27/06
to
Jay,

I liked your idea of searching for the location id, so I added it to my
original submission. If you use the -s option, it will give you a menu
of choices. If it only finds one choice it will just return the
temperature.

Examples:

c:\>ruby c:\current_temp.rb madrid -s
1. Madrid, Nebraska, United States
2. Madrid, New York, United States
3. Madrid, Iowa, United States
4. Madrid, Spain
5. General La Madrid, Argentina
6. New Madrid, Missouri, United States
Please choose your location 4

The current temperature in Madrid, is 37 degrees F.

c:\>ruby current_temp.rb "madrid, spain" -s

The current temperature in Madrid, is 37 degrees F.

# current_temp.rb

require 'net/http'
require 'rexml/document'
require 'optparse'

require "rubygems"
require "highline/import"
require 'cgi'

class LocationSearch
attr_reader :loc

def initialize(string)
city = CGI.escape(string)

h = Net::HTTP.new('weather.yahoo.com', 80)
resp, data = h.get("/search/weather2?p=#{city}", nil)

case resp
when Net::HTTPSuccess then @loc = location_menu(
parse_locations(data) )
when Net::HTTPRedirection then @loc =
get_location(resp['location'])
end
end

def location_menu(hash)
choose do |menu|
menu.prompt = "Please choose your location "
hash.each do |key,val|
menu.choice val do return key end
end
end
end

def parse_locations(data)
a = {}
data.split("\n").each do |i|
a[get_location(i)]=strip_html(i) if /a href="\/forecast/ =~ i
end
a
end

def strip_html(str)
str = str.strip || ''
str.gsub(/<(\/|\s)*[^>]*>/,'')
end

def get_location(string)
string.split(/\/|\./)[2]
end

end


class CurrentTemp
include REXML

def unit
self.temperature
end

def state
self.region
end

end

begin

opts = OptionParser.new
opts.banner = "Usage:\n\n current_temp.rb city [-u unit]\n\n
"
opts.banner += "city should be a zip code, or a Yahoo Weather
location id.\n\n"
opts.on("-uARG", "--unit ARG","Should be f or c", String) {|val| @u
= val }

opts.on("-s", "--search","Search location") {@search = true}


opts.on("-h", "--help") {puts opts.to_s ; exit 0}

loc = opts.parse!.to_s
@u ||='f'

if @search
loc = LocationSearch.new(loc).loc
end

if loc.empty?
raise "Invalid city, \"#{loc}\""
else
puts
puts CurrentTemp.new(loc,@u)
end

hornd...@gmail.com

unread,
Feb 28, 2006, 1:03:25 AM2/28/06
to
Cool. I'd tried to do something like that, but couldn't quite get it to
work (because of the yahoo redirect). Looking at your code helped me
figure it out. I looked at net/http and then more at what open-uri
provides. Lo and behold it provides some meta-data that helps with the
redirect problem. Anyways thanks gordon! I added my now fixed function
below.

-----Jay Anderson

require 'rexml/document'
require 'open-uri'

LOC_MATCH = /\/forecast\/([^.]+)\.html/

#Searches Yahoo and returns an array of location ids
def yahoo_loc_search(loc)
return [loc] if loc =~ /\d/ #places usually don't have numbers in
their names
locs = []

open("http://weather.yahoo.com/search/weather2?p=#{URI.escape(loc)}")
do |http|
return [$1] if http.base_uri.to_s =~ LOC_MATCH
http.each {|line| locs << $1 if line =~ LOC_MATCH }
end
locs
end

#Returns a hash containing the location and temperature information
#Accepts US zip codes or Yahoo location id's

def yahoo_weather_query(loc_ids, units)
weather = []
loc_ids.each do |l|
h = {}

open("http://xml.weather.yahoo.com/forecastrss?p=#{l}&u=#{units}") do


|http|
response = http.read
doc = REXML::Document.new(response)

channel = doc.root.elements['channel']
title = channel.elements['title'].text
if title !~ /Error/ then


location = channel.elements['yweather:location']
h[:city] = location.attributes["city"]
h[:region] = location.attributes["region"]
h[:country] = location.attributes["country"]
h[:temp] =
channel.elements["item"].elements["yweather:condition"].attributes["temp"]

weather << h
end
end
end
weather
end

if ARGV.length < 1 then
puts "usage: #$0 <location> [f|c]"
exit
end
loc_id = ARGV[0]
units = (ARGV[1] || 'f').downcase
units = (units =~ /^(f|c)$/) ? units : 'f'

loc_ids = yahoo_loc_search(loc_id)
weather_info = yahoo_weather_query(loc_ids, units)

puts "No matches found" if weather_info.size == 0

weather_info.each do |w|
city = w[:city]
region = w[:region]
country = w[:country]
temp = w[:temp]

final_loc = "#{city}, #{region}#{', ' if region!="" and
country!=""}#{country}"
puts "The temperature in #{final_loc} is #{temp} degrees
#{units.upcase}"
end

Patrick Chanezon

unread,
Feb 28, 2006, 8:59:32 PM2/28/06
to
An answer just for the fun of it, showing that you write as obscure and
unmaintainable code in Ruby as in Perl. The challenge was to get all the
functionality in a one liner, in order to see how far you can stretch ruby
expressions. They seem to stretch pretty well:-)

It uses Google, so it supports only zip codes as input.

require 'net/http'
puts ((ARGV.length != 1) ? "Usage: #$0 <zip code>" : (["The temperature
in"] + (/Weather<\/b> for <b>(.*)<\/b>.*\D(\d+)&deg;F/.match(Net::HTTP.get(
URI.parse("http://www.google.com/search?hl=en&q=temperature+#{ARGV[0]}")))[1,2].collect!
{|x| " is " + x})).to_s.gsub!(/in is /, "in ") + " degree F")

/temp.rb 94117
The temperature in San Francisco, CA is 57 degree F
/temp.rb
Usage: ./temp.rb <zip code>


On 2/24/06, Ruby Quiz <ja...@grayproductions.net> wrote:
>

> The three rules of Ruby Quiz:
>
> 1. Please do not post any solutions or spoiler discussion for this quiz
> until
> 48 hours have passed from the time on this message.
>
> 2. Support Ruby Quiz by submitting ideas as often as you can:
>
> http://www.rubyquiz.com/
>
> 3. Enjoy!
>
> Suggestion: A [QUIZ] in the subject of emails about the problem helps
> everyone
> on Ruby Talk follow the discussion.
>
>
> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>
> by Caleb Tennis


>
> Write a Ruby program such that given a certain argument to the program it
> will return the current temperature of that location. People living in
> the United States may be interested in temperature by ZIP code:
>
> $ ruby current_temp.rb 47201

> The temperature in Columbus, Indiana is 32 degrees F.
>
> Other locales may want to use their own mailing codes, or city names:
>
> $ ruby current_temp.rb madrid
> The temperature in Madrid, Spain is 12 degrees C.
>
> Which arguments you support is up to you.
>
>


--
Patrick Chanezon, AdWords API evangelist
http://blog.chanezon.com/
http://www.google.com/apis/adwords/

David Tran

unread,
Mar 1, 2006, 5:04:56 PM3/1/06
to
My one-line solution.
Just join the below 4 lines become one single line; even no need space
when join lines.

require'open-uri';puts (ARGV.size !=1)?"Usage: #$0 <zip code>":
("The temperature in #$1 is #$2 degrees F."if
%r|Weather</b> for <b>(.*?)</b>.*?(\d+)&deg;F|=~
open("http://www.google.com/search?hl=en&q=temperature+#{ARGV[0]}"){|f|f.read})


semm...@gmail.com

unread,
Mar 1, 2006, 6:15:07 PM3/1/06
to
Semi-colons, that is so cheating. :-)

dave

unread,
Mar 2, 2006, 3:21:22 AM3/2/06
to

| Semi-colons, that is so cheating

require("open-uri") && puts((ARGV.size !=1) ? "Usage: #$0 <zip code>": ("The
temperature in #$1 is #$2 degrees F." if %r|Weather</b> for <b>(.*?)</b>.*?
(\d+)&deg;F|
=~open("http://www.google.com/search?hl=en&q=temperature+#{ARGV[0]}"){|f|
f.read}))


This is without semi-colons :)


--
>here are more things in heaven and earth,
horatio, than are dreamt of in your philosophy.


--
Email.it, the professional e-mail, gratis per te: http://www.email.it/f

Sponsor:
DVD vergini: acquista online a prezzi vantaggiosi!
Clicca qui: http://adv.email.it/cgi-bin/foclick.cgi?mid=4589&d=2-3


Ruby Quiz

unread,
Mar 2, 2006, 8:57:36 AM3/2/06
to
Obviously, this quiz is about fetching data from the Internet. People went
about this in two different ways. The most popular solution was to "scrape" the
answer from popular Internet weather sites. Here is one such solution from
David Tran:

require 'uri'
require 'open-uri'
require 'rexml/document'

class Weather
attr_reader :location, :temperature, :unit

def initialize(zip_or_city, unit='f')
raise "Error: Unit must be 'C' or 'F'." unless unit =~ /^[cf]$/i
id = get_id(zip_or_city)

url = "http://xml.weather.yahoo.com/" +


"forecastrss/#{id}_#{unit.downcase}.xml"
xml = open(url) { |f| f.read }
doc = REXML::Document.new(xml)
@temperature = doc.elements['/rss/channel/item/yweather:condition/@temp'].
to_s.to_i
@unit = unit.upcase
end

private
def get_id(location)
location = URI.escape(location)
url = "http://xoap.weather.com/search/search?where=#{location}"
xml = open(url) { |f| f.read }
doc = REXML::Document.new(xml)
locations = doc.elements.to_a("/search/loc")
raise "Cannot find the location." if locations.size <= 0

@location = locations[0].text.sub(/\s*\(\d+\)\s*$/, '')
locations[0].attributes['id']
end
end

if __FILE__ == $0
if ARGV.size <= 0 || (ARGV[1] && ARGV[1] !~ /^[cf]$/i)
puts "Usage: #$0 city_or_zip_code [c|f]"
exit(1)
end

begin
w = Weather.new(ARGV[0], ARGV[1] || 'f')

puts "The temperature in #{w.location} is " +


"#{w.temperature} degress #{w.unit}."
rescue
puts "Information for #{ARGV[0]} was not found or unavailable."
end
end

Start at the bottom if statement. You can see in here that arguments are
checked and a usage statement is printed, if needed. Next we can see that a
Weather object is constructed and the temperature information is pulled from its
methods. If anything goes wrong in this process, the rescue clause prints a
suitable error message.

Now we need to check out the Weather object.

In initialize(), the zip_or_city is somehow turned into an "id". Using that id,
a url is constructed for an RSS feed of weather information for the location.
Next the terrific open-uri library is used to slurp the feed, just as we would a
normal file. The xml is then dropped into Ruby's standard REXML library and an
XPath statement is used to extract the temperature from the feed.

The other method is the magic "id" conversion I mentioned earlier. It really
works pretty similar to the method we just examined. Again a url is
constructed, the page slurped, the xml handed to REXML, and XPath used to find a
matching location that was listed on the page. This time a Regexp is also
needed to clean up the location name.

Unfortunately, scraping has some down sides. First, this use of REXML and
Regexps to grab the information is pretty fragile. As soon as these pages
change in some way the author didn't expect, the application is broken and will
need maintenance.

The other big problem with scraping is legal issues. Many sites do not allow
access to their content like this (because it bypasses their ads). Google is a
famous example of a company that does not allow scraping content. Be sure you
check the usage policies of a site before you construct or use software like
this.

If you want to get around these issues, you can use a web service. Web services
represent a predefined communication protocol. You pass the information the
service expects in, and it will return a promised response. Here's a
temperature solution using a web service by Rudolfs Osins:

#!/usr/local/bin/ruby
require 'soap/wsdlDriver'
require 'rexml/document'

URL = 'http://www.webservicex.net/globalweather.asmx?WSDL'

# process the comandline arguments
if ARGV[0] == nil
abort("Usage: weather.rb city")
else
city = ARGV.join(' ')
end

soap = SOAP::WSDLDriverFactory.new(URL).create_rpc_driver
begin
weather = soap.GetWeather(:CityName => city, :CountryName => "")

# strip the first line with <? ?> stuff, else REXML wont parse
xml = weather.getWeatherResult.gsub(/(<\?.*?>\n)/, '')
data = REXML::Document.new(xml)

# celsius degrees are in parentheses
data.elements["//Temperature"].text[/\((.*)\)/]; temp = $1
data.elements["//Location"].text[/^(.*),/]; loc = $1

# show the gathered data
puts "The temperature in " + loc + " is " + temp
rescue
puts "Could not find data for your supplied city: " + city
end

Here again, we see that a URL is built, but this time the URL points to the
document describing the web service (a Web Service Description Language, or
WSDL, document). Another standard library, SOAP, is used to read and parse that
document. In doing so, it will build a custom object that has the methods
provided by the service, accepting the arguments they expect.

The rest of the solution will look pretty familiar, since this service provides
an xml answer. Again REXML is used, with XPath, to find the data we are
interested in and again Regexps are used to clean it up for display. In this
case we didn't get too far away from the fragile scraping technique it seems,
though web services typically see less change than web pages.

Not that the above solution doesn't work with zip codes, because the service
expects a city name. Supporting a zip code might be possible using another
service to lookup the city name, if one could be found.

My thanks to all the submitters for another great quiz. Pulling information
from the web is pretty common and these guys did a great job of showing off how
painless it can be.

Tomorrow's quiz is an easy, but unique, problem. If you have yet to complete a
quiz solution, this one is for you...


Uncle D

unread,
Mar 2, 2006, 9:14:25 AM3/2/06
to
Rudolfs Osins solution is very sound and using a web-service is a good
decision for the reasons James mentioned. Bravo.

David Tran

unread,
Mar 2, 2006, 9:39:04 AM3/2/06
to
Thanks James to point out legal issue.

My solution is just kind of Ruby exercice to show how to apply REXML
and open-uri.

Please consider my solution as an example of how to use REXML and open-uri
to answer the Quiz and do NOT use my solution, because it has legal issue.

I am declare here that I am not responsible for legal problem of usage
my solution.

Sorry for the trouble.


--
www.doublegifts.com


Ryan Leavengood

unread,
Mar 2, 2006, 2:39:44 PM3/2/06
to
It was good of James to mention it in the summary, but in reality I
think it is "fair use" to scrape a page, regardless of what the site
owner says. People use ad-filters all the time. Just because someone
has a page with ads does not mean people should be forced to view
them. Providing publicly accessible information on the internet
carries the risk that people will use it in ways you did not intend
(much like publishing a book.)

I think scraping only becomes a problem when you are doing it as part
of a commercial enterprise (which maybe is what James meant.) Much
like copyright infringement is really only an issue when profit is
involved for the copier.

But I'd love to see Google sue a single person for some little
scraping utility that they use. It seems ridiculous to me.

Ryan

Jeff Cohen

unread,
Mar 2, 2006, 3:36:03 PM3/2/06
to
Ryan Leavengood wrote:
> Much like copyright infringement is really only an issue when profit is
> involved for the copier.

Or loss of profit to the original publisher.

If I buy a book and make 100 copies and give them away for free, that's
still not legal because the publisher was entitled to sell those copies.

But overall I agree, if you're not making money on it and you're not
causing anyone else to lose revenue, then it's probably fair use.

Jeff

Christian Neukirchen

unread,
Mar 2, 2006, 5:19:23 PM3/2/06
to
"Uncle D" <uncle...@gmail.com> writes:

> Rudolfs Osins solution is very sound and using a web-service is a good
> decision for the reasons James mentioned. Bravo.

Yet is the web-service used total crap. You should not be forced
to hack out XML declarations in real life.

Christian Neukirchen

unread,
Mar 2, 2006, 5:20:45 PM3/2/06
to
Jeff Cohen <cohen...@gmail.com> writes:

> Ryan Leavengood wrote:
>> Much like copyright infringement is really only an issue when profit is
>> involved for the copier.
>
> Or loss of profit to the original publisher.
>
> If I buy a book and make 100 copies and give them away for free, that's
> still not legal because the publisher was entitled to sell those copies.
>
> But overall I agree, if you're not making money on it and you're not
> causing anyone else to lose revenue, then it's probably fair use.

Now, let's consider screen-scraping really is "quoting". With proper
attribution, that's fair use... IANAL.

> Jeff

Jeff Cohen

unread,
Mar 2, 2006, 5:38:59 PM3/2/06
to
Christian Neukirchen wrote:

> Jeff Cohen <cohen...@gmail.com> writes:
>
>> causing anyone else to lose revenue, then it's probably fair use.
> Now, let's consider screen-scraping really is "quoting". With proper
> attribution, that's fair use... IANAL.

Um, I don't know about that, actually. You have to make sure you're not
taking money away from the original publisher.

For example, I could easily write a site which lets you type a google
search into a text box. I could make the google http get call myself,
scrape the html, and then present the results *without* all of the ads
that usually appear in the right-hand margin.

Even if I say at the top, "Results provided by Google", do you think
it's ok for me to do that? I'm using Google's hard work for my benefit
*and* they would lose money because of it.

One reason I decided not to have ads on my hockey site
(www.hawksfans.com) is because I'm scraping RSS feeds, and even though
everything is clearly attributed with a link back to the original
articles, I just wanted to be on the safe side.

By the way, I really do think most of us doing html scrapes are doing it
fairly... my apologies if I took us too far off-topic. :-)

Jeff
www.softiesonrails.com

Christian Neukirchen

unread,
Mar 2, 2006, 6:00:38 PM3/2/06
to
Jeff Cohen <cohen...@gmail.com> writes:

> One reason I decided not to have ads on my hockey site
> (www.hawksfans.com) is because I'm scraping RSS feeds, and even though
> everything is clearly attributed with a link back to the original
> articles, I just wanted to be on the safe side.

Let's sue Technorati! And Google Feeds!

If you provide feeds, you want them scraped...

0 new messages