sundawg_geonames_client 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +8 -0
- data/README.rdoc +65 -0
- data/Rakefile +13 -0
- data/lib/geonames_client.rb +48 -0
- data/lib/geonames_client_helper.rb +24 -0
- data/sundawg_geonames_client.gemspec +34 -0
- data/test/geonames_client_helper_test.rb +29 -0
- data/test/geonames_client_integration_test.rb +47 -0
- data/test/geonames_client_test.rb +40 -0
- metadata +91 -0
data/Manifest
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
== About
|
2
|
+
|
3
|
+
Sundawg Geonames Client is a ruby gem for the RESTful web services provided by geonames.org. This client attempts to use the method_missing functionality of Ruby to quickly provide generic support for any and all services provided by geonames.org. For more information on these services and geonames, please see the following:
|
4
|
+
|
5
|
+
- http://www.geonames.org
|
6
|
+
- http://www.geonames.org/export/ws-overview.html
|
7
|
+
|
8
|
+
== Installation
|
9
|
+
|
10
|
+
You can install the gem as so:
|
11
|
+
|
12
|
+
gem install sundawg_geonames_client
|
13
|
+
|
14
|
+
== Tests
|
15
|
+
|
16
|
+
You can confirm tests and functionality with geonames.org by running:
|
17
|
+
|
18
|
+
rake tests
|
19
|
+
|
20
|
+
== Usage
|
21
|
+
|
22
|
+
To use the client, you simply call the service method you want on geonames.org directly on the client. Any parameters that the service requires are passed as a hash object to the client.
|
23
|
+
|
24
|
+
For example, to call the postalCodeLookup service, you would do the following:
|
25
|
+
|
26
|
+
SunDawg::GeonamesClient.new.postal_code_lookup :postalcode => "10069", :country => "US"
|
27
|
+
=> {"postalcodes":[{"adminName2":"New York","adminCode2":"061","postalcode":"10069","adminCode1":"NY","countryCode":"US","lng":-73.988381,"placeName":"New York City","lat":40.777952,"adminName1":"New York"}]}
|
28
|
+
|
29
|
+
By default, the client uses the JSON service provided by geonames.org and provides the raw JSON output back to you. You can then use the JSON parser of your choice to parse the data i.e. json gem or ActiveSupport.
|
30
|
+
|
31
|
+
- http://flori.github.com/json/
|
32
|
+
- http://api.rubyonrails.org/classes/ActiveSupport/JSON.html
|
33
|
+
|
34
|
+
The client allows you to pass in several configration changes. If you want to use camelcase notation as the service is provided, then you can pass in :camelize => true when creating the client. This will remove all of the string manipulation to turn snake case notation into camel notation, thus allowing you to break out of the Ruby conventions if you want.
|
35
|
+
|
36
|
+
Here is the same call, but with camel notation.
|
37
|
+
|
38
|
+
SunDawg::GeonamesClient.new(:camelize => true).postalCodeLookup :postalcode => "10069", :country => "US"
|
39
|
+
|
40
|
+
You can also turn off JSON support and attempt to hit the standard service with :json => false. One small warning, is that the documentation is unclear as to whether you will always get XML or non-markup text in your response. For example the countryCode service seems to return the ISO Alpha2 country when you hit the non-JSON service. Passing in a :type => "xml" will force XML.
|
41
|
+
|
42
|
+
SunDawg::GeonamesClient.new(:json => false).country_code :lat => "47.03", :lng => "10.2", :type => "xml"
|
43
|
+
|
44
|
+
Lastly, you can pass a username to the client. This is a new requirement imposed by geonames.org to identify a malignant client that is creating DDOS issues for geonames.org. Their explanation is here:
|
45
|
+
|
46
|
+
http://geonames.wordpress.com/2010/03/16/ddos-part-ii/
|
47
|
+
|
48
|
+
SunDawg::Geonames client will default the username if you do not provide one.
|
49
|
+
|
50
|
+
== Helper Feature
|
51
|
+
|
52
|
+
The reason for this library is for my need to convert postal code information into other sorts of useful information. This requires two calls to the geonames.org services, one to turn a postal code into longitude and latitude coordinates, then a call to the service that will provide the interesting information.
|
53
|
+
|
54
|
+
There is a small helper class that will allow you to chain a postal_code_lookup service call with other geonames.org service calls. For example, if I want to get the timezone associated with a postal code, I can use the helper as so:
|
55
|
+
|
56
|
+
json = SunDawg::GeonamesClientHelper.postal_code_to(:postalcode => "10069", :country => "US") do |client, params, original_json|
|
57
|
+
assert_equal "40.777952", params[:lat]
|
58
|
+
assert_equal "-73.988381", params[:lng]
|
59
|
+
client.timezone params
|
60
|
+
end
|
61
|
+
assert_match "America/New_York", json
|
62
|
+
|
63
|
+
The helper is invoking the postal code service and then executing the yield block. You are provided an instance of the client with whatever options you specified, a hash containing longitude and latitude that can be passed to subsequent calls, and for complete-ness sake, the JSON response from the postal call.
|
64
|
+
|
65
|
+
The helper automatically uses the first match from the postal service.
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Rakefile
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'echoe'
|
5
|
+
|
6
|
+
Echoe.new('sundawg_geonames_client', '0.0.1') do |p|
|
7
|
+
p.description = "Ruby client interface to all and future RESTful geo-services provided by geonames.org."
|
8
|
+
p.url = "http://github.com/SunDawg/geonames_client"
|
9
|
+
p.author = "Christopher Sun"
|
10
|
+
p.email = "christopher.sun@gmail.com"
|
11
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
12
|
+
p.development_dependencies = ['mocha']
|
13
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module SunDawg
|
6
|
+
class GeonamesClient
|
7
|
+
URL = "http://ws.geonames.org"
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
options[:json].nil? ? options[:json] = true : nil
|
11
|
+
options[:camelize].nil? ? options[:camelize] = false : nil
|
12
|
+
options[:username].nil? ? options[:username] = "SunDawg" : nil
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(signature, *args)
|
17
|
+
raise ArgumentError.new("must provide a hash of geoname parameters for first argument when calling web services [#{signature}] [#{args.inspect}]") if args.size < 1 || !args[0].is_a?(Hash)
|
18
|
+
signature = smart_camelize(signature.to_s) unless @options[:camelize]
|
19
|
+
do_http_call(service_resource(signature), args[0])
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def do_http_call(resource, params)
|
25
|
+
params.merge!(:username => @options[:username])
|
26
|
+
resource += "?" + query_string(params)
|
27
|
+
url = URI.parse(resource)
|
28
|
+
response = Net::HTTP.start(url.host, url.port) do |http|
|
29
|
+
http.get(resource)
|
30
|
+
end
|
31
|
+
response.body
|
32
|
+
end
|
33
|
+
|
34
|
+
def query_string(params)
|
35
|
+
params.keys.map { |i| "#{i}=#{CGI::escape(params[i])}#{i == params.keys.last ? "" : "&"}" }.join
|
36
|
+
end
|
37
|
+
|
38
|
+
# provided to remove active support dependency
|
39
|
+
def smart_camelize(s)
|
40
|
+
s = s.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join
|
41
|
+
"#{s[0..0].swapcase!}#{s[1..s.length]}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def service_resource(service)
|
45
|
+
"#{URL}/#{service}#{ @options[:json] ? "JSON" : "" }"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SunDawg
|
2
|
+
class GeonamesClientHelper
|
3
|
+
|
4
|
+
def self.postal_code_to(params, options = {})
|
5
|
+
json = SunDawg::GeonamesClient.new(options.reject { |k, v| k == :json }.reject { |k, v| k == :camelize }).postal_code_lookup params
|
6
|
+
yield(SunDawg::GeonamesClient.new(options), {:lat => get_lat(json), :lng => get_lng(json)}, json)
|
7
|
+
end
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
# simple string search so we don't need to import a json library
|
12
|
+
def self.get_lat(s)
|
13
|
+
json_parse("lat", s)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.get_lng(s)
|
17
|
+
json_parse("lng", s)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.json_parse(field, s)
|
21
|
+
s.match(/"#{field}":-?(\d|\.)+/)[0].split(":")[1]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{sundawg_geonames_client}
|
5
|
+
s.version = "0.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Christopher Sun"]
|
9
|
+
s.date = %q{2010-04-16}
|
10
|
+
s.description = %q{Ruby client interface to all and future RESTful geo-services provided by geonames.org.}
|
11
|
+
s.email = %q{christopher.sun@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["README.rdoc", "lib/geonames_client.rb", "lib/geonames_client_helper.rb"]
|
13
|
+
s.files = ["Manifest", "README.rdoc", "Rakefile", "lib/geonames_client.rb", "lib/geonames_client_helper.rb", "test/geonames_client_helper_test.rb", "test/geonames_client_integration_test.rb", "test/geonames_client_test.rb", "sundawg_geonames_client.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/SunDawg/geonames_client}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Sundawg_geonames_client", "--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{sundawg_geonames_client}
|
18
|
+
s.rubygems_version = %q{1.3.6}
|
19
|
+
s.summary = %q{Ruby client interface to all and future RESTful geo-services provided by geonames.org.}
|
20
|
+
s.test_files = ["test/geonames_client_helper_test.rb", "test/geonames_client_integration_test.rb", "test/geonames_client_test.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
28
|
+
else
|
29
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
30
|
+
end
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geonames_client_helper'
|
3
|
+
|
4
|
+
class GeonamesClientHelperTest < Test::Unit::TestCase
|
5
|
+
def test_postal_code_to_timezone
|
6
|
+
json = SunDawg::GeonamesClientHelper.postal_code_to(:postalcode => "10069", :country => "US") do |client, params|
|
7
|
+
assert_equal "40.777952", params[:lat]
|
8
|
+
assert_equal "-73.988381", params[:lng]
|
9
|
+
client.timezone params
|
10
|
+
end
|
11
|
+
assert_match "America/New_York", json
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_get_lat
|
15
|
+
s = '{"postalcodes":[{"adminName2":"Olsztyn","postalcode":"10-069","countryCode":"PL","lng":20.4833333,"placeName":"Olsztyn","lat":53.7833333,"adminName1":"Warmińsko-Mazurskie"},{"adminName2":"New York","adminCode2":"061","postalcode":"10069","adminCode1":"NY","countryCode":"US","lng":-73.988381,"placeName":"New York City","lat":40.777952,"adminName1":"New York"},{"adminName2":"Torino","adminCode2":"TO","postalcode":"10069","countryCode":"IT","lng":7.248118,"placeName":"Villar Perosa","lat":44.91932,"adminName1":"Piemonte"}]}'
|
16
|
+
assert_equal "53.7833333", SunDawg::GeonamesClientHelper.send(:get_lat, s)
|
17
|
+
|
18
|
+
s = '{"postalcodes":[{"adminName2":"New York","adminCode2":"061","postalcode":"10069","adminCode1":"NY","countryCode":"US","lng":-73.988381,"placeName":"New York City","lat":40.777952,"adminName1":"New York"}]}'
|
19
|
+
assert_equal "40.777952", SunDawg::GeonamesClientHelper.send(:get_lat, s)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_get_lng
|
23
|
+
s = '{"postalcodes":[{"adminName2":"Olsztyn","postalcode":"10-069","countryCode":"PL","lng":20.4833333,"placeName":"Olsztyn","lat":53.7833333,"adminName1":"Warmińsko-Mazurskie"},{"adminName2":"New York","adminCode2":"061","postalcode":"10069","adminCode1":"NY","countryCode":"US","lng":-73.988381,"placeName":"New York City","lat":40.777952,"adminName1":"New York"},{"adminName2":"Torino","adminCode2":"TO","postalcode":"10069","countryCode":"IT","lng":7.248118,"placeName":"Villar Perosa","lat":44.91932,"adminName1":"Piemonte"}]}'
|
24
|
+
assert_equal "20.4833333", SunDawg::GeonamesClientHelper.send(:get_lng, s)
|
25
|
+
|
26
|
+
s = '{"postalcodes":[{"adminName2":"New York","adminCode2":"061","postalcode":"10069","adminCode1":"NY","countryCode":"US","lng":-73.988381,"placeName":"New York City","lat":40.777952,"adminName1":"New York"}]}'
|
27
|
+
assert_equal "-73.988381", SunDawg::GeonamesClientHelper.send(:get_lng, s)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'geonames_client'
|
4
|
+
|
5
|
+
class GeonamesClientIntegrationTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@client = SunDawg::GeonamesClient.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_postal_code_lookup_json
|
11
|
+
json = client.postal_code_lookup :postalcode => "10069", :country => "US"
|
12
|
+
assert_match "New York", json
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_postal_code_lookup_camelize
|
16
|
+
json = SunDawg::GeonamesClient.new(:camelize => true).postalCodeLookup :postalcode => "10069", :country => "US"
|
17
|
+
assert_match "New York", json
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_country_code_json
|
21
|
+
json = client.country_code :lat => "47.03", :lng => "10.2"
|
22
|
+
assert_match "Austria", json
|
23
|
+
assert_match "AT", json
|
24
|
+
|
25
|
+
json = client.country_code :lat => "40.7", :lng => "-73.98"
|
26
|
+
assert_match "United States", json
|
27
|
+
assert_match "US", json
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_timezone
|
31
|
+
json = client.timezone :lat => "40.777952", :lng => "-73.988381"
|
32
|
+
assert_match "America/New_York", json
|
33
|
+
end
|
34
|
+
|
35
|
+
# validate hitting non JSON resource
|
36
|
+
def test_country_code_xml
|
37
|
+
xml = SunDawg::GeonamesClient.new(:json => false).country_code :lat => "47.03", :lng => "10.2", :type => "xml"
|
38
|
+
assert_match "<countryName>Austria</countryName>", xml
|
39
|
+
assert_match "<countryCode>AT</countryCode>", xml
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def client
|
45
|
+
@client
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'geonames_client'
|
4
|
+
require 'mocha'
|
5
|
+
|
6
|
+
class GeonamesClientTest < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@client = SunDawg::GeonamesClient.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_invalid_arguments
|
12
|
+
assert_raise ArgumentError do
|
13
|
+
client.foo
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_do_http_call
|
18
|
+
params = {:arg1 => "x", :arg2 => "y"}
|
19
|
+
client.expects(:do_http_call).with("http://ws.geonames.org/fooJSON", params)
|
20
|
+
client.foo params
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_query_string
|
24
|
+
params = {:arg1 => "x", :arg2 => "123"}
|
25
|
+
query_string = client.send(:query_string, params)
|
26
|
+
|
27
|
+
# hash is not-ordered
|
28
|
+
assert (query_string == "arg1=x&arg2=123" or query_string == "arg2=123&arg1=x")
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_smart_camelize
|
32
|
+
assert_equal "helloWorld", client.send(:smart_camelize, "hello_world")
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def client
|
38
|
+
@client
|
39
|
+
end
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sundawg_geonames_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Christopher Sun
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-04-16 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: mocha
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
description: Ruby client interface to all and future RESTful geo-services provided by geonames.org.
|
33
|
+
email: christopher.sun@gmail.com
|
34
|
+
executables: []
|
35
|
+
|
36
|
+
extensions: []
|
37
|
+
|
38
|
+
extra_rdoc_files:
|
39
|
+
- README.rdoc
|
40
|
+
- lib/geonames_client.rb
|
41
|
+
- lib/geonames_client_helper.rb
|
42
|
+
files:
|
43
|
+
- Manifest
|
44
|
+
- README.rdoc
|
45
|
+
- Rakefile
|
46
|
+
- lib/geonames_client.rb
|
47
|
+
- lib/geonames_client_helper.rb
|
48
|
+
- test/geonames_client_helper_test.rb
|
49
|
+
- test/geonames_client_integration_test.rb
|
50
|
+
- test/geonames_client_test.rb
|
51
|
+
- sundawg_geonames_client.gemspec
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: http://github.com/SunDawg/geonames_client
|
54
|
+
licenses: []
|
55
|
+
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options:
|
58
|
+
- --line-numbers
|
59
|
+
- --inline-source
|
60
|
+
- --title
|
61
|
+
- Sundawg_geonames_client
|
62
|
+
- --main
|
63
|
+
- README.rdoc
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
segments:
|
78
|
+
- 1
|
79
|
+
- 2
|
80
|
+
version: "1.2"
|
81
|
+
requirements: []
|
82
|
+
|
83
|
+
rubyforge_project: sundawg_geonames_client
|
84
|
+
rubygems_version: 1.3.6
|
85
|
+
signing_key:
|
86
|
+
specification_version: 3
|
87
|
+
summary: Ruby client interface to all and future RESTful geo-services provided by geonames.org.
|
88
|
+
test_files:
|
89
|
+
- test/geonames_client_helper_test.rb
|
90
|
+
- test/geonames_client_integration_test.rb
|
91
|
+
- test/geonames_client_test.rb
|