google_distance_matrix 0.4.0 → 0.5.0

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