address_geocoder 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,70 @@
1
+ require 'address_geocoder/requester'
2
+ require 'maps_api/google/url_generator'
3
+ require 'maps_api/google/parser'
4
+
5
+ module MapsApi
6
+ module Google
7
+ # Class for making requests to Google Maps API
8
+ class Requester < ::AddressGeocoder::Requester
9
+ # Make a call to Google Maps' Geocoding API
10
+ # @return (see AddressGeocoder::Requester#make_call)
11
+ def make_call
12
+ @url_generator = UrlGenerator.new(address: @address.dup,
13
+ api_key: @api_key,
14
+ language: @language)
15
+ @url_generator.levels.each do |level_of_search|
16
+ @url_generator.level = level_of_search
17
+ call
18
+ break if success?
19
+ end
20
+ end
21
+
22
+ # Determines whether the request to Google Maps' Geocoding API was a
23
+ # success
24
+ # @return (see AddressGeocoder::Requester#success?)
25
+ def success?
26
+ return false unless @result['status'] == 'OK'
27
+ return false unless @result['results'][0]['address_components'].length > 1
28
+ true
29
+ end
30
+
31
+ # Check if the certainty level of the response
32
+ # @note certainty is determined in two ways: first, by ensuring that the
33
+ # country was not the only field returned and that it was the correct
34
+ # country; second, that the city, state, and postal code were all
35
+ # present in the response if they were included in the level of call.
36
+ def certain?
37
+ level = @url_generator.level
38
+ if @parser.just_country?(@result) ||
39
+ @parser.not_correct_country?(@result)
40
+ false
41
+ elsif @parser.city_present?(level) || @parser.state_present?(level) ||
42
+ @parser.pc_present?(level)
43
+ false
44
+ else
45
+ true
46
+ end
47
+ end
48
+
49
+ # Return a compacted, flattened array of different address responses.
50
+ # @return (see AddressGeocoder::Requester#array_result)
51
+ def array_result
52
+ [@result['results']].flatten
53
+ end
54
+
55
+ private
56
+
57
+ def call
58
+ attempts = 0
59
+ begin
60
+ @result = HTTParty.get(@url_generator.generate_url)
61
+ rescue
62
+ sleep(0.5)
63
+ attempts += 1
64
+ retry if attempts <= 5
65
+ connection_error('Could not connect to GoogleAPI')
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,94 @@
1
+ require 'address_geocoder/url_generator'
2
+
3
+ module MapsApi
4
+ module Google
5
+ # Class for generatoring URLs to call Google Maps API
6
+ class UrlGenerator < ::AddressGeocoder::UrlGenerator
7
+ # Google's attribute names for our address variables
8
+ GOOGLE_TITLES = {
9
+ country: 'country',
10
+ postal_code: 'postal_code',
11
+ city: 'locality',
12
+ state: 'administrative_area'
13
+ }.freeze
14
+ # The URL of Google Maps' Geocoding API
15
+ URL = 'https://maps.googleapis.com/maps/api/geocode/json?'.freeze
16
+ # Google accepted language codes
17
+ # @see https://developers.google.com/maps/faq#languagesupport
18
+ LANGUAGES = ['zh-CN', 'ja', 'es', 'ko', 'ru', 'de', 'fr'].freeze
19
+ # The call levels to cycle through
20
+ CYCLE = { base: [1, 5], no_street: [2, 6], no_city: [3, 7],
21
+ no_state: [4] }.freeze
22
+
23
+ # @!attribute level
24
+ # @return [Integer] the level at which to generate the URL
25
+ attr_accessor :level
26
+
27
+ def initialize(args = {})
28
+ @level = args[:level]
29
+ super args
30
+ end
31
+
32
+ # Generates a URL with which to call Google Maps' Geocoding API
33
+ # @return (see AddressGeocoder::UrlGenerator#generate_url)
34
+ def generate_url
35
+ params = prune_address.map { |key, value| add(key, value) }
36
+ params = params.join.tr('\=', ':').chop
37
+
38
+ if ([1, 5] & [@level]).any?
39
+ street = hash_to_query('address' => @street) + '&'
40
+ end
41
+
42
+ params << "&key=#{@api_key}" unless @api_key.empty?
43
+
44
+ language = "&language=#{@language}" if LANGUAGES.include? @language
45
+
46
+ "#{URL}#{street}components=#{params}#{language}"
47
+ end
48
+
49
+ # Generates layers of calls to make, starting with a base layer that calls
50
+ # all valid fields, and removing a layer each call
51
+ # @return [Array<Integer>] a list of calls to determine what values are
52
+ # used in the call to Google Maps' API
53
+ def levels
54
+ levels = []
55
+ # Assign base levels unless no street
56
+ levels += CYCLE[:base] if @address[:street]
57
+ # Assign levels that don't use street if valid city
58
+ levels += CYCLE[:no_street] if @address[:city]
59
+ # Assign levels that don't use street,city if valid state
60
+ levels += CYCLE[:no_city] if @address[:state]
61
+ if @address[:postal_code]
62
+ # Assign the level that doesn't use street,city,state
63
+ levels += CYCLE[:no_state]
64
+ else
65
+ # Remove all levels that included postal code
66
+ levels -= [5, 6, 7]
67
+ end
68
+ levels.sort
69
+ end
70
+
71
+ private
72
+
73
+ # Removes attributes from the address that don't fit with the level
74
+ # @return [Hash] an address object to add to the call
75
+ def prune_address
76
+ address = (@address.select { |_k, v| v }).to_h
77
+ address[:country] = address[:country][:alpha2]
78
+ @street = address.delete(:street)
79
+
80
+ address.delete(:postal_code) if @level > 4
81
+ address.delete(:city) if ([3, 4, 7] & [@level]).any?
82
+ address.delete(:state) if @level == 4
83
+ address
84
+ end
85
+
86
+ # Parses a key and value from a hash into a query
87
+ # @return [String] a query to be used in the URL
88
+ def add(key, value)
89
+ str = hash_to_query(GOOGLE_TITLES[key] => value)
90
+ "#{str}|"
91
+ end
92
+ end
93
+ end
94
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: address_geocoder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Carson Long
8
+ - Wing Leung Choi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2016-05-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.13.7
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.13.7
28
+ description: Calls and parses various maps APIs for accurate address validation and
29
+ geocoding
30
+ email:
31
+ - ctlong.970@gmail.com
32
+ - wingleungchoi@gmail.com
33
+ executables:
34
+ - console
35
+ - setup
36
+ extensions: []
37
+ extra_rdoc_files: []
38
+ files:
39
+ - ".gitignore"
40
+ - ".travis.yml"
41
+ - CODE_OF_CONDUCT.md
42
+ - Gemfile
43
+ - LICENSE
44
+ - README.rdoc
45
+ - Rakefile
46
+ - address_geocoder.gemspec
47
+ - bin/console
48
+ - bin/setup
49
+ - countries.yaml
50
+ - lib/address_geocoder.rb
51
+ - lib/address_geocoder/client.rb
52
+ - lib/address_geocoder/error.rb
53
+ - lib/address_geocoder/parser.rb
54
+ - lib/address_geocoder/requester.rb
55
+ - lib/address_geocoder/url_generator.rb
56
+ - lib/address_geocoder/version.rb
57
+ - lib/maps_api.rb
58
+ - lib/maps_api/google.rb
59
+ - lib/maps_api/google/client.rb
60
+ - lib/maps_api/google/parser.rb
61
+ - lib/maps_api/google/requester.rb
62
+ - lib/maps_api/google/url_generator.rb
63
+ homepage: https://github.com/ctlong/address_geocoder
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message: Happy mapping!
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.4.5.1
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Address validation and geocoding
87
+ test_files: []
88
+ has_rdoc: