address_geocoder 0.0.1

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.
@@ -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: