geocodio-ocd 1.7.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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/lib/geocodio/address.rb +114 -0
  3. data/lib/geocodio/address_set.rb +48 -0
  4. data/lib/geocodio/canadian/electoral_district_base.rb +21 -0
  5. data/lib/geocodio/canadian/electoral_districts.rb +23 -0
  6. data/lib/geocodio/canadian/federal_electoral_district.rb +12 -0
  7. data/lib/geocodio/canadian/provincial_electoral_district.rb +6 -0
  8. data/lib/geocodio/canadian.rb +14 -0
  9. data/lib/geocodio/client/error.rb +15 -0
  10. data/lib/geocodio/client/response.rb +13 -0
  11. data/lib/geocodio/client.rb +155 -0
  12. data/lib/geocodio/congressional_district.rb +30 -0
  13. data/lib/geocodio/legislator.rb +69 -0
  14. data/lib/geocodio/school_district.rb +15 -0
  15. data/lib/geocodio/state_legislative_district.rb +16 -0
  16. data/lib/geocodio/timezone.rb +16 -0
  17. data/lib/geocodio/utils.rb +43 -0
  18. data/lib/geocodio/version.rb +13 -0
  19. data/lib/geocodio.rb +9 -0
  20. data/spec/address_set_spec.rb +31 -0
  21. data/spec/address_spec.rb +235 -0
  22. data/spec/canadian_spec.rb +77 -0
  23. data/spec/client_spec.rb +196 -0
  24. data/spec/congressional_district_spec.rb +45 -0
  25. data/spec/legislator_spec.rb +116 -0
  26. data/spec/school_district_spec.rb +27 -0
  27. data/spec/spec_helper.rb +25 -0
  28. data/spec/state_legislative_district_spec.rb +43 -0
  29. data/spec/timezone_spec.rb +23 -0
  30. data/spec/vcr_cassettes/alaska_geocode_with_fields.yml +68 -0
  31. data/spec/vcr_cassettes/batch_geocode.yml +83 -0
  32. data/spec/vcr_cassettes/batch_geocode_with_bad_address.yml +61 -0
  33. data/spec/vcr_cassettes/batch_geocode_with_fields.yml +121 -0
  34. data/spec/vcr_cassettes/batch_reverse.yml +181 -0
  35. data/spec/vcr_cassettes/batch_reverse_with_fields.yml +384 -0
  36. data/spec/vcr_cassettes/canadian.yml +101 -0
  37. data/spec/vcr_cassettes/geocode.yml +61 -0
  38. data/spec/vcr_cassettes/geocode_bad_address.yml +53 -0
  39. data/spec/vcr_cassettes/geocode_with_fields.yml +71 -0
  40. data/spec/vcr_cassettes/geocode_with_fields_legacy.yml +91 -0
  41. data/spec/vcr_cassettes/geocode_with_postdirectional.yml +61 -0
  42. data/spec/vcr_cassettes/invalid_key.yml +59 -0
  43. data/spec/vcr_cassettes/parse.yml +51 -0
  44. data/spec/vcr_cassettes/reverse.yml +97 -0
  45. data/spec/vcr_cassettes/reverse_with_fields.yml +163 -0
  46. data/spec/vcr_cassettes/reverse_with_fields_no_house_info.yml +138 -0
  47. metadata +189 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c9199f7869e69da03015cf1880392b7850d05485fcb121e3ce019bdf7a21add9
4
+ data.tar.gz: dd5b48ccd480bf03f505a9fe49553966c9f1fe09f2ff443db50f27eef3db1e3c
5
+ SHA512:
6
+ metadata.gz: c411d98acf8ffd2a498fcbec82670f5995285b638db230027eace6ebec0403e769d5f16f93834b31a9f82afcad63ab36170fb65282f8bbb6453a232ecc502807
7
+ data.tar.gz: d165d5e4169ff699f4c1ac099ac17cdca76580a307034224eb1e1698563e3876f64394d437b3a0599848f86b7b341e72ff69b85a6e61c9df270140b49e4153c1
@@ -0,0 +1,114 @@
1
+ require 'geocodio/congressional_district'
2
+ require 'geocodio/school_district'
3
+ require 'geocodio/state_legislative_district'
4
+ require 'geocodio/timezone'
5
+ require 'geocodio/canadian/electoral_districts'
6
+
7
+ module Geocodio
8
+ class Address
9
+ include Geocodio::Canadian
10
+ attr_reader :number, :predirectional, :street, :suffix, :postdirectional,
11
+ :formatted_street, :city, :state, :zip, :county
12
+
13
+ attr_reader :latitude, :longitude
14
+ alias :lat :latitude
15
+ alias :lng :longitude
16
+
17
+ attr_reader :congressional_districts, :house_districts, :senate_districts,
18
+ :unified_school_district, :elementary_school_district,
19
+ :secondary_school_district
20
+
21
+ attr_reader :timezone
22
+
23
+ # How accurate geocod.io deemed this result to be given the original query.
24
+ #
25
+ # @return [Float] a number between 0 and 1
26
+ attr_reader :accuracy, :accuracy_type, :source
27
+
28
+ def initialize(payload = {})
29
+ set_attributes(payload['address_components']) if payload['address_components']
30
+ set_coordinates(payload['location']) if payload['location']
31
+ set_additional_fields(payload['fields']) if payload['fields']
32
+
33
+ @source = payload['source']
34
+ @accuracy = payload['accuracy']
35
+ @accuracy_type = payload['accuracy_type']
36
+ @formatted_address = payload['formatted_address']
37
+ end
38
+
39
+ # Formats the address in the standard way.
40
+ #
41
+ # @return [String] a formatted address
42
+ def to_s
43
+ @formatted_address
44
+ end
45
+
46
+ private
47
+
48
+ def set_attributes(attributes)
49
+ @number = attributes['number']
50
+ @predirectional = attributes['predirectional']
51
+ @street = attributes['street']
52
+ @suffix = attributes['suffix']
53
+ @postdirectional = attributes['postdirectional']
54
+ @formatted_street = attributes['formatted_street']
55
+ @city = attributes['city']
56
+ @state = attributes['state']
57
+ @zip = attributes['zip']
58
+ @county = attributes['county']
59
+ end
60
+
61
+ def set_coordinates(coordinates)
62
+ @latitude = coordinates['lat']
63
+ @longitude = coordinates['lng']
64
+ end
65
+
66
+ def set_additional_fields(fields)
67
+ set_congressional_districts(fields['congressional_districts']) if fields['congressional_districts']
68
+ set_legislative_districts(fields['state_legislative_districts']) if fields['state_legislative_districts']
69
+ set_school_districts(fields['school_districts']) if fields['school_districts']
70
+ set_timezone(fields['timezone']) if fields['timezone']
71
+
72
+ riding, provincial_riding = fields.values_at('riding', 'provincial_riding')
73
+ if riding || provincial_riding
74
+ set_canadian_fields(riding, provincial_riding)
75
+ end
76
+ end
77
+
78
+ def set_congressional_districts(districts)
79
+ return if districts.empty?
80
+
81
+ @congressional_districts = districts.map { |district| CongressionalDistrict.new(district) }
82
+ end
83
+
84
+ def set_legislative_districts(districts)
85
+ return if districts.empty?
86
+ @house_districts, @senate_districts = districts.values_at('house', 'senate').map do |district_entries|
87
+ [*district_entries].map{|entry| StateLegislativeDistrict.new(entry)}
88
+ end
89
+ end
90
+
91
+ def set_school_districts(schools)
92
+ return if schools.empty?
93
+
94
+ if schools['unified']
95
+ @unified_school_district = SchoolDistrict.new(schools['unified'])
96
+ else
97
+ @elementary_school_district = SchoolDistrict.new(schools['elementary']) if schools['elementary']
98
+ @secondary_school_district = SchoolDistrict.new(schools['secondary']) if schools['secondary']
99
+ end
100
+ end
101
+
102
+ def set_timezone(timezone)
103
+ return if timezone.empty?
104
+
105
+ @timezone = Timezone.new(timezone)
106
+ end
107
+
108
+ def <=>(address)
109
+ return -1 if self.accuracy < address.accuracy
110
+ return 0 if self.accuracy == address.accuracy
111
+ return 1 if self.accuracy > address.accuracy
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,48 @@
1
+ # AddressSet is a collection of Geocodio::Address objects that get returned from
2
+ # a query to geocod.io. Because one query can return multiple results, each with
3
+ # an accuracy, a collection to manage them is needed. Most of the time, the user
4
+ # should only need to use the #best method.
5
+ module Geocodio
6
+ class AddressSet
7
+ include Enumerable
8
+
9
+ # Returns the query that retrieved this result set.
10
+ #
11
+ # @return [String] the original query
12
+ attr_reader :query
13
+
14
+ def initialize(query, *addresses, input: nil)
15
+ @query = query
16
+ @addresses = addresses
17
+ @formatted_input = input
18
+ end
19
+
20
+ def each(&block)
21
+ @addresses.each(&block)
22
+ end
23
+
24
+ # Returns the result that geocod.io deemed the most accurate for the query.
25
+ #
26
+ # @return [Geocodio::Address] the most accurate address
27
+ def best
28
+ best = @addresses.find { |address| address.to_s == @formatted_input }
29
+ return best if best
30
+
31
+ max_by(&:accuracy)
32
+ end
33
+
34
+ # Returns the number of addresses contained in this result set.
35
+ #
36
+ # @return [Integer] the number of addresses
37
+ def size
38
+ @addresses.size
39
+ end
40
+
41
+ # Returns whether or not there are any addresses in this result set.
42
+ #
43
+ # @return [Boolean] if there were any results returned by Geocodio
44
+ def empty?
45
+ @addresses.empty?
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ module Geocodio
2
+ module Canadian
3
+ class ElectoralDistrictBase
4
+ attr_accessor :ocd_id
5
+ attr_accessor :name_french
6
+ attr_accessor :name_english
7
+ attr_accessor :source
8
+
9
+ def initialize(payload = {})
10
+ @ocd_id = payload['ocd_id']
11
+ @name_english = payload['name_english']
12
+ @name_french = payload['name_french']
13
+ @source = payload['source']
14
+ end
15
+
16
+ def name
17
+ @name_english
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ require 'geocodio/canadian/electoral_district_base'
2
+ require 'geocodio/canadian/federal_electoral_district'
3
+ require 'geocodio/canadian/provincial_electoral_district'
4
+
5
+ module Geocodio
6
+ module Canadian
7
+ class ElectoralDistricts
8
+ attr_accessor :federal_electoral_district
9
+ attr_accessor :provincial_electoral_district
10
+
11
+ def initialize(riding, provincial_riding)
12
+
13
+ if riding && riding.is_a?(Hash) && riding.size > 0
14
+ @federal_electoral_district = FederalElectoralDistrict.new(riding)
15
+ end
16
+
17
+ if provincial_riding && provincial_riding.is_a?(Hash) && provincial_riding.size > 0
18
+ @provincial_electoral_district = ProvincialElectoralDistrict.new(provincial_riding)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ module Geocodio
2
+ module Canadian
3
+ class FederalElectoralDistrict < ElectoralDistrictBase
4
+ attr_accessor :code
5
+
6
+ def initialize(payload = {})
7
+ super
8
+ @code = payload['code']
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ module Geocodio
2
+ module Canadian
3
+ class ProvincialElectoralDistrict < ElectoralDistrictBase
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ module Geocodio
2
+ module Canadian
3
+ attr_reader :canadian
4
+
5
+ def set_canadian_fields(riding, provincial_riding)
6
+ @canadian = ElectoralDistricts.new(riding, provincial_riding)
7
+ end
8
+
9
+ def canadian?
10
+ !!@canadian
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module Geocodio
2
+ class Client
3
+ class Error < RuntimeError
4
+ attr_reader :response
5
+
6
+ def initialize(response)
7
+ @response, @json = response, JSON.parse(response.body)
8
+ end
9
+
10
+ def body() @json end
11
+ def message() body['error'] end
12
+ alias :error :message
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ require 'json'
2
+
3
+ module Geocodio
4
+ class Client
5
+ class Response
6
+ attr_reader :body
7
+
8
+ def initialize(response)
9
+ @body = JSON.parse(response.body)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,155 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'cgi'
4
+
5
+ require 'geocodio/client/error'
6
+ require 'geocodio/client/response'
7
+ require 'geocodio/utils'
8
+
9
+ module Geocodio
10
+ class Client
11
+ include Geocodio::Utils
12
+
13
+ CONTENT_TYPE = 'application/json'
14
+ METHODS = {
15
+ :get => Net::HTTP::Get,
16
+ :post => Net::HTTP::Post,
17
+ :put => Net::HTTP::Put,
18
+ :delete => Net::HTTP::Delete
19
+ }
20
+ HOST = 'api.geocod.io'
21
+ BASE_PATH = '/v1.7'
22
+ PORT = 80
23
+
24
+ def initialize(api_key = ENV['GEOCODIO_API_KEY'])
25
+ @api_key = api_key
26
+ end
27
+
28
+ # Geocodes one or more addresses. If one address is specified, a GET request
29
+ # is submitted to http://api.geocod.io/v1/geocode. Multiple addresses will
30
+ # instead submit a POST request.
31
+ #
32
+ # @param [Array<String>] addresses one or more String addresses
33
+ # @param [Hash] options an options hash
34
+ # @option options [Array] :fields a list of option fields to request (possible: "cd" or "cd113", "stateleg", "school", "timezone")
35
+ # @return [Geocodio::Address, Array<Geocodio::AddressSet>] One or more Address Sets
36
+ def geocode(addresses, options = {})
37
+ if addresses.size < 1
38
+ raise ArgumentError, 'You must provide at least one address to geocode.'
39
+ elsif addresses.size == 1
40
+ geocode_single(addresses.first, options)
41
+ else
42
+ geocode_batch(addresses, options)
43
+ end
44
+ end
45
+
46
+ # Reverse geocodes one or more pairs of coordinates. Coordinate pairs may be
47
+ # specified either as a comma-separated "latitude,longitude" string, or as
48
+ # a Hash with :lat/:latitude and :lng/:longitude keys. If one pair of
49
+ # coordinates is specified, a GET request is submitted to
50
+ # http://api.geocod.io/v1/reverse. Multiple pairs of coordinates will
51
+ # instead submit a POST request.
52
+ #
53
+ # @param [Array<String>, Array<Hash>] coordinates one or more pairs of coordinates
54
+ # @param [Hash] options an options hash
55
+ # @option options [Array] :fields a list of option fields to request (possible: "cd" or "cd113", "stateleg", "school", "timezone")
56
+ # @return [Geocodio::Address, Array<Geocodio::AddressSet>] One or more Address Sets
57
+ def reverse_geocode(coordinates, options = {})
58
+ if coordinates.size < 1
59
+ raise ArgumentError, 'You must provide coordinates to reverse geocode.'
60
+ elsif coordinates.size == 1
61
+ reverse_geocode_single(coordinates.first, options)
62
+ else
63
+ reverse_geocode_batch(coordinates, options)
64
+ end
65
+ end
66
+ alias :reverse :reverse_geocode
67
+
68
+ # Sends a GET request to http://api.geocod.io/v1/parse to correctly dissect
69
+ # an address into individual parts. As this endpoint does not do any
70
+ # geocoding, parts missing from the passed address will be missing from the
71
+ # result.
72
+ #
73
+ # @param address [String] the full or partial address to parse
74
+ # @return [Geocodio::Address] a parsed and formatted Address
75
+ def parse(address, options = {})
76
+ params, options = normalize_params_and_options(options)
77
+ params[:q] = address
78
+
79
+ Address.new get('/parse', params, options).body
80
+ end
81
+
82
+ private
83
+
84
+ METHODS.each do |method, _|
85
+ define_method(method) do |path, params = {}, options = {}|
86
+ request method, path, options.merge(params: params)
87
+ end
88
+ end
89
+
90
+ def geocode_single(address, options = {})
91
+ params, options = normalize_params_and_options(options)
92
+ params[:q] = address
93
+
94
+ response = get '/geocode', params, options
95
+ addresses, input = parse_results(response)
96
+
97
+ AddressSet.new(address, *addresses, input: input)
98
+ end
99
+
100
+ def reverse_geocode_single(pair, options = {})
101
+ params, options = normalize_params_and_options(options)
102
+ pair = normalize_coordinates(pair)
103
+ params[:q] = pair
104
+
105
+ response = get '/reverse', params, options
106
+ addresses, input = parse_results(response)
107
+
108
+ AddressSet.new(pair, *addresses, input: input)
109
+ end
110
+
111
+ def geocode_batch(addresses, options = {})
112
+ params, options = normalize_params_and_options(options)
113
+ options[:body] = addresses
114
+
115
+ response = post '/geocode', params, options
116
+
117
+ parse_nested_results(response)
118
+ end
119
+
120
+ def reverse_geocode_batch(pairs, options = {})
121
+ params, options = normalize_params_and_options(options)
122
+ options[:body] = pairs.map { |pair| normalize_coordinates(pair) }
123
+
124
+ response = post '/reverse', params, options
125
+
126
+ parse_nested_results(response)
127
+ end
128
+
129
+ def request(method, path, options)
130
+ path += "?api_key=#{@api_key}"
131
+
132
+ if params = options[:params] and !params.empty?
133
+ q = params.map { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }
134
+ path += "&#{q.join('&')}"
135
+ end
136
+
137
+ req = METHODS[method].new(BASE_PATH + path, 'Accept' => CONTENT_TYPE)
138
+
139
+ if options.key?(:body)
140
+ req['Content-Type'] = CONTENT_TYPE
141
+ req.body = options[:body] ? JSON.dump(options[:body]) : ''
142
+ end
143
+ http = Net::HTTP.new HOST, PORT
144
+ http.read_timeout = options[:timeout] if options[:timeout]
145
+ res = http.start { http.request(req) }
146
+
147
+ case res
148
+ when Net::HTTPSuccess
149
+ return Response.new(res)
150
+ else
151
+ raise Error, res
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,30 @@
1
+ require 'geocodio/legislator'
2
+
3
+ module Geocodio
4
+ class CongressionalDistrict
5
+ attr_reader :name
6
+ attr_reader :district_number
7
+ attr_reader :congress_number
8
+ attr_reader :proportion
9
+ attr_reader :current_legislators
10
+ attr_accessor :ocd_id
11
+
12
+ def initialize(payload = {})
13
+ @name = payload['name']
14
+ @district_number = payload['district_number'].to_i
15
+ @congress_number = payload['congress_number'].to_i
16
+ @congress_years = payload['congress_years']
17
+ @proportion = payload['proportion'].to_i
18
+ @ocd_id = payload['ocd_id']
19
+
20
+ @current_legislators = [*payload['current_legislators']].map do |legislator|
21
+ Legislator.new(legislator)
22
+ end
23
+ end
24
+
25
+ def congress_years
26
+ first, last = @congress_years.split('-').map(&:to_i)
27
+ first..last
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,69 @@
1
+ module Geocodio
2
+ class Legislator
3
+ attr_reader :type
4
+ attr_reader :name
5
+ attr_reader :birthday
6
+ attr_reader :gender
7
+ attr_reader :party
8
+ attr_reader :url
9
+ attr_reader :address
10
+ attr_reader :phone
11
+ attr_reader :contact_form
12
+ attr_reader :rss_url
13
+ attr_reader :twitter
14
+ attr_reader :facebook
15
+ attr_reader :youtube
16
+ attr_reader :youtube_id
17
+ attr_reader :bioguide_id
18
+ attr_reader :thomas_id
19
+ attr_reader :opensecrets_id
20
+ attr_reader :lis_id
21
+ attr_reader :cspan_id
22
+ attr_reader :govtrack_id
23
+ attr_reader :votesmart_id
24
+ attr_reader :ballotpedia_id
25
+ attr_reader :washington_post_id
26
+ attr_reader :icpsr_id
27
+ attr_reader :wikipedia_id
28
+
29
+ def initialize(payload = {})
30
+ @type = payload['type']
31
+
32
+ if payload['bio']
33
+ @name = "#{payload['bio']['first_name']} #{payload['bio']['last_name']}"
34
+ @birthday = Date.new(*payload['bio']['birthday'].split('-').map(&:to_i))
35
+ @gender = payload['bio']['gender']
36
+ @party = payload['bio']['party']
37
+ end
38
+
39
+ if payload['contact']
40
+ @url = payload['contact']['url']
41
+ @address = payload['contact']['address']
42
+ @phone = payload['contact']['phone']
43
+ @contact_form = payload['contact']['contact_form']
44
+ end
45
+
46
+ if payload['social']
47
+ @rss_url = payload['social']['rss_url']
48
+ @twitter = payload['social']['twitter']
49
+ @facebook = payload['social']['facebook']
50
+ @youtube = payload['social']['youtube']
51
+ @youtube_id = payload['social']['youtube_id']
52
+ end
53
+
54
+ if payload['references']
55
+ @bioguide_id = payload['references']['bioguide_id']
56
+ @thomas_id = payload['references']['thomas_id']
57
+ @opensecrets_id = payload['references']['opensecrets_id']
58
+ @lis_id = payload['references']['lis_id']
59
+ @cspan_id = payload['references']['cspan_id']
60
+ @govtrack_id = payload['references']['govtrack_id']
61
+ @votesmart_id = payload['references']['votesmart_id']
62
+ @ballotpedia_id = payload['references']['ballotpedia_id']
63
+ @washington_post_id = payload['references']['washington_post_id']
64
+ @icpsr_id = payload['references']['icpsr_id']
65
+ @wikipedia_id = payload['references']['wikipedia_id']
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,15 @@
1
+ module Geocodio
2
+ class SchoolDistrict
3
+ attr_reader :name
4
+ attr_reader :lea_code
5
+ attr_reader :grade_low
6
+ attr_reader :grade_high
7
+
8
+ def initialize(payload = {})
9
+ @name = payload['name']
10
+ @lea_code = payload['lea_code']
11
+ @grade_low = payload['grade_low']
12
+ @grade_high = payload['grade_high']
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module Geocodio
2
+ class StateLegislativeDistrict
3
+ attr_accessor :name
4
+ attr_accessor :district_number
5
+ attr_accessor :proportion
6
+ attr_accessor :ocd_id
7
+
8
+ def initialize(payload = {})
9
+ @name = payload['name']
10
+ @district_number = payload['district_number'].to_i
11
+ @district_number = payload['district_number'] if @district_number == 0
12
+ @proportion = payload['proportion']
13
+ @ocd_id = payload['ocd_id']
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Geocodio
2
+ class Timezone
3
+ attr_reader :name
4
+ attr_reader :utc_offset
5
+
6
+ def initialize(payload = {})
7
+ @name = payload['name']
8
+ @utc_offset = payload['utc_offset']
9
+ @observes_dst = payload['observes_dst']
10
+ end
11
+
12
+ def observes_dst?
13
+ !!@observes_dst
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,43 @@
1
+ module Geocodio
2
+ module Utils
3
+ def parse_results(response)
4
+ results = response.body['results']
5
+ input = response.body['input']['formatted_address'] if response.body['input']
6
+ [results.map { |result| Address.new(result) }, input]
7
+ end
8
+
9
+ def parse_nested_results(response)
10
+ results = response.body['results']
11
+
12
+ results = results.map do |result_set|
13
+ addresses = Array(result_set['response']['results'])
14
+ addresses.map! { |result| Address.new(result) }
15
+
16
+ query = result_set['query']
17
+ input = result_set['response']['input']['formatted_address'] if result_set['response']['input']
18
+
19
+ AddressSet.new(query, *addresses, input: input)
20
+ end
21
+ end
22
+
23
+ def normalize_coordinates(coordinates)
24
+ return coordinates unless coordinates.is_a?(Hash)
25
+ coordinates.sort.map { |p| p[1] }.join(',')
26
+ end
27
+
28
+ def normalize_params_and_options(hash)
29
+ hash = Hash[hash.map { |k, v| [k.to_sym, v] }]
30
+
31
+ # The only supported parameter is fields
32
+ params = hash.select { |k, _| [:fields].include?(k) }
33
+
34
+ # Normalize this particular parameter to be a comma-separated string
35
+ params[:fields] = params[:fields].join(',') if params[:fields]
36
+
37
+ # The only supported option is `timeout`
38
+ options = hash.select { |k, _| [:timeout].include?(k) }
39
+
40
+ [params, options]
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,13 @@
1
+ module Geocodio
2
+ class Version
3
+ MAJOR = 1
4
+ MINOR = 7
5
+ PATCH = 0
6
+
7
+ def self.to_s
8
+ [MAJOR, MINOR, PATCH].join('.')
9
+ end
10
+ end
11
+
12
+ VERSION = Version.to_s
13
+ end
data/lib/geocodio.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'geocodio/address'
2
+ require 'geocodio/address_set'
3
+ require 'geocodio/client'
4
+ require 'geocodio/canadian'
5
+
6
+ require 'geocodio/version'
7
+
8
+ module Geocodio
9
+ end