woefoo 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +9 -0
- data/Rakefile +15 -0
- data/lib/woefoo/config.rb +22 -0
- data/lib/woefoo/geoplanet_adjacency.rb +7 -0
- data/lib/woefoo/geoplanet_alias.rb +9 -0
- data/lib/woefoo/geoplanet_place.rb +61 -0
- data/lib/woefoo/response.rb +53 -0
- data/lib/woefoo/town_match_validators.rb +40 -0
- data/lib/woefoo/version.rb +3 -0
- data/lib/woefoo.rb +59 -0
- data/test/data/multiple_results_1.xml +101 -0
- data/test/data/poor_input_quality.xml +101 -0
- data/test/data/single_result.xml +39 -0
- data/test/response_test.rb +38 -0
- data/test/test_helper.rb +18 -0
- data/test/woefoo_test.rb +80 -0
- data/woefoo.gemspec +27 -0
- metadata +169 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'bundler'
|
4
|
+
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
desc 'Default: run unit tests.'
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Test the woefoo gem'
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << 'test'
|
13
|
+
t.test_files = FileList['test/**/*_test.rb']
|
14
|
+
t.verbose = true
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Woefoo
|
2
|
+
module Config
|
3
|
+
# appid is the user's Yahoo app id (http://developer.yahoo.com/geo/placefinder/)
|
4
|
+
attr :appid, true
|
5
|
+
# the number of results to return for each request
|
6
|
+
attr :placefinder_result_count, true
|
7
|
+
# the minimum input quality for a canonical town match
|
8
|
+
attr :min_input_quality, true
|
9
|
+
|
10
|
+
def placefinder_result_count
|
11
|
+
(@placefinder_result_count && @placefinder_result_count > 0) ? @placefinder_result_count : 3
|
12
|
+
end
|
13
|
+
|
14
|
+
def min_input_quality
|
15
|
+
(@min_input_quality && @min_input_quality > 0) ? @min_input_quality : 40
|
16
|
+
end
|
17
|
+
|
18
|
+
def min_result_quality
|
19
|
+
(@min_result_quality && @min_result_quality > 0) ? @min_result_quality : 80
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module Woefoo
|
2
|
+
class GeoplanetAdjacency < ActiveRecord::Base
|
3
|
+
set_table_name "geoplanet_adjacencies"
|
4
|
+
belongs_to :place, :class_name => 'GeoplanetPlace', :foreign_key => 'woeid', :primary_key => 'woeid'
|
5
|
+
belongs_to :adjacent_place, :class_name => 'GeoplanetPlace', :foreign_key => 'neighbour_woeid', :primary_key => 'woeid'
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Woefoo
|
2
|
+
class GeoplanetPlace < ActiveRecord::Base
|
3
|
+
set_table_name "geoplanet_places"
|
4
|
+
set_primary_key 'woeid'
|
5
|
+
|
6
|
+
has_many :aliases, :class_name => 'GeoplanetAlias', :foreign_key => 'woeid'
|
7
|
+
has_many :adjacencies, :class_name => 'GeoplanetAdjacency', :foreign_key => 'woeid'
|
8
|
+
has_many :adjacent_places, :through => :adjacencies
|
9
|
+
|
10
|
+
belongs_to :parent,
|
11
|
+
:class_name => "GeoplanetPlace",
|
12
|
+
:foreign_key => 'parent_woeid',
|
13
|
+
:primary_key => 'woeid'
|
14
|
+
|
15
|
+
has_ancestry
|
16
|
+
|
17
|
+
def town
|
18
|
+
self.ancestors.find(:first, :conditions => {:place_type => "Town"})
|
19
|
+
end
|
20
|
+
|
21
|
+
def region
|
22
|
+
self.ancestors.find(:first, :conditions => {:place_type => "State"})
|
23
|
+
end
|
24
|
+
|
25
|
+
def country
|
26
|
+
self.ancestors.find(:first, :conditions => {:place_type => "Country"})
|
27
|
+
end
|
28
|
+
|
29
|
+
def continent
|
30
|
+
self.ancestors.find(:first, :conditions => {:place_type => "Continent"})
|
31
|
+
end
|
32
|
+
|
33
|
+
def full_name
|
34
|
+
if !@full_name
|
35
|
+
@full_name = self.name
|
36
|
+
@full_name = "#{@full_name}, #{self.region.name}" if self.region
|
37
|
+
@full_name = "#{@full_name}, #{self.country.name}" if self.country
|
38
|
+
end
|
39
|
+
|
40
|
+
return @full_name
|
41
|
+
end
|
42
|
+
|
43
|
+
def full_name_parts
|
44
|
+
ret = {:name => self.name}
|
45
|
+
ret[:region_name] = self.region.name if self.region
|
46
|
+
ret[:country_name] = self.country.name if self.country
|
47
|
+
ret[:continent_name] = self.continent.name if self.continent
|
48
|
+
return ret
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.build_ancestry_from_parent_ids! parent_id = nil, ancestry = nil
|
52
|
+
parent_id = parent_id || 0
|
53
|
+
self.base_class.all(:conditions => {:parent_woeid => parent_id}).each do |node|
|
54
|
+
node.without_ancestry_callbacks do
|
55
|
+
node.update_attribute ancestry_column, ancestry
|
56
|
+
end
|
57
|
+
build_ancestry_from_parent_ids! node.id, if ancestry.nil? then "#{node.id}" else "#{ancestry}/#{node.id}" end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Woefoo
|
4
|
+
class Response
|
5
|
+
attr_reader :query_options
|
6
|
+
attr_reader :typhoeus_response
|
7
|
+
attr_reader :xml_doc
|
8
|
+
attr_reader :code
|
9
|
+
attr_reader :found
|
10
|
+
attr_reader :results
|
11
|
+
# http://developer.yahoo.com/geo/placefinder/guide/responses.html#address-quality
|
12
|
+
attr_reader :input_quality
|
13
|
+
|
14
|
+
def initialize(opts, response)
|
15
|
+
@query_options = opts
|
16
|
+
@typhoeus_response = response
|
17
|
+
@xml_doc = Nokogiri::XML(response.body)
|
18
|
+
@code = response.code
|
19
|
+
parse_results!
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# ONLY CALL THIS ONCE!
|
26
|
+
def parse_results!
|
27
|
+
# woetype => http://developer.yahoo.com/geo/geoplanet/guide/concepts.html#placetypes
|
28
|
+
@results = []
|
29
|
+
if @xml_doc
|
30
|
+
@xml_doc.xpath('ResultSet/Result').each do |r|
|
31
|
+
@results << {
|
32
|
+
:quality => r.at("quality").text.to_i,
|
33
|
+
:line1 => r.at("line1").text,
|
34
|
+
:line2 => r.at("line2").text,
|
35
|
+
:city => r.at("city").text,
|
36
|
+
:state => r.at("state").text,
|
37
|
+
:statecode => r.at("statecode").text,
|
38
|
+
:postal => r.at("postal").text,
|
39
|
+
:countrycode => r.at("countrycode").text,
|
40
|
+
:latitude => r.at("latitude").text.to_f,
|
41
|
+
:longitude => r.at("longitude").text.to_f,
|
42
|
+
:woeid => r.at("woeid").text.to_i,
|
43
|
+
:woetype => r.at("woetype").text.to_i
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
@found = @xml_doc.xpath("ResultSet/Found").text.to_i
|
48
|
+
@input_quality = @xml_doc.xpath("ResultSet/Quality").text.to_i
|
49
|
+
end
|
50
|
+
@results.sort! { |x,y| y[:quality] <=> x[:quality] }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# A town match validator is a hash of two procs, the first proc is
|
2
|
+
# a procedure for conditions in which this validator should be used,
|
3
|
+
# and the second proc is the actual validator to run on the place results
|
4
|
+
require 'geokit'
|
5
|
+
|
6
|
+
module Woefoo
|
7
|
+
module TownMatchValidators
|
8
|
+
extend self
|
9
|
+
|
10
|
+
def default_validators
|
11
|
+
[{
|
12
|
+
:conditions => Proc.new {|response|
|
13
|
+
true # If all else fails, use this catch all validator
|
14
|
+
},
|
15
|
+
:validator => Proc.new {|response|
|
16
|
+
best_match=nil
|
17
|
+
if response.input_quality >= min_input_quality
|
18
|
+
response.results.each do |r|
|
19
|
+
if r[:quality] >= response.input_quality && r[:quality] >= min_result_quality
|
20
|
+
place=Woefoo::GeoplanetPlace.find_by_woeid(r[:woeid])
|
21
|
+
if place.place_type=="Town"
|
22
|
+
# If the query included a country code, make sure the place we're picking is in the same country!
|
23
|
+
next if (response.query_options.include?(:country_code) &&
|
24
|
+
response.query_options[:country_code].downcase != place.country_code.downcase)
|
25
|
+
best_match=r[:woeid]
|
26
|
+
elsif ["Zip", "Suburb"].include?(place.place_type) && place.town
|
27
|
+
next if (response.query_options.include?(:country_code) &&
|
28
|
+
response.query_options[:country_code].downcase != place.country_code.downcase)
|
29
|
+
best_match=place.town.woeid
|
30
|
+
end
|
31
|
+
end
|
32
|
+
break if best_match
|
33
|
+
end
|
34
|
+
end
|
35
|
+
best_match
|
36
|
+
}
|
37
|
+
}]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/woefoo.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'typhoeus'
|
3
|
+
require 'active_record'
|
4
|
+
require 'ancestry'
|
5
|
+
require 'woefoo/config'
|
6
|
+
require 'woefoo/town_match_validators'
|
7
|
+
require 'woefoo/response'
|
8
|
+
require 'woefoo/geoplanet_place'
|
9
|
+
require 'woefoo/geoplanet_alias'
|
10
|
+
require 'woefoo/geoplanet_adjacency'
|
11
|
+
|
12
|
+
module Woefoo
|
13
|
+
include Woefoo::Config
|
14
|
+
include Woefoo::TownMatchValidators
|
15
|
+
extend self
|
16
|
+
# Fires a placefinder request to yahoo, returns with suggested places that match the query
|
17
|
+
def lookup(opts={})
|
18
|
+
validate!(opts)
|
19
|
+
perform_lookup(opts)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Takes a Woefoo Response, and figures out a Town-level or equivalent
|
23
|
+
# woe id that identifies where the queried address is
|
24
|
+
def canonical_town_match(response)
|
25
|
+
default_validators.each do |v|
|
26
|
+
if v[:conditions].call(response)
|
27
|
+
return v[:validator].call(response)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def validate!(opts)
|
35
|
+
raise "Woefoo.appid is not defined" if !@appid
|
36
|
+
if opts.length == 0 || opts.include?(:query) && (opts[:query] == "")
|
37
|
+
raise "Invalid query"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_query(opts={})
|
42
|
+
s = ""
|
43
|
+
if opts.include?(:query)
|
44
|
+
s = opts[:query]
|
45
|
+
else
|
46
|
+
[:line1, :city, :region, :zip, :country_code].each do |l|
|
47
|
+
s << "#{opts[l]} " if opts.include?(l)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
::CGI.escape(s)
|
51
|
+
end
|
52
|
+
|
53
|
+
def perform_lookup(opts)
|
54
|
+
query = build_query(opts)
|
55
|
+
url = "http://where.yahooapis.com/geocode?appid=#{@appid}&count=#{placefinder_result_count}&q=#{query}"
|
56
|
+
tr = Typhoeus::Request.get(url)
|
57
|
+
Woefoo::Response.new(opts, tr) if tr
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<ResultSet version="1.0">
|
3
|
+
<Error>0</Error>
|
4
|
+
<ErrorMessage>No error</ErrorMessage>
|
5
|
+
<Locale>us_US</Locale>
|
6
|
+
<Quality>87</Quality>
|
7
|
+
<Found>3</Found>
|
8
|
+
<Result>
|
9
|
+
<quality>40</quality>
|
10
|
+
<latitude>37.416275</latitude>
|
11
|
+
<longitude>-122.025092</longitude>
|
12
|
+
<offsetlat>37.416397</offsetlat>
|
13
|
+
<offsetlon>-122.025055</offsetlon>
|
14
|
+
<radius>500</radius>
|
15
|
+
<name></name>
|
16
|
+
<line1>701 1st Ave</line1>
|
17
|
+
<line2>Sunnyvale, CA 94089-1019</line2>
|
18
|
+
<line3></line3>
|
19
|
+
<line4>United States</line4>
|
20
|
+
<house>701</house>
|
21
|
+
<street>1st Ave</street>
|
22
|
+
<xstreet></xstreet>
|
23
|
+
<unittype></unittype>
|
24
|
+
<unit></unit>
|
25
|
+
<postal>94089-1019</postal>
|
26
|
+
<neighborhood></neighborhood>
|
27
|
+
<city>Sunnyvale</city>
|
28
|
+
<county>Santa Clara County</county>
|
29
|
+
<state>California</state>
|
30
|
+
<country>United States</country>
|
31
|
+
<countrycode>US</countrycode>
|
32
|
+
<statecode>CA</statecode>
|
33
|
+
<countycode></countycode>
|
34
|
+
<uzip>94089</uzip>
|
35
|
+
<hash>DDAD1896CC0CDC41</hash>
|
36
|
+
<woeid>12797150</woeid>
|
37
|
+
<woetype>11</woetype>
|
38
|
+
</Result>
|
39
|
+
<Result>
|
40
|
+
<quality>87</quality>
|
41
|
+
<latitude>37.416275</latitude>
|
42
|
+
<longitude>-122.025092</longitude>
|
43
|
+
<offsetlat>37.416397</offsetlat>
|
44
|
+
<offsetlon>-122.025055</offsetlon>
|
45
|
+
<radius>500</radius>
|
46
|
+
<name></name>
|
47
|
+
<line1>701 1st Ave</line1>
|
48
|
+
<line2>Sunnyvale, CA 94089-1019</line2>
|
49
|
+
<line3></line3>
|
50
|
+
<line4>United States</line4>
|
51
|
+
<house>701</house>
|
52
|
+
<street>1st Ave</street>
|
53
|
+
<xstreet></xstreet>
|
54
|
+
<unittype></unittype>
|
55
|
+
<unit></unit>
|
56
|
+
<postal>94089-1019</postal>
|
57
|
+
<neighborhood></neighborhood>
|
58
|
+
<city>Sunnyvale</city>
|
59
|
+
<county>Santa Clara County</county>
|
60
|
+
<state>California</state>
|
61
|
+
<country>United States</country>
|
62
|
+
<countrycode>US</countrycode>
|
63
|
+
<statecode>CA</statecode>
|
64
|
+
<countycode></countycode>
|
65
|
+
<uzip>94089</uzip>
|
66
|
+
<hash>DDAD1896CC0CDC41</hash>
|
67
|
+
<woeid>12797150</woeid>
|
68
|
+
<woetype>11</woetype>
|
69
|
+
</Result>
|
70
|
+
<Result>
|
71
|
+
<quality>80</quality>
|
72
|
+
<latitude>37.416275</latitude>
|
73
|
+
<longitude>-122.025092</longitude>
|
74
|
+
<offsetlat>37.416397</offsetlat>
|
75
|
+
<offsetlon>-122.025055</offsetlon>
|
76
|
+
<radius>500</radius>
|
77
|
+
<name></name>
|
78
|
+
<line1>701 1st Ave</line1>
|
79
|
+
<line2>Sunnyvale, CA 94089-1019</line2>
|
80
|
+
<line3></line3>
|
81
|
+
<line4>United States</line4>
|
82
|
+
<house>701</house>
|
83
|
+
<street>1st Ave</street>
|
84
|
+
<xstreet></xstreet>
|
85
|
+
<unittype></unittype>
|
86
|
+
<unit></unit>
|
87
|
+
<postal>94089-1019</postal>
|
88
|
+
<neighborhood></neighborhood>
|
89
|
+
<city>Sunnyvale</city>
|
90
|
+
<county>Santa Clara County</county>
|
91
|
+
<state>California</state>
|
92
|
+
<country>United States</country>
|
93
|
+
<countrycode>US</countrycode>
|
94
|
+
<statecode>CA</statecode>
|
95
|
+
<countycode></countycode>
|
96
|
+
<uzip>94089</uzip>
|
97
|
+
<hash>DDAD1896CC0CDC41</hash>
|
98
|
+
<woeid>12797150</woeid>
|
99
|
+
<woetype>11</woetype>
|
100
|
+
</Result>
|
101
|
+
</ResultSet>
|
@@ -0,0 +1,101 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<ResultSet version="1.0">
|
3
|
+
<Error>0</Error>
|
4
|
+
<ErrorMessage>No error</ErrorMessage>
|
5
|
+
<Locale>us_US</Locale>
|
6
|
+
<Quality>35</Quality>
|
7
|
+
<Found>3</Found>
|
8
|
+
<Result>
|
9
|
+
<quality>40</quality>
|
10
|
+
<latitude>37.416275</latitude>
|
11
|
+
<longitude>-122.025092</longitude>
|
12
|
+
<offsetlat>37.416397</offsetlat>
|
13
|
+
<offsetlon>-122.025055</offsetlon>
|
14
|
+
<radius>500</radius>
|
15
|
+
<name></name>
|
16
|
+
<line1>701 1st Ave</line1>
|
17
|
+
<line2>Sunnyvale, CA 94089-1019</line2>
|
18
|
+
<line3></line3>
|
19
|
+
<line4>United States</line4>
|
20
|
+
<house>701</house>
|
21
|
+
<street>1st Ave</street>
|
22
|
+
<xstreet></xstreet>
|
23
|
+
<unittype></unittype>
|
24
|
+
<unit></unit>
|
25
|
+
<postal>94089-1019</postal>
|
26
|
+
<neighborhood></neighborhood>
|
27
|
+
<city>Sunnyvale</city>
|
28
|
+
<county>Santa Clara County</county>
|
29
|
+
<state>California</state>
|
30
|
+
<country>United States</country>
|
31
|
+
<countrycode>US</countrycode>
|
32
|
+
<statecode>CA</statecode>
|
33
|
+
<countycode></countycode>
|
34
|
+
<uzip>94089</uzip>
|
35
|
+
<hash>DDAD1896CC0CDC41</hash>
|
36
|
+
<woeid>12797150</woeid>
|
37
|
+
<woetype>11</woetype>
|
38
|
+
</Result>
|
39
|
+
<Result>
|
40
|
+
<quality>87</quality>
|
41
|
+
<latitude>37.416275</latitude>
|
42
|
+
<longitude>-122.025092</longitude>
|
43
|
+
<offsetlat>37.416397</offsetlat>
|
44
|
+
<offsetlon>-122.025055</offsetlon>
|
45
|
+
<radius>500</radius>
|
46
|
+
<name></name>
|
47
|
+
<line1>701 1st Ave</line1>
|
48
|
+
<line2>Sunnyvale, CA 94089-1019</line2>
|
49
|
+
<line3></line3>
|
50
|
+
<line4>United States</line4>
|
51
|
+
<house>701</house>
|
52
|
+
<street>1st Ave</street>
|
53
|
+
<xstreet></xstreet>
|
54
|
+
<unittype></unittype>
|
55
|
+
<unit></unit>
|
56
|
+
<postal>94089-1019</postal>
|
57
|
+
<neighborhood></neighborhood>
|
58
|
+
<city>Sunnyvale</city>
|
59
|
+
<county>Santa Clara County</county>
|
60
|
+
<state>California</state>
|
61
|
+
<country>United States</country>
|
62
|
+
<countrycode>US</countrycode>
|
63
|
+
<statecode>CA</statecode>
|
64
|
+
<countycode></countycode>
|
65
|
+
<uzip>94089</uzip>
|
66
|
+
<hash>DDAD1896CC0CDC41</hash>
|
67
|
+
<woeid>12797150</woeid>
|
68
|
+
<woetype>11</woetype>
|
69
|
+
</Result>
|
70
|
+
<Result>
|
71
|
+
<quality>80</quality>
|
72
|
+
<latitude>37.416275</latitude>
|
73
|
+
<longitude>-122.025092</longitude>
|
74
|
+
<offsetlat>37.416397</offsetlat>
|
75
|
+
<offsetlon>-122.025055</offsetlon>
|
76
|
+
<radius>500</radius>
|
77
|
+
<name></name>
|
78
|
+
<line1>701 1st Ave</line1>
|
79
|
+
<line2>Sunnyvale, CA 94089-1019</line2>
|
80
|
+
<line3></line3>
|
81
|
+
<line4>United States</line4>
|
82
|
+
<house>701</house>
|
83
|
+
<street>1st Ave</street>
|
84
|
+
<xstreet></xstreet>
|
85
|
+
<unittype></unittype>
|
86
|
+
<unit></unit>
|
87
|
+
<postal>94089-1019</postal>
|
88
|
+
<neighborhood></neighborhood>
|
89
|
+
<city>Sunnyvale</city>
|
90
|
+
<county>Santa Clara County</county>
|
91
|
+
<state>California</state>
|
92
|
+
<country>United States</country>
|
93
|
+
<countrycode>US</countrycode>
|
94
|
+
<statecode>CA</statecode>
|
95
|
+
<countycode></countycode>
|
96
|
+
<uzip>94089</uzip>
|
97
|
+
<hash>DDAD1896CC0CDC41</hash>
|
98
|
+
<woeid>12797150</woeid>
|
99
|
+
<woetype>11</woetype>
|
100
|
+
</Result>
|
101
|
+
</ResultSet>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<ResultSet version="1.0">
|
3
|
+
<Error>0</Error>
|
4
|
+
<ErrorMessage>No error</ErrorMessage>
|
5
|
+
<Locale>us_US</Locale>
|
6
|
+
<Quality>87</Quality>
|
7
|
+
<Found>1</Found>
|
8
|
+
<Result>
|
9
|
+
<quality>87</quality>
|
10
|
+
<latitude>37.416275</latitude>
|
11
|
+
<longitude>-122.025092</longitude>
|
12
|
+
<offsetlat>37.416397</offsetlat>
|
13
|
+
<offsetlon>-122.025055</offsetlon>
|
14
|
+
<radius>500</radius>
|
15
|
+
<name></name>
|
16
|
+
<line1>701 1st Ave</line1>
|
17
|
+
<line2>Sunnyvale, CA 94089-1019</line2>
|
18
|
+
<line3></line3>
|
19
|
+
<line4>United States</line4>
|
20
|
+
<house>701</house>
|
21
|
+
<street>1st Ave</street>
|
22
|
+
<xstreet></xstreet>
|
23
|
+
<unittype></unittype>
|
24
|
+
<unit></unit>
|
25
|
+
<postal>94089-1019</postal>
|
26
|
+
<neighborhood></neighborhood>
|
27
|
+
<city>Sunnyvale</city>
|
28
|
+
<county>Santa Clara County</county>
|
29
|
+
<state>California</state>
|
30
|
+
<country>United States</country>
|
31
|
+
<countrycode>US</countrycode>
|
32
|
+
<statecode>CA</statecode>
|
33
|
+
<countycode></countycode>
|
34
|
+
<uzip>94089</uzip>
|
35
|
+
<hash>DDAD1896CC0CDC41</hash>
|
36
|
+
<woeid>12797150</woeid>
|
37
|
+
<woetype>11</woetype>
|
38
|
+
</Result>
|
39
|
+
</ResultSet>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ResponseTest < Test::Unit::TestCase
|
4
|
+
TEST_APPID = "asdfjkl"
|
5
|
+
COPTER_WORLD_HQ = "103 W. Main Street Charlottesville VA"
|
6
|
+
YAHOO_BASE_URL = "http://where.yahooapis.com/geocode"
|
7
|
+
|
8
|
+
context "results parsing" do
|
9
|
+
should "correctly parse results array" do
|
10
|
+
typh_resp = mock()
|
11
|
+
|
12
|
+
typh_resp.expects(:body).returns(File.open(File.join(TEST_DATA_DIR, "single_result.xml")).read)
|
13
|
+
typh_resp.expects(:code).returns("200")
|
14
|
+
|
15
|
+
response = Woefoo::Response.new(COPTER_WORLD_HQ, typh_resp)
|
16
|
+
|
17
|
+
assert_equal response.code, "200"
|
18
|
+
assert_equal response.found, 1
|
19
|
+
assert_equal response.results.count, 1
|
20
|
+
end
|
21
|
+
|
22
|
+
should "sort results by quality" do
|
23
|
+
typh_resp = mock()
|
24
|
+
|
25
|
+
typh_resp.expects(:body).returns(File.open(File.join(TEST_DATA_DIR, "multiple_results_1.xml")).read)
|
26
|
+
typh_resp.expects(:code).returns("200")
|
27
|
+
|
28
|
+
response = Woefoo::Response.new(COPTER_WORLD_HQ, typh_resp)
|
29
|
+
|
30
|
+
assert_equal response.code, "200"
|
31
|
+
assert_equal response.found, 3
|
32
|
+
assert_equal response.results.count, 3
|
33
|
+
assert response.results[0][:quality] > response.results[1][:quality]
|
34
|
+
assert response.results[1][:quality] > response.results[2][:quality]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup(:default, :test)
|
4
|
+
Bundler.require(:default, :test)
|
5
|
+
|
6
|
+
dir = File.dirname(File.expand_path(__FILE__))
|
7
|
+
$LOAD_PATH.unshift dir + '/../lib'
|
8
|
+
|
9
|
+
TEST_DATA_DIR = File.join(dir, "data")
|
10
|
+
TEST_APPID = "asdfjkl"
|
11
|
+
COPTER_WORLD_HQ = "103 W. Main Street Charlottesville VA"
|
12
|
+
YAHOO_BASE_URL = "http://where.yahooapis.com/geocode"
|
13
|
+
|
14
|
+
require 'test/unit'
|
15
|
+
require 'shoulda'
|
16
|
+
require 'shoulda/context'
|
17
|
+
require 'mocha'
|
18
|
+
require 'mocha/integration/test_unit'
|
data/test/woefoo_test.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class WoefooTest < Test::Unit::TestCase
|
4
|
+
context 'lookup exceptions' do
|
5
|
+
should "throw an exception if appid is not defined" do
|
6
|
+
assert_raises RuntimeError do
|
7
|
+
begin
|
8
|
+
Woefoo.lookup(COPTER_WORLD_HQ)
|
9
|
+
rescue RuntimeError => ex
|
10
|
+
assert_equal ex.message, "Woefoo.appid is not defined"
|
11
|
+
raise
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
should "throw an exception if the query is nil" do
|
17
|
+
assert_raises RuntimeError do
|
18
|
+
begin
|
19
|
+
Woefoo.appid=TEST_APPID
|
20
|
+
Woefoo.lookup
|
21
|
+
rescue RuntimeError => ex
|
22
|
+
assert ex.message.include?("Invalid query")
|
23
|
+
raise
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
should "throw an exception if the query is blank" do
|
29
|
+
assert_raises RuntimeError do
|
30
|
+
begin
|
31
|
+
Woefoo.appid=TEST_APPID
|
32
|
+
Woefoo.lookup({:query => ""})
|
33
|
+
rescue RuntimeError => ex
|
34
|
+
assert ex.message.include?("Invalid query")
|
35
|
+
raise
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "lookup url generation" do
|
42
|
+
should "use default placefinder count if none is defined" do
|
43
|
+
typh_resp = Typhoeus::Response.new
|
44
|
+
Woefoo.appid=TEST_APPID
|
45
|
+
url_should_be="#{YAHOO_BASE_URL}?appid=#{TEST_APPID}&count=3&q=#{::CGI.escape(COPTER_WORLD_HQ)}"
|
46
|
+
Typhoeus::Request.expects(:get).with(url_should_be).returns(typh_resp).at_least_once
|
47
|
+
Woefoo::Response.expects(:new).with({:query => COPTER_WORLD_HQ}, typh_resp).at_least_once
|
48
|
+
|
49
|
+
Woefoo.lookup({:query => COPTER_WORLD_HQ})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "canonical town matching" do
|
54
|
+
should "not return canonical town result if input quality is below minimum" do
|
55
|
+
Woefoo.min_input_quality=50
|
56
|
+
response = mock()
|
57
|
+
response.expects(:input_quality).returns(35)
|
58
|
+
ctr = Woefoo.canonical_town_match(response)
|
59
|
+
assert ctr === nil, "No town should match input because input quality was too low"
|
60
|
+
|
61
|
+
response = mock()
|
62
|
+
response.expects(:input_quality).returns(49)
|
63
|
+
ctr = Woefoo.canonical_town_match(response)
|
64
|
+
assert ctr === nil, "No town should match input because input quality was too low"
|
65
|
+
end
|
66
|
+
|
67
|
+
should "use default validator if no users validators are specified" do
|
68
|
+
response = mock()
|
69
|
+
place = mock()
|
70
|
+
Woefoo::GeoplanetPlace.expects(:find_by_woeid).with(12345).returns(place)
|
71
|
+
response.expects(:input_quality).twice.returns(75)
|
72
|
+
response.expects(:results).returns([{:quality => 80, :woeid=>12345, :woetype=>7}])
|
73
|
+
response.expects(:query_options).returns({})
|
74
|
+
place.expects(:place_type).returns("Town")
|
75
|
+
|
76
|
+
best_woeid = Woefoo.canonical_town_match(response)
|
77
|
+
assert_equal 12345, best_woeid, "WOEID town match mismatch!"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/woefoo.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "woefoo/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "woefoo"
|
7
|
+
s.version = Woefoo::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["smnirven"]
|
10
|
+
s.email = ["smnirven@gmail.com"]
|
11
|
+
s.homepage = "http://smnirven.com"
|
12
|
+
s.summary = %q{Lookup yahoo woe (where on earth) ids}
|
13
|
+
s.description = %q{Lookup yahoo woe (where on earth) ids}
|
14
|
+
|
15
|
+
s.rubyforge_project = "woefoo"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency("typhoeus", ">=0.1.27")
|
23
|
+
s.add_dependency("nokogiri", ">=1.4.2")
|
24
|
+
s.add_dependency("ancestry", ">=1.2.4")
|
25
|
+
s.add_dependency("activerecord", ">=2.3.8")
|
26
|
+
s.add_dependency("geokit", ">=1.5.0")
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: woefoo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 6
|
10
|
+
version: 0.0.6
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- smnirven
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-05-25 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: typhoeus
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 45
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 1
|
33
|
+
- 27
|
34
|
+
version: 0.1.27
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: nokogiri
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 4
|
49
|
+
- 2
|
50
|
+
version: 1.4.2
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: ancestry
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 23
|
62
|
+
segments:
|
63
|
+
- 1
|
64
|
+
- 2
|
65
|
+
- 4
|
66
|
+
version: 1.2.4
|
67
|
+
type: :runtime
|
68
|
+
version_requirements: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activerecord
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 19
|
78
|
+
segments:
|
79
|
+
- 2
|
80
|
+
- 3
|
81
|
+
- 8
|
82
|
+
version: 2.3.8
|
83
|
+
type: :runtime
|
84
|
+
version_requirements: *id004
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: geokit
|
87
|
+
prerelease: false
|
88
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 3
|
94
|
+
segments:
|
95
|
+
- 1
|
96
|
+
- 5
|
97
|
+
- 0
|
98
|
+
version: 1.5.0
|
99
|
+
type: :runtime
|
100
|
+
version_requirements: *id005
|
101
|
+
description: Lookup yahoo woe (where on earth) ids
|
102
|
+
email:
|
103
|
+
- smnirven@gmail.com
|
104
|
+
executables: []
|
105
|
+
|
106
|
+
extensions: []
|
107
|
+
|
108
|
+
extra_rdoc_files: []
|
109
|
+
|
110
|
+
files:
|
111
|
+
- .gitignore
|
112
|
+
- Gemfile
|
113
|
+
- Rakefile
|
114
|
+
- lib/woefoo.rb
|
115
|
+
- lib/woefoo/config.rb
|
116
|
+
- lib/woefoo/geoplanet_adjacency.rb
|
117
|
+
- lib/woefoo/geoplanet_alias.rb
|
118
|
+
- lib/woefoo/geoplanet_place.rb
|
119
|
+
- lib/woefoo/response.rb
|
120
|
+
- lib/woefoo/town_match_validators.rb
|
121
|
+
- lib/woefoo/version.rb
|
122
|
+
- test/data/multiple_results_1.xml
|
123
|
+
- test/data/poor_input_quality.xml
|
124
|
+
- test/data/single_result.xml
|
125
|
+
- test/response_test.rb
|
126
|
+
- test/test_helper.rb
|
127
|
+
- test/woefoo_test.rb
|
128
|
+
- woefoo.gemspec
|
129
|
+
has_rdoc: true
|
130
|
+
homepage: http://smnirven.com
|
131
|
+
licenses: []
|
132
|
+
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
hash: 3
|
144
|
+
segments:
|
145
|
+
- 0
|
146
|
+
version: "0"
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
hash: 3
|
153
|
+
segments:
|
154
|
+
- 0
|
155
|
+
version: "0"
|
156
|
+
requirements: []
|
157
|
+
|
158
|
+
rubyforge_project: woefoo
|
159
|
+
rubygems_version: 1.5.2
|
160
|
+
signing_key:
|
161
|
+
specification_version: 3
|
162
|
+
summary: Lookup yahoo woe (where on earth) ids
|
163
|
+
test_files:
|
164
|
+
- test/data/multiple_results_1.xml
|
165
|
+
- test/data/poor_input_quality.xml
|
166
|
+
- test/data/single_result.xml
|
167
|
+
- test/response_test.rb
|
168
|
+
- test/test_helper.rb
|
169
|
+
- test/woefoo_test.rb
|