google_distance_matrix 0.4.0 → 0.5.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +16 -0
  3. data/.rubocop.yml +6 -0
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +1 -0
  6. data/CHANGELOG.md +20 -0
  7. data/Gemfile +2 -0
  8. data/Rakefile +9 -4
  9. data/google_distance_matrix.gemspec +20 -18
  10. data/lib/google_distance_matrix/client.rb +32 -18
  11. data/lib/google_distance_matrix/client_cache.rb +9 -3
  12. data/lib/google_distance_matrix/configuration.rb +37 -19
  13. data/lib/google_distance_matrix/errors.rb +6 -3
  14. data/lib/google_distance_matrix/log_subscriber.rb +14 -14
  15. data/lib/google_distance_matrix/logger.rb +6 -4
  16. data/lib/google_distance_matrix/matrix.rb +45 -22
  17. data/lib/google_distance_matrix/place.rb +32 -25
  18. data/lib/google_distance_matrix/places.rb +5 -4
  19. data/lib/google_distance_matrix/polyline_encoder/delta.rb +4 -2
  20. data/lib/google_distance_matrix/polyline_encoder/value_encoder.rb +11 -4
  21. data/lib/google_distance_matrix/polyline_encoder.rb +2 -2
  22. data/lib/google_distance_matrix/railtie.rb +4 -1
  23. data/lib/google_distance_matrix/route.rb +22 -15
  24. data/lib/google_distance_matrix/routes_finder.rb +25 -29
  25. data/lib/google_distance_matrix/url_builder/polyline_encoder_buffer.rb +3 -0
  26. data/lib/google_distance_matrix/url_builder.rb +44 -16
  27. data/lib/google_distance_matrix/version.rb +3 -1
  28. data/lib/google_distance_matrix.rb +25 -23
  29. data/spec/lib/google_distance_matrix/client_cache_spec.rb +26 -11
  30. data/spec/lib/google_distance_matrix/client_spec.rb +40 -30
  31. data/spec/lib/google_distance_matrix/configuration_spec.rb +36 -24
  32. data/spec/lib/google_distance_matrix/log_subscriber_spec.rb +13 -44
  33. data/spec/lib/google_distance_matrix/logger_spec.rb +16 -13
  34. data/spec/lib/google_distance_matrix/matrix_spec.rb +90 -57
  35. data/spec/lib/google_distance_matrix/place_spec.rb +30 -25
  36. data/spec/lib/google_distance_matrix/places_spec.rb +29 -28
  37. data/spec/lib/google_distance_matrix/polyline_encoder/delta_spec.rb +5 -3
  38. data/spec/lib/google_distance_matrix/polyline_encoder_spec.rb +7 -2
  39. data/spec/lib/google_distance_matrix/route_spec.rb +11 -9
  40. data/spec/lib/google_distance_matrix/routes_finder_spec.rb +95 -81
  41. data/spec/lib/google_distance_matrix/url_builder_spec.rb +97 -48
  42. data/spec/spec_helper.rb +3 -1
  43. metadata +35 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ca9d7d93abd6340a3cb5d36d03cbadfda5e4e1c
4
- data.tar.gz: 2cb7956404bd763af695eae8c10b81de5db48450
3
+ metadata.gz: e2a2232b164549acbd29af07935315c8859dc19c
4
+ data.tar.gz: 3f29dc7ef98d87e913c1372fabd660766150dc8b
5
5
  SHA512:
6
- metadata.gz: 2c5364924b1a055ce8c67c1508b219a1fc187ee9ecfa95d683775c21888f1076518bd1617b6a2e5be0020f5ef862e6707341bf8c71fff60e12e011d4dad719b9
7
- data.tar.gz: 59c211df2561ea00a1da7858051fb87de4a02e35642faba695852baf974e9e3d30d34c9b710e3078e05a2cf1ea1caefb6411f35a0604a90a6f01c37f9c9af715
6
+ metadata.gz: bead0dcdb680422cac5db6463af54580036dc6fae0f98373eec513e18a3a197ca7981d57db4ca64a27cc7461e675bd181b81a284b0cd98129e98fa916735be28
7
+ data.tar.gz: 0d6d9b8a96282568c647293adab6c58e9eed438e36d1e42f1f4b473a6da59c7cb5014eebc5cc56b4326211e8faa68e292651940a5f1e1d2c30b9b82ba5435710
data/.editorconfig ADDED
@@ -0,0 +1,16 @@
1
+ # EditorConfig helps developers define and maintain consistent
2
+ # coding styles between different editors and IDEs
3
+ # editorconfig.org
4
+
5
+ root = true
6
+
7
+ [*]
8
+ end_of_line = lf
9
+ charset = utf-8
10
+ trim_trailing_whitespace = true
11
+ insert_final_newline = true
12
+ indent_style = space
13
+ indent_size = 2
14
+
15
+ [*.{diff,md}]
16
+ trim_trailing_whitespace = false
data/.rubocop.yml ADDED
@@ -0,0 +1,6 @@
1
+ Metrics/LineLength:
2
+ Max: 100
3
+
4
+ Metrics/BlockLength:
5
+ Exclude:
6
+ - 'spec/**/*_spec.rb'
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.3.1
1
+ 2.3.4
data/.travis.yml CHANGED
@@ -7,6 +7,7 @@ rvm:
7
7
  - 2.2.3
8
8
  - 2.3.0
9
9
  - 2.3.1
10
+ - 2.3.4
10
11
  - ruby-head
11
12
  matrix:
12
13
  allow_failures:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## v.0.5.0
2
+
3
+ This release contains breaking change where `url` has been renamed to
4
+ `sensitive_url`. A `filtered_url` method is added to make it clear that
5
+ the URL returned is filtered according to configuration while the other one
6
+ will contain sensitive information like key and signature.
7
+
8
+ The cache key is changed so it no longer uses the URL, but a digest of the URL
9
+ as key. You may set `config.cache_key_transform` to a block passing given url
10
+ through if you don't want this.
11
+
12
+ * Fixed an issue where read/write to cache used url with sensitive data and
13
+ and filtered url resulting in cache miss.
14
+ * Instrumentation payload `url` renamed `sensitive_url`.
15
+ * Instrumentation payload added `filtered_url`.
16
+ * Cache key is a digest of the `sensitive_url` so we don't store in cache the
17
+ sensitive parts of the URL.
18
+ * Digesting cache key is configurable with `cache_key_transform`. It's a callable
19
+ object expected to take the url and transform it to the key you want.
20
+
1
21
  ## v.0.4.0
2
22
  * When mode is `driving` and `departure_time` is set all `route` objects will contain
3
23
  `duration_in_traffic_in_seconds` and `duration_in_traffic_text`.
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in google_distance_matrix.gemspec
data/Rakefile CHANGED
@@ -1,9 +1,14 @@
1
- require "bundler/gem_tasks"
1
+ # frozen_string_literal: true
2
2
 
3
- require "rspec/core/rake_task"
3
+ require 'bundler/gem_tasks'
4
+
5
+ require 'rubocop/rake_task'
6
+ RuboCop::RakeTask.new
7
+
8
+ require 'rspec/core/rake_task'
4
9
  RSpec::Core::RakeTask.new(:spec) do |t|
5
- t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
10
+ t.rspec_opts = ['-c', '-f progress', '-r ./spec/spec_helper.rb']
6
11
  t.pattern = 'spec/**/*_spec.rb'
7
12
  end
8
13
 
9
- task default: :spec
14
+ task default: %i[rubocop spec]
@@ -1,30 +1,32 @@
1
- # coding: utf-8
1
+ # frozen_String_literal: true
2
+
2
3
  lib = File.expand_path('../lib', __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'google_distance_matrix/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "google_distance_matrix"
8
+ spec.name = 'google_distance_matrix'
8
9
  spec.version = GoogleDistanceMatrix::VERSION
9
- spec.authors = ["Thorbjørn Hermansen"]
10
- spec.email = ["thhermansen@gmail.com"]
11
- spec.description = %q{Ruby client for The Google Distance Matrix API}
12
- spec.summary = %q{Ruby client for The Google Distance Matrix API}
13
- spec.homepage = ""
14
- spec.license = "MIT"
10
+ spec.authors = ['Thorbjørn Hermansen']
11
+ spec.email = ['thhermansen@gmail.com']
12
+ spec.description = %(Ruby client for The Google Distance Matrix API)
13
+ spec.summary = %(Ruby client for The Google Distance Matrix API)
14
+ spec.homepage = ''
15
+ spec.license = 'MIT'
15
16
 
16
- spec.files = `git ls-files`.split($/)
17
+ spec.files = `git ls-files`.split($RS)
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
20
21
 
21
- spec.add_dependency "activesupport", ">= 3.2.13"
22
- spec.add_dependency "activemodel", ">= 3.2.13"
23
- spec.add_dependency "google_business_api_url_signer", "~> 0.1.3"
22
+ spec.add_dependency 'activesupport', '>= 3.2.13', '<= 5'
23
+ spec.add_dependency 'activemodel', '>= 3.2.13', '<= 5'
24
+ spec.add_dependency 'google_business_api_url_signer', '~> 0.1.3'
24
25
 
25
- spec.add_development_dependency "bundler"
26
- spec.add_development_dependency "rspec", "~> 3.4.0"
27
- spec.add_development_dependency "shoulda-matchers", "~> 3.1.1"
28
- spec.add_development_dependency "webmock", "~> 2.0.2"
29
- spec.add_development_dependency "rake"
26
+ spec.add_development_dependency 'bundler'
27
+ spec.add_development_dependency 'rspec', '~> 3.5.0'
28
+ spec.add_development_dependency 'rubocop', '~> 0.48'
29
+ spec.add_development_dependency 'shoulda-matchers', '~> 3.1.1'
30
+ spec.add_development_dependency 'webmock', '~> 3.0.1'
31
+ spec.add_development_dependency 'rake'
30
32
  end
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GoogleDistanceMatrix
4
+ # HTTP client making request to Google's API
2
5
  class Client
3
6
  CLIENT_ERRORS = %w[
4
7
  INVALID_REQUEST
@@ -6,41 +9,52 @@ module GoogleDistanceMatrix
6
9
  OVER_QUERY_LIMIT
7
10
  REQUEST_DENIED
8
11
  UNKNOWN_ERROR
9
- ]
10
-
11
- def get(url, options = {})
12
+ ].freeze
13
+
14
+ # Make a GET request to given URL
15
+ #
16
+ # @param url The URL to Google's API we'll make a request to
17
+ # @param instrumentation A hash with instrumentation payload
18
+ # @param options Other options we don't care about, for example we'll capture
19
+ # `configuration` option which we are not using, but the ClientCache
20
+ # is using.
21
+ #
22
+ # @return Hash with data from parsed response body
23
+ def get(url, instrumentation: {}, **_options)
12
24
  uri = URI.parse url
13
- instrumentation = {url: url}.merge(options[:instrumentation] || {})
14
25
 
15
- response = ActiveSupport::Notifications.instrument "client_request_matrix_data.google_distance_matrix", instrumentation do
26
+ response = ActiveSupport::Notifications.instrument(
27
+ 'client_request_matrix_data.google_distance_matrix', instrumentation
28
+ ) do
16
29
  Net::HTTP.get_response uri
17
30
  end
18
31
 
32
+ handle response, url
33
+ rescue Timeout::Error => error
34
+ raise ServerError, error
35
+ end
36
+
37
+ private
38
+
39
+ def handle(response, url) # rubocop:disable Metrics/MethodLength
19
40
  case response
20
41
  when Net::HTTPSuccess
21
42
  inspect_for_client_errors! response
22
43
  when Net::HTTPRequestURITooLong
23
- fail MatrixUrlTooLong.new url, UrlBuilder::MAX_URL_SIZE, response
44
+ raise MatrixUrlTooLong.new url, UrlBuilder::MAX_URL_SIZE, response
24
45
  when Net::HTTPClientError
25
- fail ClientError.new response
46
+ raise ClientError, response
26
47
  when Net::HTTPServerError
27
- fail ServerError.new response
48
+ raise ServerError, response
28
49
  else # Handle this as a request error for now. Maybe fine tune this more later.
29
- fail ServerError.new response
50
+ raise ServerError, response
30
51
  end
31
- rescue Timeout::Error => error
32
- fail ServerError.new error
33
52
  end
34
53
 
35
-
36
- private
37
-
38
54
  def inspect_for_client_errors!(response)
39
- status = JSON.parse(response.body).fetch "status"
55
+ status = JSON.parse(response.body).fetch 'status'
40
56
 
41
- if CLIENT_ERRORS.include? status
42
- fail ClientError.new response, status
43
- end
57
+ raise ClientError.new response, status if CLIENT_ERRORS.include? status
44
58
 
45
59
  response
46
60
  end
@@ -1,9 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GoogleDistanceMatrix
4
+ # Cached client, which takes care of caching data from Google API
2
5
  class ClientCache
3
6
  attr_reader :client, :cache
4
7
 
5
- def self.key(url)
6
- url
8
+ # Returns a cache key for given URL
9
+ #
10
+ # @return String
11
+ def self.key(url, config)
12
+ config.cache_key_transform.call url
7
13
  end
8
14
 
9
15
  def initialize(client, cache)
@@ -12,7 +18,7 @@ module GoogleDistanceMatrix
12
18
  end
13
19
 
14
20
  def get(url, options = {})
15
- cache.fetch self.class.key(url) do
21
+ cache.fetch self.class.key(url, options.fetch(:configuration)) do
16
22
  client.get url, options
17
23
  end
18
24
  end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
1
5
  module GoogleDistanceMatrix
2
6
  # Public: Configuration of matrix and it's request.
3
7
  #
@@ -15,19 +19,20 @@ module GoogleDistanceMatrix
15
19
  departure_time arrival_time
16
20
  transit_mode transit_routing_preference
17
21
  traffic_model
18
- ]
22
+ ].freeze
19
23
 
20
24
  API_DEFAULTS = {
21
- mode: "driving",
22
- units: "metric",
23
- traffic_model: "best_guess",
25
+ mode: 'driving',
26
+ units: 'metric',
27
+ traffic_model: 'best_guess',
24
28
  use_encoded_polylines: false,
25
29
  protocol: 'https',
26
30
  lat_lng_scale: 5,
27
- filter_parameters_in_logged_url: ['key', 'signature'].freeze
31
+ filter_parameters_in_logged_url: %w[key signature].freeze,
32
+ cache_key_transform: ->(url) { Digest::SHA512.new.update(url).to_s }
28
33
  }.with_indifferent_access
29
34
 
30
- attr_accessor *ATTRIBUTES
35
+ attr_accessor(*ATTRIBUTES)
31
36
 
32
37
  # The protocol to use, either http or https
33
38
  attr_accessor :protocol
@@ -50,22 +55,38 @@ module GoogleDistanceMatrix
50
55
  # When logging we filter sensitive parameters
51
56
  attr_accessor :filter_parameters_in_logged_url
52
57
 
53
- validates :mode, inclusion: {in: ["driving", "walking", "bicycling", "transit"]}, allow_blank: true
54
- validates :avoid, inclusion: {in: ["tolls", "highways", "ferries", "indoor"]}, allow_blank: true
55
- validates :units, inclusion: {in: ["metric", "imperial"]}, allow_blank: true
58
+ # Callable object which transform given url to key used in cache
59
+ # @see ClientCache
60
+ attr_accessor :cache_key_transform
61
+
62
+ validates :mode, inclusion: { in: %w[driving walking bicycling transit] }, allow_blank: true
63
+ validates :avoid, inclusion: { in: %w[tolls highways ferries indoor] }, allow_blank: true
64
+ validates :units, inclusion: { in: %w[metric imperial] }, allow_blank: true
56
65
 
57
66
  validates :departure_time, format: /\A(\d+|now)\Z/, allow_blank: true
58
67
  validates :arrival_time, numericality: true, allow_blank: true
59
68
 
60
- validates :transit_mode, inclusion: {in: %w[bus subway train tram rail]}, allow_blank: true
61
- validates :transit_routing_preference, inclusion: {in: %w[less_walking fewer_transfers]}, allow_blank: true
62
- validates :traffic_model, inclusion: {in: %w[best_guess pessimistic optimistic]}, allow_blank: true
69
+ validates :transit_mode,
70
+ inclusion: { in: %w[bus subway train tram rail] },
71
+ allow_blank: true
72
+
73
+ validates :transit_routing_preference,
74
+ inclusion: { in: %w[less_walking fewer_transfers] },
75
+ allow_blank: true
63
76
 
64
- validates :protocol, inclusion: {in: ["http", "https"]}, allow_blank: true
77
+ validates :traffic_model,
78
+ inclusion: { in: %w[best_guess pessimistic optimistic] },
79
+ allow_blank: true
80
+
81
+ validates :protocol, inclusion: { in: %w[http https] }, allow_blank: true
65
82
 
66
83
  def initialize
67
84
  API_DEFAULTS.each_pair do |attr_name, value|
68
- self[attr_name] = value.dup rescue value
85
+ self[attr_name] = begin
86
+ value.dup
87
+ rescue
88
+ value
89
+ end
69
90
  end
70
91
  end
71
92
 
@@ -77,7 +98,6 @@ module GoogleDistanceMatrix
77
98
  public_send "#{attr_name}=", value
78
99
  end
79
100
 
80
-
81
101
  private
82
102
 
83
103
  def array_param
@@ -89,15 +109,13 @@ module GoogleDistanceMatrix
89
109
  out << ['client', google_business_api_client_id]
90
110
  end
91
111
 
92
- if google_api_key.present?
93
- out << ['key', google_api_key]
94
- end
112
+ out << ['key', google_api_key] if google_api_key.present?
95
113
 
96
114
  out
97
115
  end
98
116
 
99
117
  def param_same_as_api_default?(param)
100
- API_DEFAULTS[param[0]] == param[1]
118
+ API_DEFAULTS[param[0]] == param[1]
101
119
  end
102
120
  end
103
121
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GoogleDistanceMatrix
2
4
  # Public: Error class for lib.
3
5
  class Error < StandardError
@@ -67,7 +69,8 @@ module GoogleDistanceMatrix
67
69
  end
68
70
 
69
71
  def to_s
70
- "GoogleDistanceMatrix::ClientError - #{[response, status_read_from_api_response].compact.join('. ')}."
72
+ "GoogleDistanceMatrix::ClientError - \
73
+ #{[response, status_read_from_api_response].compact.join('. ')}."
71
74
  end
72
75
  end
73
76
 
@@ -75,7 +78,8 @@ module GoogleDistanceMatrix
75
78
  #
76
79
  # See https://developers.google.com/maps/documentation/distancematrix/#Limits, which states:
77
80
  # "Distance Matrix API URLs are restricted to 2048 characters, before URL encoding."
78
- # "As some Distance Matrix API service URLs may involve many locations, be aware of this limit when constructing your URLs."
81
+ # "As some Distance Matrix API service URLs may involve many locations,
82
+ # be aware of this limit when constructing your URLs."
79
83
  #
80
84
  class MatrixUrlTooLong < ClientError
81
85
  attr_reader :url, :max_url_size
@@ -91,5 +95,4 @@ module GoogleDistanceMatrix
91
95
  "Matrix API URL max size is: #{max_url_size}. Built URL was: #{url.length}. URL: '#{url}'."
92
96
  end
93
97
  end
94
-
95
98
  end
@@ -1,8 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GoogleDistanceMatrix
4
+ # LogSubscriber logs to GoogleDistanceMatrix.logger by subscribing to gem's intstrumentation.
5
+ #
6
+ # NOTE: This log subscruber uses the default_configuration as it's configuration.
7
+ # This is relevant for example for the filter_parameters_in_logged_url configuration
2
8
  class LogSubscriber < ActiveSupport::LogSubscriber
3
9
  attr_reader :logger, :config
4
10
 
5
- def initialize(logger: GoogleDistanceMatrix.logger, config: GoogleDistanceMatrix.default_configuration)
11
+ def initialize(
12
+ logger: GoogleDistanceMatrix.logger,
13
+ config: GoogleDistanceMatrix.default_configuration
14
+ )
6
15
  super()
7
16
 
8
17
  @logger = logger
@@ -10,20 +19,11 @@ module GoogleDistanceMatrix
10
19
  end
11
20
 
12
21
  def client_request_matrix_data(event)
13
- url = filter_url! event.payload[:url]
14
- logger.info "(#{event.duration}ms) (elements: #{event.payload[:elements]}) GET #{url}", tag: :client
15
- end
16
-
17
- private
18
-
19
- def filter_url!(url)
20
- config.filter_parameters_in_logged_url.each do |param|
21
- url.gsub! %r{(#{param})=.*?(&|$)}, '\1=[FILTERED]\2'
22
- end
23
-
24
- url
22
+ url = event.payload[:filtered_url]
23
+ logger.info "(#{event.duration}ms) (elements: #{event.payload[:elements]}) GET #{url}",
24
+ tag: :client
25
25
  end
26
26
  end
27
27
  end
28
28
 
29
- GoogleDistanceMatrix::LogSubscriber.attach_to "google_distance_matrix"
29
+ GoogleDistanceMatrix::LogSubscriber.attach_to 'google_distance_matrix'
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GoogleDistanceMatrix
4
+ # Logger class for Google Distance Matrix
2
5
  class Logger
3
- PREFIXES = %w[google_distance_matrix]
4
- LEVELS = %w[fatal error warn info debug]
6
+ PREFIXES = %w[google_distance_matrix].freeze
7
+ LEVELS = %w[fatal error warn info debug].freeze
5
8
 
6
9
  attr_reader :backend
7
10
 
@@ -20,13 +23,12 @@ module GoogleDistanceMatrix
20
23
  end
21
24
  end
22
25
 
23
-
24
26
  private
25
27
 
26
28
  def tag_msg(msg, tags)
27
29
  msg_buffer = tags.map { |tag| "[#{tag}]" }
28
30
  msg_buffer << msg
29
- msg_buffer.join " "
31
+ msg_buffer.join ' '
30
32
  end
31
33
  end
32
34
  end