geocodio-ocd 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
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