heycarsten-gcoder 0.3.0

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ coverage
2
+ rdoc
3
+ pkg
data/README.rdoc ADDED
@@ -0,0 +1,7 @@
1
+ = GCoder
2
+
3
+ Instead of letting postal codes turn me into a postal coder, I made this gem!
4
+
5
+ == Copyright
6
+
7
+ Copyright (C) 2009 Carsten Nielsen. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |g|
8
+ g.name = 'gcoder'
9
+ g.summary = 'A library for geocoding postal codes via the Google Maps ' \
10
+ 'Geocoding API with a persisted cache through Tokyo Tyrant'
11
+ g.email = 'heycarsten@gmail.com'
12
+ g.homepage = 'http://github.com/heycarsten/gcoder'
13
+ g.authors = ['Carsten Nielsen']
14
+ end
15
+ rescue LoadError
16
+ puts 'Jeweler not available. Install it with: sudo gem install ' \
17
+ 'technicalpickles-jeweler -s http://gems.github.com'
18
+ end
19
+
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/*_test.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/*_test.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort 'RCov is not available. In order to run rcov, you must: sudo gem ' \
39
+ 'install spicycode-rcov'
40
+ end
41
+ end
42
+
43
+
44
+ task :default => :test
45
+
46
+ # require 'rake/rdoctask'
47
+ # Rake::RDocTask.new do |rdoc|
48
+ # if File.exist?('VERSION.yml')
49
+ # config = YAML.load(File.read('VERSION.yml'))
50
+ # version = [config[:major], config[:minor], config[:patch]].join('.')
51
+ # else
52
+ # version = ''
53
+ # end
54
+ # rdoc.rdoc_dir = 'rdoc'
55
+ # rdoc.title = "GCoder #{version}"
56
+ # rdoc.rdoc_files.include('README*')
57
+ # rdoc.rdoc_files.include('lib/**/*.rb')
58
+ # end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 3
3
+ :patch: 0
4
+ :major: 0
data/gcoder.gemspec ADDED
@@ -0,0 +1,57 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{gcoder}
5
+ s.version = "0.3.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Carsten Nielsen"]
9
+ s.date = %q{2009-06-26}
10
+ s.email = %q{heycarsten@gmail.com}
11
+ s.extra_rdoc_files = [
12
+ "README.rdoc"
13
+ ]
14
+ s.files = [
15
+ ".document",
16
+ ".gitignore",
17
+ "README.rdoc",
18
+ "Rakefile",
19
+ "VERSION.yml",
20
+ "gcoder.gemspec",
21
+ "lib/gcoder.rb",
22
+ "lib/gcoder/config.rb",
23
+ "lib/gcoder/geocoding_api.rb",
24
+ "lib/gcoder/persistence.rb",
25
+ "lib/gcoder/resolver.rb",
26
+ "test/config_test.rb",
27
+ "test/gcoder_test.rb",
28
+ "test/geocoding_api_test.rb",
29
+ "test/persistence_test.rb",
30
+ "test/resolver_test.rb",
31
+ "test/test_helper.rb"
32
+ ]
33
+ s.has_rdoc = true
34
+ s.homepage = %q{http://github.com/heycarsten/gcoder}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.1}
38
+ s.summary = %q{A library for geocoding postal codes via the Google Maps Geocoding API with a persisted cache through Tokyo Tyrant}
39
+ s.test_files = [
40
+ "test/config_test.rb",
41
+ "test/gcoder_test.rb",
42
+ "test/geocoding_api_test.rb",
43
+ "test/persistence_test.rb",
44
+ "test/resolver_test.rb",
45
+ "test/test_helper.rb"
46
+ ]
47
+
48
+ if s.respond_to? :specification_version then
49
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
50
+ s.specification_version = 2
51
+
52
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
53
+ else
54
+ end
55
+ else
56
+ end
57
+ end
@@ -0,0 +1,26 @@
1
+ module GCoder
2
+ module Config
3
+
4
+ # If :tt_port is 0 then :tt_host should point to a Unix socket.
5
+ @default_settings = {
6
+ :gmaps_api_key => nil,
7
+ :gmaps_api_timeout => 2,
8
+ :tt_host => nil,
9
+ :tt_port => 0,
10
+ :append_query => nil,
11
+ :no_raise_on_connection_fail => false }
12
+
13
+ def self.merge(overrides)
14
+ @default_settings.merge(overrides)
15
+ end
16
+
17
+ def self.update(hsh)
18
+ @default_settings.update(hsh)
19
+ end
20
+
21
+ def self.[](key)
22
+ @default_settings[key]
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,167 @@
1
+ module GCoder
2
+ module GeocodingAPI
3
+
4
+ BASE_URI = 'http://maps.google.com/maps/geo'
5
+ BASE_PARAMS = {
6
+ :q => nil,
7
+ :output => 'json',
8
+ :oe => 'utf8',
9
+ :sensor => 'false',
10
+ :key => nil }
11
+
12
+
13
+ class Request
14
+
15
+ def self.get(query, options = {})
16
+ response = new(query, options).get
17
+ response.validate!
18
+ response.to_h
19
+ end
20
+
21
+ def initialize(query, options = {})
22
+ unless query
23
+ raise Errors::BlankRequestError, "query cannot be nil"
24
+ end
25
+ unless query.is_a?(String)
26
+ raise Errors::MalformedQueryError, "query must be String, not: #{query.class}"
27
+ end
28
+ @config = Config.merge(options)
29
+ @query = query
30
+ validate_state!
31
+ end
32
+
33
+ def params
34
+ BASE_PARAMS.merge(:key => @config[:gmaps_api_key], :q => query)
35
+ end
36
+
37
+ def to_params
38
+ params.inject([]) do |array, (key, value)|
39
+ array << "#{uri_escape key}=#{uri_escape value}"
40
+ end.join('&')
41
+ end
42
+
43
+ def query
44
+ @config[:append_query] ? "#{@query} #{@config[:append_query]}" : @query
45
+ end
46
+
47
+ def uri
48
+ [BASE_URI, '?', to_params].join
49
+ end
50
+
51
+ def get
52
+ return @json_response if @json_response
53
+ Timeout.timeout(@config[:gmaps_api_timeout]) do
54
+ Response.new(http_get)
55
+ end
56
+ rescue Timeout::Error
57
+ raise Errors::RequestTimeoutError, 'The query timed out at ' \
58
+ "#{@config[:timeout]} second(s)"
59
+ end
60
+
61
+ protected
62
+
63
+ def http_get
64
+ open(uri).read
65
+ end
66
+
67
+ # Snaked from Rack::Utils which 'stole' it from Camping.
68
+ def uri_escape(string)
69
+ string.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
70
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
71
+ end.tr(' ', '+')
72
+ end
73
+
74
+ def validate_state!
75
+ if '' == query.strip.to_s
76
+ raise Errors::BlankRequestError, 'You must specifiy a query to resolve.'
77
+ end
78
+ unless @config[:gmaps_api_key]
79
+ raise Errors::NoAPIKeyError, 'You must provide a Google Maps API ' \
80
+ 'key in your configuration. Go to http://code.google.com/apis/maps/' \
81
+ 'signup.html to get one.'
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+
88
+ class Response
89
+
90
+ def initialize(raw_response)
91
+ @response = JSON.parse(raw_response)
92
+ end
93
+
94
+ def status
95
+ @response['Status']['code']
96
+ end
97
+
98
+ def validate!
99
+ case status
100
+ when 400
101
+ raise Errors::APIMalformedRequestError, 'The GMaps Geo API has ' \
102
+ 'indicated that the request is not formed correctly: ' \
103
+ "(#{@response.inspect})"
104
+ when 602
105
+ raise Errors::APIGeocodingError, 'The GMaps Geo API has indicated ' \
106
+ "that it is not able to geocode the request: (#{@response.inspect})"
107
+ end
108
+ end
109
+
110
+ def to_h
111
+ { :accuracy => accuracy,
112
+ :country => {
113
+ :name => country_name,
114
+ :code => country_code,
115
+ :administrative_area => administrative_area },
116
+ :point => {
117
+ :longitude => longitude,
118
+ :latitude => latitude },
119
+ :box => box }
120
+ end
121
+
122
+ def box
123
+ { :north => placemark['ExtendedData']['LatLonBox']['north'],
124
+ :south => placemark['ExtendedData']['LatLonBox']['south'],
125
+ :east => placemark['ExtendedData']['LatLonBox']['east'],
126
+ :west => placemark['ExtendedData']['LatLonBox']['west'] }
127
+ end
128
+
129
+ def accuracy
130
+ placemark['AddressDetails']['Accuracy']
131
+ end
132
+
133
+ def latitude
134
+ placemark['Point']['coordinates'][1]
135
+ end
136
+
137
+ def longitude
138
+ placemark['Point']['coordinates'][0]
139
+ end
140
+
141
+ def latlon_box
142
+ placemark['ExtendedData']['LatLonBox']
143
+ end
144
+
145
+ def country_name
146
+ placemark['AddressDetails']['Country']['CountryName']
147
+ end
148
+
149
+ def country_code
150
+ placemark['AddressDetails']['Country']['CountryNameCode']
151
+ end
152
+
153
+ def administrative_area
154
+ placemark['AddressDetails']['Country']['AdministrativeArea']['AdministrativeAreaName']
155
+ end
156
+
157
+ private
158
+
159
+ def placemark
160
+ @response['Placemark'][0]
161
+ end
162
+
163
+ end
164
+
165
+
166
+ end
167
+ end
@@ -0,0 +1,80 @@
1
+ module GCoder
2
+ module Persistence
3
+
4
+
5
+ class DataStore
6
+
7
+ def initialize(options = {})
8
+ @config = Config.merge(options)
9
+ unless @config[:tt_host]
10
+ raise ArgumentError, ':tt_host must be specified when it is not ' \
11
+ 'present in the global configuration.'
12
+ end
13
+ @tyrant = Rufus::Tokyo::Tyrant.new(@config[:tt_host], @config[:tt_port])
14
+ rescue RuntimeError => boom
15
+ if boom.message.include?('couldn\'t connect to tyrant')
16
+ errmsg = 'Unable to connect to the Tokyo Tyrant server at ' \
17
+ "#{@config[:tt_host]} [#{@config[:tt_port]}]"
18
+ if @config[:no_raise_on_connection_fail]
19
+ @tyrant = nil
20
+ STDERR.puts("[GCODER] #{errmsg}")
21
+ else
22
+ raise Errors::TTUnableToConnectError, errmsg
23
+ end
24
+ else
25
+ raise boom
26
+ end
27
+ end
28
+
29
+ def fetch(key, &block)
30
+ unless block_given?
31
+ raise ArgumentError, 'no block was given but one was expected'
32
+ end
33
+ value = storage_get(key)
34
+ return value if value
35
+ storage_put(key, block.call(key.to_s))
36
+ end
37
+
38
+ def [](key)
39
+ storage_get(key)
40
+ end
41
+
42
+ def []=(key, value)
43
+ unless key.is_a?(String) || key.is_a?(Symbol)
44
+ raise ArgumentError, "key must be String or Symbol, not: #{key.class}"
45
+ end
46
+ storage_put(key, value)
47
+ end
48
+
49
+ protected
50
+
51
+ def storage_get(key)
52
+ if tyrant
53
+ value = tyrant[key.to_s]
54
+ value ? YAML.load(value) : nil
55
+ else
56
+ STDERR.puts "[GCODER] Unable to get #{key.inspect} " \
57
+ 'because there is no Tyrant connection.'
58
+ nil
59
+ end
60
+ end
61
+
62
+ def storage_put(key, value)
63
+ if tyrant
64
+ tyrant[key.to_s.downcase] = YAML.dump(value)
65
+ value # <- We don't want to return YAML in this case.
66
+ else
67
+ STDERR.puts "[GCODER] Unable to put #{key.inspect} " \
68
+ 'because there is no Tyrant connection.'
69
+ value
70
+ end
71
+ end
72
+
73
+ def tyrant
74
+ @tyrant
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,15 @@
1
+ module GCoder
2
+ class Resolver < Persistence::DataStore
3
+
4
+ def resolve(query)
5
+ fetch(query) do |q|
6
+ GeocodingAPI::Request.get(q, @config)
7
+ end
8
+ end
9
+
10
+ def [](postal_code)
11
+ resolve(postal_code)
12
+ end
13
+
14
+ end
15
+ end
data/lib/gcoder.rb ADDED
@@ -0,0 +1,42 @@
1
+ require 'rubygems'
2
+ require 'rufus/tokyo/tyrant'
3
+ require 'json'
4
+ require 'yaml'
5
+ require 'open-uri'
6
+ require 'timeout'
7
+
8
+ $:.unshift(File.dirname(__FILE__))
9
+
10
+ require 'gcoder/config'
11
+ require 'gcoder/geocoding_api'
12
+ require 'gcoder/persistence'
13
+ require 'gcoder/resolver'
14
+
15
+
16
+ module GCoder
17
+
18
+ module Errors
19
+ class Error < StandardError; end
20
+ class MalformedQueryError < Error; end
21
+ class BlankRequestError < Error; end
22
+ class RequestTimeoutError < Error; end
23
+ class NoAPIKeyError < Error; end
24
+ class APIMalformedRequestError < Error; end
25
+ class APIGeocodingError < Error; end
26
+ class TTUnableToConnectError < Error; end
27
+ class InvalidStorageValueError < Error; end
28
+ class UnknownFormatSymbolError < Error; end
29
+ end
30
+
31
+
32
+ module ProxyMethods
33
+ def GCoder.config=(hsh)
34
+ Config.update(hsh)
35
+ end
36
+
37
+ def GCoder.connect(options = {})
38
+ Resolver.new(options)
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,37 @@
1
+ require 'test_helper'
2
+
3
+ class ConfigTest < Test::Unit::TestCase
4
+
5
+ context 'Config#merge' do
6
+ setup do
7
+ @config = GCoder::Config.merge(:gmaps_api_timeout => 3)
8
+ end
9
+
10
+ should 'return a hash of updated config settings' do
11
+ assert_instance_of Hash, @config
12
+ assert_equal 6, @config.size
13
+ assert_equal 3, @config[:gmaps_api_timeout]
14
+ end
15
+
16
+ should 'not change default configuration' do
17
+ assert_equal 2, GCoder::Config[:gmaps_api_timeout]
18
+ end
19
+ end
20
+
21
+ context 'Config#update' do
22
+ setup do
23
+ @config = GCoder::Config.update(:gmaps_api_timeout => 1)
24
+ end
25
+
26
+ should 'return a hash of updated config settings' do
27
+ assert_instance_of Hash, @config
28
+ assert_equal 6, @config.size
29
+ assert_equal 1, @config[:gmaps_api_timeout]
30
+ end
31
+
32
+ should 'change default configuration' do
33
+ assert_equal 1, GCoder::Config[:gmaps_api_timeout]
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,26 @@
1
+ require 'test_helper'
2
+
3
+ class GCoderTest < Test::Unit::TestCase
4
+
5
+ context 'ProxyMethods' do
6
+ should 'be present in GCoder module' do
7
+ assert_respond_to GCoder, :config=
8
+ assert_respond_to GCoder, :connect
9
+ end
10
+
11
+ context 'GCoder.config=' do
12
+ should 'proxy to Config.update' do
13
+ GCoder::Config.expects(:update).with({})
14
+ assert GCoder.config = {}
15
+ end
16
+ end
17
+
18
+ context 'GCoder.connect' do
19
+ should 'proxy to Resolver.new' do
20
+ GCoder::Resolver.expects(:new).with({}).returns(:it_works)
21
+ assert_equal :it_works, GCoder.connect
22
+ end
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,74 @@
1
+ require 'test_helper'
2
+
3
+
4
+ class GeocodingAPITest < Test::Unit::TestCase
5
+
6
+ context 'initialize with incorrect arguments' do
7
+ should 'fail with no arguments' do
8
+ assert_raise ArgumentError do
9
+ GCoder::GeocodingAPI::Request.new
10
+ end
11
+ end
12
+
13
+ should 'fail with any argument other than a string' do
14
+ assert_raise GCoder::Errors::BlankRequestError do
15
+ GCoder::GeocodingAPI::Request.new(nil)
16
+ end
17
+ assert_raise GCoder::Errors::MalformedQueryError do
18
+ GCoder::GeocodingAPI::Request.new(0)
19
+ end
20
+ end
21
+
22
+ should 'fail when passed a blank string as an argument' do
23
+ assert_raise GCoder::Errors::BlankRequestError do
24
+ GCoder::GeocodingAPI::Request.new(' ')
25
+ end
26
+ end
27
+ end
28
+
29
+ context 'initialize with no API key present' do
30
+ should 'fall down, go boom' do
31
+ assert_raise GCoder::Errors::NoAPIKeyError do
32
+ GCoder::GeocodingAPI::Request.new('M6R2G5')
33
+ end
34
+ end
35
+ end
36
+
37
+ context 'query with correct arguments' do
38
+ setup do
39
+ @zip = GCoder::GeocodingAPI::Request.new('M6R2G5',
40
+ :gmaps_api_key => 'apikey')
41
+ end
42
+
43
+ should 'return parsed and tidied JSON' do
44
+ @zip.expects(:http_get).returns(PAYLOADS[:json_m6r2g5])
45
+ response = @zip.get.to_h
46
+ assert_equal 5, response[:accuracy]
47
+ assert_equal 'Canada', response[:country][:name]
48
+ assert_equal 'CA', response[:country][:code]
49
+ assert_equal 'ON', response[:country][:administrative_area]
50
+ assert_equal 43.6504650, response[:point][:latitude]
51
+ assert_equal -79.4449720, response[:point][:longitude]
52
+ assert_equal 43.6536126, response[:box][:north]
53
+ assert_equal 43.6473174, response[:box][:south]
54
+ assert_equal -79.4418244, response[:box][:east]
55
+ assert_equal -79.4481196, response[:box][:west]
56
+ end
57
+
58
+ should 'raise an error when API returns malformed request' do
59
+ @zip.expects(:http_get).returns(PAYLOADS[:json_400])
60
+ assert_raise GCoder::Errors::APIMalformedRequestError do
61
+ @zip.get.validate!
62
+ end
63
+ end
64
+
65
+ should 'have an appropriate URI' do
66
+ assert_match /output=json/, @zip.uri
67
+ assert_match /q=M6R2G5/, @zip.uri
68
+ assert_match /oe=u/, @zip.uri
69
+ assert_match /sensor=false/, @zip.uri
70
+ assert_match /key=apikey/, @zip.uri
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,78 @@
1
+ require 'test_helper'
2
+
3
+ class PersistenceTest < Test::Unit::TestCase
4
+
5
+ context 'DataStore.new' do
6
+ should 'throw and argument error without :tt_host specified' do
7
+ assert_raise ArgumentError do
8
+ GCoder::Persistence::DataStore.new
9
+ end
10
+ end
11
+
12
+ should 'throw a better error if the Tokyo Tyrant connection fails' do
13
+ assert_raise GCoder::Errors::TTUnableToConnectError do
14
+ GCoder::Persistence::DataStore.new(:tt_host => '/tmp/fake')
15
+ end
16
+ end
17
+ end
18
+
19
+ context 'DataStore' do
20
+ setup do
21
+ Rufus::Tokyo::Tyrant.stubs(:new).returns({})
22
+ @db = GCoder::Persistence::DataStore.new(:tt_host => '/tmp/tttest')
23
+ end
24
+
25
+ context '#fetch' do
26
+ should 'raise an error without any block specified' do
27
+ assert_raise ArgumentError do
28
+ @db.fetch(:stuff)
29
+ end
30
+ end
31
+
32
+ should 'get a value that exists' do
33
+ @db[:test_key] = 'test_value'
34
+ assert_equal 'test_value', @db.fetch(:test_key) { raise 'ultrafail' }
35
+ end
36
+
37
+ should 'set the value to whatever the block yields if value is nil' do
38
+ @db.fetch(:test_key) { 'block_value' }
39
+ assert_equal 'block_value', @db[:test_key]
40
+ end
41
+ end
42
+
43
+ context '#[]=' do
44
+ should 'now allow numbers or nil as a key' do
45
+ assert_raise ArgumentError do
46
+ @db[nil] = 'extreme fail'
47
+ end
48
+ assert_raise ArgumentError do
49
+ @db[23] = 'massive fail'
50
+ end
51
+ end
52
+ end
53
+
54
+ context '#[]' do
55
+ should 'return nil for a key that does not exist' do
56
+ assert_nil @db['nope']
57
+ assert_nil @db['']
58
+ assert_nil @db[0]
59
+ assert_nil @db[nil]
60
+ end
61
+
62
+ should 'alow retrieval of nil' do
63
+ @db[:nil_key] = nil
64
+ assert_nil @db[:nil_key]
65
+ end
66
+
67
+ should 'return parsed JSON for keys that do exist' do
68
+ @db[:payload_1] = PAYLOADS[:test_string]
69
+ @db[:payload_2] = PAYLOADS[:test_hash]
70
+ @db[:payload_3] = PAYLOADS[:test_array]
71
+ assert_equal PAYLOADS[:test_string], @db[:payload_1]
72
+ assert_equal PAYLOADS[:test_hash], @db[:payload_2]
73
+ assert_equal PAYLOADS[:test_array], @db[:payload_3]
74
+ end
75
+ end
76
+ end
77
+
78
+ end
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+
3
+ class ResolverTest < Test::Unit::TestCase
4
+
5
+ context 'Resolver' do
6
+ setup do
7
+ Rufus::Tokyo::Tyrant.stubs(:new).returns({})
8
+ @db = GCoder::Resolver.new(:tt_host => '/tmp/tttest', :gmaps_api_key => 'testkey')
9
+ end
10
+
11
+ should 'return a hash of information for a new address' do
12
+ GCoder::GeocodingAPI::Request.any_instance.
13
+ expects(:http_get).returns(PAYLOADS[:json_m6r2g5])
14
+ assert_instance_of Hash, @db.resolve('m6r2g5')
15
+ end
16
+
17
+ should 'not call api when a cached postal code is called' do
18
+ assert_instance_of Hash, @db.resolve('m6r2g5')
19
+ end
20
+
21
+ should 'store the postal code key in the correct format' do
22
+ assert_instance_of Hash, @db.resolve('M6R2G5')
23
+ end
24
+
25
+ should 'allow [] to resolve with auto-instantiation' do
26
+ assert_instance_of Hash, @db['M6R2G5']
27
+ end
28
+
29
+ should 'raise malformed postal code error for a nil postal code' do
30
+ assert_raise GCoder::Errors::BlankRequestError do
31
+ @db.resolve(nil)
32
+ end
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'mocha'
5
+
6
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ $:.unshift(File.dirname(__FILE__))
8
+ require 'gcoder'
9
+
10
+ class Test::Unit::TestCase
11
+
12
+ PAYLOADS = {
13
+ :json_m6r2g5 => %q<
14
+ {
15
+ "name": "M6R2G5",
16
+ "Status": {
17
+ "code": 200,
18
+ "request": "geocode"
19
+ },
20
+ "Placemark": [ {
21
+ "id": "p1",
22
+ "address": "Ontario M6R 2G5, Canada",
23
+ "AddressDetails": {"Country": {"CountryNameCode": "CA","CountryName": "Canada","AdministrativeArea": {"AdministrativeAreaName": "ON","PostalCode": {"PostalCodeNumber": "M6R 2G5"}}},"Accuracy": 5},
24
+ "ExtendedData": {
25
+ "LatLonBox": {
26
+ "north": 43.6536126,
27
+ "south": 43.6473174,
28
+ "east": -79.4418244,
29
+ "west": -79.4481196
30
+ }
31
+ },
32
+ "Point": {
33
+ "coordinates": [ -79.4449720, 43.6504650, 0 ]
34
+ }
35
+ } ]
36
+ }>,
37
+ :json_602 => %q<
38
+ {
39
+ "name": "crashbangboom",
40
+ "Status": {
41
+ "code": 602,
42
+ "request": "geocode"
43
+ }
44
+ }>,
45
+ :json_400 => %q<
46
+ {
47
+ "name": "",
48
+ "Status": {
49
+ "code": 400,
50
+ "request": "geocode"
51
+ }
52
+ }>,
53
+ :test_string => "test\nstring",
54
+ :test_hash => { 'test' => 'value', 100 => 'one hundred' },
55
+ :test_array => ['test', 1, 3.1415, true, false, nil]
56
+ }
57
+
58
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: heycarsten-gcoder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Carsten Nielsen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-26 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: heycarsten@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - .document
26
+ - .gitignore
27
+ - README.rdoc
28
+ - Rakefile
29
+ - VERSION.yml
30
+ - gcoder.gemspec
31
+ - lib/gcoder.rb
32
+ - lib/gcoder/config.rb
33
+ - lib/gcoder/geocoding_api.rb
34
+ - lib/gcoder/persistence.rb
35
+ - lib/gcoder/resolver.rb
36
+ - test/config_test.rb
37
+ - test/gcoder_test.rb
38
+ - test/geocoding_api_test.rb
39
+ - test/persistence_test.rb
40
+ - test/resolver_test.rb
41
+ - test/test_helper.rb
42
+ has_rdoc: true
43
+ homepage: http://github.com/heycarsten/gcoder
44
+ post_install_message:
45
+ rdoc_options:
46
+ - --charset=UTF-8
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.2.0
65
+ signing_key:
66
+ specification_version: 2
67
+ summary: A library for geocoding postal codes via the Google Maps Geocoding API with a persisted cache through Tokyo Tyrant
68
+ test_files:
69
+ - test/config_test.rb
70
+ - test/gcoder_test.rb
71
+ - test/geocoding_api_test.rb
72
+ - test/persistence_test.rb
73
+ - test/resolver_test.rb
74
+ - test/test_helper.rb