google_distance_matrix 0.4.0 → 0.6.3
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.
- checksums.yaml +5 -5
- data/.editorconfig +16 -0
- data/.rubocop.yml +6 -0
- data/.ruby-version +1 -1
- data/.travis.yml +4 -6
- data/CHANGELOG.md +40 -0
- data/Gemfile +2 -0
- data/README.md +0 -3
- data/Rakefile +9 -4
- data/google_distance_matrix.gemspec +21 -19
- data/lib/google_distance_matrix.rb +25 -23
- data/lib/google_distance_matrix/client.rb +32 -18
- data/lib/google_distance_matrix/client_cache.rb +9 -3
- data/lib/google_distance_matrix/configuration.rb +39 -24
- data/lib/google_distance_matrix/errors.rb +6 -3
- data/lib/google_distance_matrix/log_subscriber.rb +14 -14
- data/lib/google_distance_matrix/logger.rb +7 -5
- data/lib/google_distance_matrix/matrix.rb +45 -22
- data/lib/google_distance_matrix/place.rb +37 -28
- data/lib/google_distance_matrix/places.rb +5 -4
- data/lib/google_distance_matrix/polyline_encoder.rb +2 -2
- data/lib/google_distance_matrix/polyline_encoder/delta.rb +4 -2
- data/lib/google_distance_matrix/polyline_encoder/value_encoder.rb +13 -5
- data/lib/google_distance_matrix/railtie.rb +4 -1
- data/lib/google_distance_matrix/route.rb +22 -15
- data/lib/google_distance_matrix/routes_finder.rb +27 -29
- data/lib/google_distance_matrix/url_builder.rb +44 -16
- data/lib/google_distance_matrix/url_builder/polyline_encoder_buffer.rb +3 -0
- data/lib/google_distance_matrix/version.rb +3 -1
- data/spec/lib/google_distance_matrix/client_cache_spec.rb +27 -11
- data/spec/lib/google_distance_matrix/client_spec.rb +40 -30
- data/spec/lib/google_distance_matrix/configuration_spec.rb +36 -24
- data/spec/lib/google_distance_matrix/log_subscriber_spec.rb +13 -44
- data/spec/lib/google_distance_matrix/logger_spec.rb +16 -13
- data/spec/lib/google_distance_matrix/matrix_spec.rb +90 -57
- data/spec/lib/google_distance_matrix/place_spec.rb +38 -25
- data/spec/lib/google_distance_matrix/places_spec.rb +29 -28
- data/spec/lib/google_distance_matrix/polyline_encoder/delta_spec.rb +5 -3
- data/spec/lib/google_distance_matrix/polyline_encoder_spec.rb +7 -2
- data/spec/lib/google_distance_matrix/route_spec.rb +11 -9
- data/spec/lib/google_distance_matrix/routes_finder_spec.rb +124 -81
- data/spec/lib/google_distance_matrix/url_builder_spec.rb +97 -48
- data/spec/spec_helper.rb +3 -1
- metadata +46 -18
| @@ -1,6 +1,9 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module GoogleDistanceMatrix
         | 
| 4 | 
            +
              # Class integrating with rails
         | 
| 2 5 | 
             
              class Railtie < Rails::Railtie
         | 
| 3 | 
            -
                initializer  | 
| 6 | 
            +
                initializer 'google_distance_matrix.logger_setup' do
         | 
| 4 7 | 
             
                  GoogleDistanceMatrix.configure_defaults do |config|
         | 
| 5 8 | 
             
                    config.logger = Rails.logger
         | 
| 6 9 | 
             
                  end
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module GoogleDistanceMatrix
         | 
| 2 4 | 
             
              # Public: Thin wrapper class for an element in the matrix.
         | 
| 3 5 | 
             
              #
         | 
| @@ -15,10 +17,9 @@ module GoogleDistanceMatrix | |
| 15 17 | 
             
                  duration_in_traffic_text duration_in_traffic_in_seconds
         | 
| 16 18 | 
             
                ].freeze
         | 
| 17 19 |  | 
| 18 | 
            -
                attr_reader | 
| 19 | 
            -
             | 
| 20 | 
            -
                delegate *(STATUSES.map { |s| s + '?' }), to: :status, allow_nil: true
         | 
| 20 | 
            +
                attr_reader(*ATTRIBUTES)
         | 
| 21 21 |  | 
| 22 | 
            +
                delegate(*(STATUSES.map { |s| s + '?' }), to: :status, allow_nil: true)
         | 
| 22 23 |  | 
| 23 24 | 
             
                def initialize(attributes = {})
         | 
| 24 25 | 
             
                  attributes = attributes.with_indifferent_access
         | 
| @@ -28,25 +29,31 @@ module GoogleDistanceMatrix | |
| 28 29 |  | 
| 29 30 | 
             
                  @status = ActiveSupport::StringInquirer.new attributes[:status].downcase
         | 
| 30 31 |  | 
| 31 | 
            -
                  if ok?
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                    @distance_in_meters = attributes[:distance][:value]
         | 
| 34 | 
            -
                    @duration_text = attributes[:duration][:text]
         | 
| 35 | 
            -
                    @duration_in_seconds = attributes[:duration][:value]
         | 
| 32 | 
            +
                  assign attributes if ok?
         | 
| 33 | 
            +
                end
         | 
| 36 34 |  | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
                    end
         | 
| 35 | 
            +
                def inspect
         | 
| 36 | 
            +
                  attrs = ATTRIBUTES.reject do |a|
         | 
| 37 | 
            +
                    public_send(a).blank?
         | 
| 41 38 | 
             
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  inspection = attrs.map { |a| "#{a}: #{public_send(a).inspect}" }.join ', '
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  "#<#{self.class} #{inspection}>"
         | 
| 42 43 | 
             
                end
         | 
| 43 44 |  | 
| 45 | 
            +
                private
         | 
| 44 46 |  | 
| 47 | 
            +
                def assign(attributes)
         | 
| 48 | 
            +
                  @distance_text = attributes[:distance][:text]
         | 
| 49 | 
            +
                  @distance_in_meters = attributes[:distance][:value]
         | 
| 50 | 
            +
                  @duration_text = attributes[:duration][:text]
         | 
| 51 | 
            +
                  @duration_in_seconds = attributes[:duration][:value]
         | 
| 45 52 |  | 
| 46 | 
            -
             | 
| 47 | 
            -
                  inspection = ATTRIBUTES.reject { |a| public_send(a).blank? }.map { |a| "#{a}: #{public_send(a).inspect}" }.join ', '
         | 
| 53 | 
            +
                  return unless  attributes.key? :duration_in_traffic
         | 
| 48 54 |  | 
| 49 | 
            -
                   | 
| 55 | 
            +
                  @duration_in_traffic_text = attributes[:duration_in_traffic][:text]
         | 
| 56 | 
            +
                  @duration_in_traffic_in_seconds = attributes[:duration_in_traffic][:value]
         | 
| 50 57 | 
             
                end
         | 
| 51 58 | 
             
              end
         | 
| 52 59 | 
             
            end
         | 
| @@ -1,17 +1,17 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module GoogleDistanceMatrix
         | 
| 2 4 | 
             
              # Public: Has logic for doing finder operations on a matrix.
         | 
| 5 | 
            +
              #
         | 
| 6 | 
            +
              # rubocop:disable Metrics/ClassLength
         | 
| 3 7 | 
             
              class RoutesFinder
         | 
| 4 | 
            -
             | 
| 5 8 | 
             
                attr_reader :matrix
         | 
| 6 9 | 
             
                delegate :data, :origins, :destinations, :configuration, to: :matrix
         | 
| 7 10 |  | 
| 8 | 
            -
             | 
| 9 11 | 
             
                def initialize(matrix)
         | 
| 10 12 | 
             
                  @matrix = matrix
         | 
| 11 13 | 
             
                end
         | 
| 12 14 |  | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 15 | 
             
                # Public: Finds routes for given place.
         | 
| 16 16 | 
             
                #
         | 
| 17 17 | 
             
                # place   -  Either an origin or destination, or an object which you built the place from
         | 
| @@ -25,7 +25,7 @@ module GoogleDistanceMatrix | |
| 25 25 | 
             
                  elsif destinations.include? place
         | 
| 26 26 | 
             
                    routes_for_destination place
         | 
| 27 27 | 
             
                  else
         | 
| 28 | 
            -
                     | 
| 28 | 
            +
                    raise ArgumentError, 'Given place not an origin nor destination.'
         | 
| 29 29 | 
             
                  end
         | 
| 30 30 | 
             
                end
         | 
| 31 31 |  | 
| @@ -41,11 +41,11 @@ module GoogleDistanceMatrix | |
| 41 41 | 
             
                  end
         | 
| 42 42 | 
             
                end
         | 
| 43 43 |  | 
| 44 | 
            -
             | 
| 45 44 | 
             
                # Public: Finds a route for you based on one origin and destination
         | 
| 46 45 | 
             
                #
         | 
| 47 46 | 
             
                # origin        - A place representing the origin, or an object which you built the origin from
         | 
| 48 | 
            -
                # destination   - A place representing the destination, or an object which you built the | 
| 47 | 
            +
                # destination   - A place representing the destination, or an object which you built the
         | 
| 48 | 
            +
                #                   destination from
         | 
| 49 49 | 
             
                #
         | 
| 50 50 | 
             
                # A Route for given origin and destination
         | 
| 51 51 | 
             
                def route_for(options = {})
         | 
| @@ -54,9 +54,7 @@ module GoogleDistanceMatrix | |
| 54 54 | 
             
                  origin = ensure_place options[:origin]
         | 
| 55 55 | 
             
                  destination = ensure_place options[:destination]
         | 
| 56 56 |  | 
| 57 | 
            -
                  if origin.nil? || destination.nil?
         | 
| 58 | 
            -
                    fail ArgumentError, "Must provide origin and destination"
         | 
| 59 | 
            -
                  end
         | 
| 57 | 
            +
                  raise ArgumentError, 'Must provide origin and destination' if origin.nil? || destination.nil?
         | 
| 60 58 |  | 
| 61 59 | 
             
                  routes_for(origin).detect { |route| route.destination == destination }
         | 
| 62 60 | 
             
                end
         | 
| @@ -71,7 +69,6 @@ module GoogleDistanceMatrix | |
| 71 69 | 
             
                  end
         | 
| 72 70 | 
             
                end
         | 
| 73 71 |  | 
| 74 | 
            -
             | 
| 75 72 | 
             
                # Public: Finds shortes route by distance to a place.
         | 
| 76 73 | 
             
                #
         | 
| 77 74 | 
             
                # place - The place, or object place was built from, you want the shortest route to
         | 
| @@ -79,7 +76,7 @@ module GoogleDistanceMatrix | |
| 79 76 | 
             
                # Returns shortest route, or nil if no routes had status ok
         | 
| 80 77 | 
             
                def shortest_route_by_distance_to(place_or_object_place_was_built_from)
         | 
| 81 78 | 
             
                  routes = routes_for place_or_object_place_was_built_from
         | 
| 82 | 
            -
                  select_ok_routes(routes).min_by | 
| 79 | 
            +
                  select_ok_routes(routes).min_by(&:distance_in_meters)
         | 
| 83 80 | 
             
                end
         | 
| 84 81 |  | 
| 85 82 | 
             
                # Public: Finds shortes route by distance to a place.
         | 
| @@ -88,7 +85,7 @@ module GoogleDistanceMatrix | |
| 88 85 | 
             
                #
         | 
| 89 86 | 
             
                # Returns shortest route, fails if any of the routes are not ok
         | 
| 90 87 | 
             
                def shortest_route_by_distance_to!(place_or_object_place_was_built_from)
         | 
| 91 | 
            -
                  routes_for!(place_or_object_place_was_built_from).min_by | 
| 88 | 
            +
                  routes_for!(place_or_object_place_was_built_from).min_by(&:distance_in_meters)
         | 
| 92 89 | 
             
                end
         | 
| 93 90 |  | 
| 94 91 | 
             
                # Public: Finds shortes route by duration to a place.
         | 
| @@ -98,7 +95,7 @@ module GoogleDistanceMatrix | |
| 98 95 | 
             
                # Returns shortest route, or nil if no routes had status ok
         | 
| 99 96 | 
             
                def shortest_route_by_duration_to(place_or_object_place_was_built_from)
         | 
| 100 97 | 
             
                  routes = routes_for place_or_object_place_was_built_from
         | 
| 101 | 
            -
                  select_ok_routes(routes).min_by | 
| 98 | 
            +
                  select_ok_routes(routes).min_by(&:duration_in_seconds)
         | 
| 102 99 | 
             
                end
         | 
| 103 100 |  | 
| 104 101 | 
             
                # Public: Finds shortes route by duration to a place.
         | 
| @@ -107,7 +104,7 @@ module GoogleDistanceMatrix | |
| 107 104 | 
             
                #
         | 
| 108 105 | 
             
                # Returns shortest route, fails if any of the routes are not ok
         | 
| 109 106 | 
             
                def shortest_route_by_duration_to!(place_or_object_place_was_built_from)
         | 
| 110 | 
            -
                  routes_for!(place_or_object_place_was_built_from).min_by | 
| 107 | 
            +
                  routes_for!(place_or_object_place_was_built_from).min_by(&:duration_in_seconds)
         | 
| 111 108 | 
             
                end
         | 
| 112 109 |  | 
| 113 110 | 
             
                # Public: Finds shortes route by duration in traffic to a place.
         | 
| @@ -122,7 +119,7 @@ module GoogleDistanceMatrix | |
| 122 119 | 
             
                  ensure_driving_and_departure_time_or_fail!
         | 
| 123 120 |  | 
| 124 121 | 
             
                  routes = routes_for place_or_object_place_was_built_from
         | 
| 125 | 
            -
                  select_ok_routes(routes).min_by | 
| 122 | 
            +
                  select_ok_routes(routes).min_by(&:duration_in_traffic_in_seconds)
         | 
| 126 123 | 
             
                end
         | 
| 127 124 |  | 
| 128 125 | 
             
                # Public: Finds shortes route by duration in traffic to a place.
         | 
| @@ -136,39 +133,39 @@ module GoogleDistanceMatrix | |
| 136 133 | 
             
                def shortest_route_by_duration_in_traffic_to!(place_or_object_place_was_built_from)
         | 
| 137 134 | 
             
                  ensure_driving_and_departure_time_or_fail!
         | 
| 138 135 |  | 
| 139 | 
            -
                  routes_for!(place_or_object_place_was_built_from).min_by | 
| 136 | 
            +
                  routes_for!(place_or_object_place_was_built_from).min_by(&:duration_in_traffic_in_seconds)
         | 
| 140 137 | 
             
                end
         | 
| 141 138 |  | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 139 | 
             
                private
         | 
| 145 140 |  | 
| 146 141 | 
             
                def ensure_place(object)
         | 
| 147 142 | 
             
                  if object.is_a? Place
         | 
| 148 143 | 
             
                    object
         | 
| 149 144 | 
             
                  else
         | 
| 145 | 
            +
                    object = object.with_indifferent_access if object.is_a? Hash
         | 
| 146 | 
            +
             | 
| 150 147 | 
             
                    find_place_for_object(origins, object) ||
         | 
| 151 | 
            -
             | 
| 148 | 
            +
                      find_place_for_object(destinations, object)
         | 
| 152 149 | 
             
                  end
         | 
| 153 150 | 
             
                end
         | 
| 154 151 |  | 
| 155 152 | 
             
                def find_place_for_object(collection, object)
         | 
| 156 153 | 
             
                  collection.detect do |place|
         | 
| 157 154 | 
             
                    place.extracted_attributes_from.present? &&
         | 
| 158 | 
            -
             | 
| 155 | 
            +
                      place.extracted_attributes_from == object
         | 
| 159 156 | 
             
                  end
         | 
| 160 157 | 
             
                end
         | 
| 161 158 |  | 
| 162 159 | 
             
                def routes_for_origin(origin)
         | 
| 163 160 | 
             
                  index = origins.index origin
         | 
| 164 | 
            -
                   | 
| 161 | 
            +
                  raise ArgumentError, 'Given origin is not i matrix.' if index.nil?
         | 
| 165 162 |  | 
| 166 163 | 
             
                  data[index]
         | 
| 167 164 | 
             
                end
         | 
| 168 165 |  | 
| 169 166 | 
             
                def routes_for_destination(destination)
         | 
| 170 167 | 
             
                  index = destinations.index destination
         | 
| 171 | 
            -
                   | 
| 168 | 
            +
                  raise ArgumentError, 'Given destination is not i matrix.' if index.nil?
         | 
| 172 169 |  | 
| 173 170 | 
             
                  [].tap do |routes|
         | 
| 174 171 | 
             
                    data.each { |row| routes << row[index] }
         | 
| @@ -180,24 +177,25 @@ module GoogleDistanceMatrix | |
| 180 177 | 
             
                  destination_index = destinations.index destination
         | 
| 181 178 |  | 
| 182 179 | 
             
                  if origin_index.nil? || destination_index.nil?
         | 
| 183 | 
            -
                     | 
| 180 | 
            +
                    raise ArgumentError, 'Given origin or destination is not i matrix.'
         | 
| 184 181 | 
             
                  end
         | 
| 185 182 |  | 
| 186 183 | 
             
                  [data[origin_index][destination_index]]
         | 
| 187 184 | 
             
                end
         | 
| 188 185 |  | 
| 189 186 | 
             
                def fail_unless_route_is_ok(route)
         | 
| 190 | 
            -
                   | 
| 187 | 
            +
                  raise InvalidRoute, route unless route.ok?
         | 
| 191 188 | 
             
                end
         | 
| 192 189 |  | 
| 193 190 | 
             
                def select_ok_routes(routes)
         | 
| 194 | 
            -
                  routes.select | 
| 191 | 
            +
                  routes.select(&:ok?)
         | 
| 195 192 | 
             
                end
         | 
| 196 193 |  | 
| 197 194 | 
             
                def ensure_driving_and_departure_time_or_fail!
         | 
| 198 | 
            -
                  if configuration.mode  | 
| 199 | 
            -
             | 
| 200 | 
            -
                   | 
| 195 | 
            +
                  return if configuration.mode == 'driving' && configuration.departure_time.present?
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                  raise InvalidQuery, 'Matrix must be in mode driving and a departure_time must be set'
         | 
| 201 198 | 
             
                end
         | 
| 202 199 | 
             
              end
         | 
| 200 | 
            +
              # rubocop:enable Metrics/ClassLength
         | 
| 203 201 | 
             
            end
         | 
| @@ -1,9 +1,12 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require_relative 'url_builder/polyline_encoder_buffer'
         | 
| 2 4 |  | 
| 3 5 | 
             
            module GoogleDistanceMatrix
         | 
| 6 | 
            +
              # Takes care of building the url for given matrix
         | 
| 4 7 | 
             
              class UrlBuilder
         | 
| 5 | 
            -
                BASE_URL =  | 
| 6 | 
            -
                DELIMITER = CGI.escape( | 
| 8 | 
            +
                BASE_URL = 'maps.googleapis.com/maps/api/distancematrix/json'
         | 
| 9 | 
            +
                DELIMITER = CGI.escape('|')
         | 
| 7 10 | 
             
                MAX_URL_SIZE = 2048
         | 
| 8 11 |  | 
| 9 12 | 
             
                attr_reader :matrix
         | 
| @@ -12,41 +15,62 @@ module GoogleDistanceMatrix | |
| 12 15 | 
             
                def initialize(matrix)
         | 
| 13 16 | 
             
                  @matrix = matrix
         | 
| 14 17 |  | 
| 15 | 
            -
                   | 
| 18 | 
            +
                  raise InvalidMatrix, matrix if matrix.invalid?
         | 
| 16 19 | 
             
                end
         | 
| 17 20 |  | 
| 18 | 
            -
                 | 
| 19 | 
            -
             | 
| 21 | 
            +
                # Returns the URL we'll call Google API with
         | 
| 22 | 
            +
                #
         | 
| 23 | 
            +
                # This URL contains key and signature and is therefor
         | 
| 24 | 
            +
                # sensitive.
         | 
| 25 | 
            +
                #
         | 
| 26 | 
            +
                # @return String
         | 
| 27 | 
            +
                # @see filtered_url
         | 
| 28 | 
            +
                def sensitive_url
         | 
| 29 | 
            +
                  @sensitive_url ||= build_url
         | 
| 20 30 | 
             
                end
         | 
| 21 31 |  | 
| 32 | 
            +
                # Returns the URL filtered as the configuration of the matrix dictates
         | 
| 33 | 
            +
                #
         | 
| 34 | 
            +
                # @return String
         | 
| 35 | 
            +
                def filtered_url
         | 
| 36 | 
            +
                  filter_url sensitive_url
         | 
| 37 | 
            +
                end
         | 
| 22 38 |  | 
| 23 39 | 
             
                private
         | 
| 24 40 |  | 
| 25 41 | 
             
                def build_url
         | 
| 26 | 
            -
                  url = [protocol, BASE_URL,  | 
| 42 | 
            +
                  url = [protocol, BASE_URL, '?', query_params_string].join
         | 
| 27 43 |  | 
| 28 44 | 
             
                  if sign_url?
         | 
| 29 | 
            -
                    url = GoogleBusinessApiUrlSigner.add_signature( | 
| 45 | 
            +
                    url = GoogleBusinessApiUrlSigner.add_signature(
         | 
| 46 | 
            +
                      url, configuration.google_business_api_private_key
         | 
| 47 | 
            +
                    )
         | 
| 30 48 | 
             
                  end
         | 
| 31 49 |  | 
| 32 | 
            -
                  if url.length > MAX_URL_SIZE
         | 
| 33 | 
            -
             | 
| 50 | 
            +
                  raise MatrixUrlTooLong.new url, MAX_URL_SIZE if url.length > MAX_URL_SIZE
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  url
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def filter_url(url)
         | 
| 56 | 
            +
                  configuration.filter_parameters_in_logged_url.each do |param|
         | 
| 57 | 
            +
                    url = url.gsub(/(#{param})=.*?(&|$)/, '\1=[FILTERED]\2')
         | 
| 34 58 | 
             
                  end
         | 
| 35 59 |  | 
| 36 60 | 
             
                  url
         | 
| 37 61 | 
             
                end
         | 
| 38 62 |  | 
| 39 63 | 
             
                def sign_url?
         | 
| 40 | 
            -
                  configuration.google_business_api_client_id.present?  | 
| 41 | 
            -
             | 
| 64 | 
            +
                  configuration.google_business_api_client_id.present? &&
         | 
| 65 | 
            +
                    configuration.google_business_api_private_key.present?
         | 
| 42 66 | 
             
                end
         | 
| 43 67 |  | 
| 44 68 | 
             
                def include_api_key?
         | 
| 45 69 | 
             
                  configuration.google_api_key.present?
         | 
| 46 70 | 
             
                end
         | 
| 47 71 |  | 
| 48 | 
            -
                def  | 
| 49 | 
            -
                  params.to_a.map { |key_value| key_value.join( | 
| 72 | 
            +
                def query_params_string
         | 
| 73 | 
            +
                  params.to_a.map { |key_value| key_value.join('=') }.join('&')
         | 
| 50 74 | 
             
                end
         | 
| 51 75 |  | 
| 52 76 | 
             
                def params
         | 
| @@ -56,8 +80,10 @@ module GoogleDistanceMatrix | |
| 56 80 | 
             
                  )
         | 
| 57 81 | 
             
                end
         | 
| 58 82 |  | 
| 83 | 
            +
                # rubocop:disable Metrics/MethodLength
         | 
| 84 | 
            +
                # rubocop:disable Metrics/AbcSize
         | 
| 59 85 | 
             
                def places_to_param(places)
         | 
| 60 | 
            -
                  places_to_param_config = {lat_lng_scale: configuration.lat_lng_scale}
         | 
| 86 | 
            +
                  places_to_param_config = { lat_lng_scale: configuration.lat_lng_scale }
         | 
| 61 87 |  | 
| 62 88 | 
             
                  out = []
         | 
| 63 89 | 
             
                  polyline_encode_buffer = PolylineEncoderBuffer.new
         | 
| @@ -67,7 +93,7 @@ module GoogleDistanceMatrix | |
| 67 93 | 
             
                      polyline_encode_buffer << place.lat_lng
         | 
| 68 94 | 
             
                    else
         | 
| 69 95 | 
             
                      polyline_encode_buffer.flush to: out
         | 
| 70 | 
            -
                      out << escape(place.to_param | 
| 96 | 
            +
                      out << escape(place.to_param(places_to_param_config))
         | 
| 71 97 | 
             
                    end
         | 
| 72 98 | 
             
                  end
         | 
| 73 99 |  | 
| @@ -75,9 +101,11 @@ module GoogleDistanceMatrix | |
| 75 101 |  | 
| 76 102 | 
             
                  out.join(DELIMITER)
         | 
| 77 103 | 
             
                end
         | 
| 104 | 
            +
                # rubocop:enable Metrics/MethodLength
         | 
| 105 | 
            +
                # rubocop:enable Metrics/AbcSize
         | 
| 78 106 |  | 
| 79 107 | 
             
                def protocol
         | 
| 80 | 
            -
                  configuration.protocol +  | 
| 108 | 
            +
                  configuration.protocol + '://'
         | 
| 81 109 | 
             
                end
         | 
| 82 110 |  | 
| 83 111 | 
             
                def escape(string)
         | 
| @@ -1,25 +1,41 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'spec_helper'
         | 
| 2 4 |  | 
| 3 5 | 
             
            describe GoogleDistanceMatrix::ClientCache do
         | 
| 4 | 
            -
              let(: | 
| 5 | 
            -
              let(: | 
| 6 | 
            +
              let(:config) { GoogleDistanceMatrix::Configuration.new }
         | 
| 7 | 
            +
              let(:url) { 'http://www.example.com' }
         | 
| 8 | 
            +
              let(:options) { { hello: :options, configuration: config } }
         | 
| 6 9 |  | 
| 7 | 
            -
              let(:client) { double get:  | 
| 10 | 
            +
              let(:client) { double get: 'data' }
         | 
| 8 11 | 
             
              let(:cache) { double }
         | 
| 9 12 |  | 
| 10 13 | 
             
              subject { described_class.new client, cache }
         | 
| 11 14 |  | 
| 12 | 
            -
               | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
                   | 
| 15 | 
            +
              # rubocop:disable Metrics/LineLength
         | 
| 16 | 
            +
              describe '::key' do
         | 
| 17 | 
            +
                it 'returns a digest of given URL' do
         | 
| 18 | 
            +
                  key = described_class.key 'some url with secret parts', config
         | 
| 19 | 
            +
                  expect(key).to eq 'e90595434d4e321da6b01d2b99d77419ddaa8861d83c5971c4a119ee76bb80a7003915cc16e6966615f205b4a1d5411bb5d4a0d907f611b3fe4cc8d9049f4f9c'
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              describe '#get' do
         | 
| 24 | 
            +
                it 'returns from cache if it hits' do
         | 
| 25 | 
            +
                  expect(cache)
         | 
| 26 | 
            +
                    .to receive(:fetch)
         | 
| 27 | 
            +
                    .with('2f7d4c4d8a51afd0f9efb9edfda07591591cccc3704130328ad323d3cb5bf7ff19df5e895b402c99217d27d5f4547618094d47069c9ba58370ed8e26cc1de114')
         | 
| 28 | 
            +
                    .and_return 'cached-data'
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  expect(subject.get(url, options)).to eq 'cached-data'
         | 
| 16 31 | 
             
                end
         | 
| 32 | 
            +
                # rubocop:enable Metrics/LineLength
         | 
| 17 33 |  | 
| 18 | 
            -
                it  | 
| 19 | 
            -
                  expect(client).to receive(:get).with(url, options).and_return  | 
| 34 | 
            +
                it 'asks client when cache miss' do
         | 
| 35 | 
            +
                  expect(client).to receive(:get).with(url, options).and_return 'api-data'
         | 
| 20 36 | 
             
                  expect(cache).to receive(:fetch) { |&block| block.call }
         | 
| 21 37 |  | 
| 22 | 
            -
                  expect(subject.get(url, options)).to eq  | 
| 38 | 
            +
                  expect(subject.get(url, options)).to eq 'api-data'
         | 
| 23 39 | 
             
                end
         | 
| 24 40 | 
             
              end
         | 
| 25 41 | 
             
            end
         | 
| @@ -1,66 +1,76 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'spec_helper'
         | 
| 2 4 |  | 
| 3 5 | 
             
            describe GoogleDistanceMatrix::Client, :request_recordings do
         | 
| 4 | 
            -
              let(:origin_1) { GoogleDistanceMatrix::Place.new address:  | 
| 5 | 
            -
              let(:destination_1) { GoogleDistanceMatrix::Place.new address:  | 
| 6 | 
            -
              let(:matrix)  | 
| 6 | 
            +
              let(:origin_1) { GoogleDistanceMatrix::Place.new address: 'Karl Johans gate, Oslo' }
         | 
| 7 | 
            +
              let(:destination_1) { GoogleDistanceMatrix::Place.new address: 'Drammensveien 1, Oslo' }
         | 
| 8 | 
            +
              let(:matrix) do
         | 
| 9 | 
            +
                GoogleDistanceMatrix::Matrix.new(origins: [origin_1], destinations: [destination_1])
         | 
| 10 | 
            +
              end
         | 
| 7 11 |  | 
| 8 12 | 
             
              let(:url_builder) { GoogleDistanceMatrix::UrlBuilder.new matrix }
         | 
| 9 | 
            -
              let(:url) { url_builder. | 
| 13 | 
            +
              let(:url) { url_builder.sensitive_url }
         | 
| 10 14 |  | 
| 11 15 | 
             
              subject { GoogleDistanceMatrix::Client.new }
         | 
| 12 16 |  | 
| 13 | 
            -
              describe  | 
| 17 | 
            +
              describe 'success' do
         | 
| 14 18 | 
             
                before { stub_request(:get, url).to_return body: recorded_request_for(:success) }
         | 
| 15 19 |  | 
| 16 | 
            -
                it  | 
| 17 | 
            -
                  expect(subject.get(url_builder. | 
| 20 | 
            +
                it 'makes the request' do
         | 
| 21 | 
            +
                  expect(subject.get(url_builder.sensitive_url).body).to eq recorded_request_for(:success).read
         | 
| 18 22 | 
             
                end
         | 
| 19 23 | 
             
              end
         | 
| 20 24 |  | 
| 21 | 
            -
              describe  | 
| 22 | 
            -
                describe  | 
| 23 | 
            -
                  it  | 
| 24 | 
            -
                    stub_request(:get, url).to_return status: [400,  | 
| 25 | 
            -
                    expect { subject.get(url_builder. | 
| 25 | 
            +
              describe 'client errors' do
         | 
| 26 | 
            +
                describe 'server issues 4xx client error' do
         | 
| 27 | 
            +
                  it 'wraps the error http response' do
         | 
| 28 | 
            +
                    stub_request(:get, url).to_return status: [400, 'Client error']
         | 
| 29 | 
            +
                    expect { subject.get(url_builder.sensitive_url) }
         | 
| 30 | 
            +
                      .to raise_error GoogleDistanceMatrix::ClientError
         | 
| 26 31 | 
             
                  end
         | 
| 27 32 |  | 
| 28 | 
            -
                  it  | 
| 29 | 
            -
                    stub_request(:get, url).to_return status: [414,  | 
| 30 | 
            -
                    expect { subject.get(url_builder. | 
| 33 | 
            +
                  it 'wraps uri too long error' do
         | 
| 34 | 
            +
                    stub_request(:get, url).to_return status: [414, 'Client error']
         | 
| 35 | 
            +
                    expect { subject.get(url_builder.sensitive_url) }
         | 
| 36 | 
            +
                      .to raise_error GoogleDistanceMatrix::MatrixUrlTooLong
         | 
| 31 37 | 
             
                  end
         | 
| 32 38 | 
             
                end
         | 
| 33 39 |  | 
| 34 40 | 
             
                described_class::CLIENT_ERRORS.each do |error|
         | 
| 35 41 | 
             
                  it "wraps '#{error}' client error" do
         | 
| 36 | 
            -
                    stub_request(:get, url).to_return body: JSON.generate( | 
| 37 | 
            -
                    expect { subject.get(url_builder. | 
| 42 | 
            +
                    stub_request(:get, url).to_return body: JSON.generate(status: error)
         | 
| 43 | 
            +
                    expect { subject.get(url_builder.sensitive_url) }
         | 
| 44 | 
            +
                      .to raise_error GoogleDistanceMatrix::ClientError
         | 
| 38 45 | 
             
                  end
         | 
| 39 46 | 
             
                end
         | 
| 40 47 | 
             
              end
         | 
| 41 48 |  | 
| 42 | 
            -
              describe  | 
| 43 | 
            -
                describe  | 
| 44 | 
            -
                  before { stub_request(:get, url).to_return status: [500,  | 
| 49 | 
            +
              describe 'request errors' do
         | 
| 50 | 
            +
                describe 'server error' do
         | 
| 51 | 
            +
                  before { stub_request(:get, url).to_return status: [500, 'Internal Server Error'] }
         | 
| 45 52 |  | 
| 46 | 
            -
                  it  | 
| 47 | 
            -
                    expect { subject.get(url_builder. | 
| 53 | 
            +
                  it 'wraps the error http response' do
         | 
| 54 | 
            +
                    expect { subject.get(url_builder.sensitive_url) }
         | 
| 55 | 
            +
                      .to raise_error GoogleDistanceMatrix::ServerError
         | 
| 48 56 | 
             
                  end
         | 
| 49 57 | 
             
                end
         | 
| 50 58 |  | 
| 51 | 
            -
                describe  | 
| 59 | 
            +
                describe 'timeout' do
         | 
| 52 60 | 
             
                  before { stub_request(:get, url).to_timeout }
         | 
| 53 61 |  | 
| 54 | 
            -
                  it  | 
| 55 | 
            -
                    expect { subject.get(url_builder. | 
| 62 | 
            +
                  it 'wraps the error from Net::HTTP' do
         | 
| 63 | 
            +
                    expect { subject.get(url_builder.sensitive_url).body }
         | 
| 64 | 
            +
                      .to raise_error GoogleDistanceMatrix::ServerError
         | 
| 56 65 | 
             
                  end
         | 
| 57 66 | 
             
                end
         | 
| 58 67 |  | 
| 59 | 
            -
                describe  | 
| 60 | 
            -
                  before { stub_request(:get, url).to_return status: [999,  | 
| 68 | 
            +
                describe 'server error' do
         | 
| 69 | 
            +
                  before { stub_request(:get, url).to_return status: [999, 'Unknown'] }
         | 
| 61 70 |  | 
| 62 | 
            -
                  it  | 
| 63 | 
            -
                    expect { subject.get(url_builder. | 
| 71 | 
            +
                  it 'wraps the error http response' do
         | 
| 72 | 
            +
                    expect { subject.get(url_builder.sensitive_url) }
         | 
| 73 | 
            +
                      .to raise_error GoogleDistanceMatrix::ServerError
         | 
| 64 74 | 
             
                  end
         | 
| 65 75 | 
             
                end
         | 
| 66 76 | 
             
              end
         |