google_distance_matrix 0.0.1 → 0.0.2

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.
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ ## v.0.0.2
2
+
3
+ * Renamed RequestError to ServerError.
4
+ * Added ability to cache API responses using an ActiveSupport::Cache::Store. Simply using URL as cache key.
5
+ * Explicit instrumentation and logging how many elements requested from the API.
6
+
7
+
8
+ ## v.0.0.1
9
+
10
+ * First release.
data/README.md CHANGED
@@ -96,6 +96,25 @@ Or install it yourself as:
96
96
 
97
97
 
98
98
 
99
+ ## Configuration
100
+
101
+ Configuration is done directly on a matrix or via GoogleDistanceMatrix.default_configuration.
102
+ Apart from configuration on requests it is also possible to provide your own logger class and
103
+ set a cache.
104
+
105
+ ### Request cache
106
+
107
+ Given Google's limit to the service you may have the need to cache requests. This is done by simply
108
+ using URL as cache keys. Cache we'll accept should provide a default ActiveSupport::Chache::Store interface.
109
+
110
+ GoogleDistanceMatrix.default_configuration do |config|
111
+ config.cache = ActiveSupport::Cache.lookup_store :your_store, {
112
+ expires_in: 12.hours
113
+ # ..or other options you like for your store
114
+ }
115
+ end
116
+
117
+
99
118
 
100
119
  ## Contributing
101
120
 
@@ -11,6 +11,7 @@ require "google_distance_matrix/errors"
11
11
  require "google_distance_matrix/configuration"
12
12
  require "google_distance_matrix/url_builder"
13
13
  require "google_distance_matrix/client"
14
+ require "google_distance_matrix/client_cache"
14
15
  require "google_distance_matrix/routes_finder"
15
16
  require "google_distance_matrix/matrix"
16
17
  require "google_distance_matrix/places"
@@ -8,10 +8,11 @@ module GoogleDistanceMatrix
8
8
  UNKNOWN_ERROR
9
9
  ]
10
10
 
11
- def get(url)
11
+ def get(url, options = {})
12
12
  uri = URI.parse url
13
+ instrumentation = {url: url}.merge(options[:instrumentation] || {})
13
14
 
14
- response = ActiveSupport::Notifications.instrument "client_request_matrix_data.google_distance_matrix", url: url do
15
+ response = ActiveSupport::Notifications.instrument "client_request_matrix_data.google_distance_matrix", instrumentation do
15
16
  Net::HTTP.get_response uri
16
17
  end
17
18
 
@@ -23,12 +24,12 @@ module GoogleDistanceMatrix
23
24
  when Net::HTTPClientError
24
25
  fail ClientError.new response
25
26
  when Net::HTTPServerError
26
- fail RequestError.new response
27
+ fail ServerError.new response
27
28
  else # Handle this as a request error for now. Maybe fine tune this more later.
28
- fail RequestError.new response
29
+ fail ServerError.new response
29
30
  end
30
31
  rescue Timeout::Error => error
31
- fail RequestError.new error
32
+ fail ServerError.new error
32
33
  end
33
34
 
34
35
 
@@ -0,0 +1,20 @@
1
+ module GoogleDistanceMatrix
2
+ class ClientCache
3
+ attr_reader :client, :cache
4
+
5
+ def self.key(url)
6
+ url
7
+ end
8
+
9
+ def initialize(client, cache)
10
+ @client = client
11
+ @cache = cache
12
+ end
13
+
14
+ def get(url, options = {})
15
+ cache.fetch self.class.key(url) do
16
+ client.get url, options
17
+ end
18
+ end
19
+ end
20
+ end
@@ -18,6 +18,7 @@ module GoogleDistanceMatrix
18
18
 
19
19
  attr_accessor *ATTRIBUTES, :protocol, :logger, :lat_lng_scale
20
20
  attr_accessor :google_business_api_client_id, :google_business_api_private_key
21
+ attr_accessor :cache
21
22
 
22
23
 
23
24
  validates :sensor, inclusion: {in: [true, false]}
@@ -34,7 +34,7 @@ module GoogleDistanceMatrix
34
34
  # This includes wire errors like timeouts etc, and server errors
35
35
  # like 5xx. Inspect error_or_response for more information.
36
36
  #
37
- class RequestError < Error
37
+ class ServerError < Error
38
38
  attr_reader :error_or_response
39
39
 
40
40
  def initialize(error_or_response)
@@ -42,7 +42,7 @@ module GoogleDistanceMatrix
42
42
  end
43
43
 
44
44
  def to_s
45
- "GoogleDistanceMatrix::RequestError - #{error_or_response.inspect}."
45
+ "GoogleDistanceMatrix::ServerError - #{error_or_response.inspect}."
46
46
  end
47
47
  end
48
48
 
@@ -5,7 +5,7 @@ module GoogleDistanceMatrix
5
5
  end
6
6
 
7
7
  def client_request_matrix_data(event)
8
- logger.info "(#{event.duration}ms) GET #{event.payload[:url]}", tag: :client
8
+ logger.info "(#{event.duration}ms) (elements: #{event.payload[:elements]}) GET #{event.payload[:url]}", tag: :client
9
9
  end
10
10
  end
11
11
  end
@@ -68,10 +68,15 @@ module GoogleDistanceMatrix
68
68
  end
69
69
 
70
70
  def reload
71
+ clear_from_cache!
71
72
  @data = load_matrix
72
73
  self
73
74
  end
74
75
 
76
+ def reset!
77
+ @data = nil
78
+ end
79
+
75
80
  def loaded?
76
81
  @data.present?
77
82
  end
@@ -103,7 +108,7 @@ module GoogleDistanceMatrix
103
108
  end
104
109
 
105
110
  def load_matrix
106
- parsed = JSON.parse client.get(url).body
111
+ parsed = JSON.parse client.get(url, instrumentation: {elements: origins.length * destinations.length}).body
107
112
 
108
113
  parsed["rows"].each_with_index.map do |row, origin_index|
109
114
  origin = origins[origin_index]
@@ -116,7 +121,19 @@ module GoogleDistanceMatrix
116
121
  end
117
122
 
118
123
  def client
119
- @client ||= Client.new
124
+ client = Client.new
125
+
126
+ if configuration.cache
127
+ ClientCache.new client, configuration.cache
128
+ else
129
+ client
130
+ end
131
+ end
132
+
133
+ def clear_from_cache!
134
+ if configuration.cache
135
+ configuration.cache.delete ClientCache.key(url)
136
+ end
120
137
  end
121
138
  end
122
139
  end
@@ -72,20 +72,40 @@ module GoogleDistanceMatrix
72
72
  end
73
73
 
74
74
 
75
+ # Public: Finds shortes route by distance to a place.
76
+ #
77
+ # place - The place, or object place was built from, you want the shortest route to
78
+ #
79
+ # Returns shortest route, or nil if no routes had status ok
75
80
  def shortest_route_by_distance_to(place_or_object_place_was_built_from)
76
81
  routes = routes_for place_or_object_place_was_built_from
77
82
  select_ok_routes(routes).min_by &:distance_in_meters
78
83
  end
79
84
 
85
+ # Public: Finds shortes route by distance to a place.
86
+ #
87
+ # place - The place, or object place was built from, you want the shortest route to
88
+ #
89
+ # Returns shortest route, fails if any of the routes are not ok
80
90
  def shortest_route_by_distance_to!(place_or_object_place_was_built_from)
81
91
  routes_for!(place_or_object_place_was_built_from).min_by &:distance_in_meters
82
92
  end
83
93
 
94
+ # Public: Finds shortes route by duration to a place.
95
+ #
96
+ # place - The place, or object place was built from, you want the shortest route to
97
+ #
98
+ # Returns shortest route, or nil if no routes had status ok
84
99
  def shortest_route_by_duration_to(place_or_object_place_was_built_from)
85
100
  routes = routes_for place_or_object_place_was_built_from
86
101
  select_ok_routes(routes).min_by &:duration_in_seconds
87
102
  end
88
103
 
104
+ # Public: Finds shortes route by duration to a place.
105
+ #
106
+ # place - The place, or object place was built from, you want the shortest route to
107
+ #
108
+ # Returns shortest route, fails if any of the routes are not ok
89
109
  def shortest_route_by_duration_to!(place_or_object_place_was_built_from)
90
110
  routes_for!(place_or_object_place_was_built_from).min_by &:duration_in_seconds
91
111
  end
@@ -1,3 +1,3 @@
1
1
  module GoogleDistanceMatrix
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,25 @@
1
+ require "spec_helper"
2
+
3
+ describe GoogleDistanceMatrix::ClientCache do
4
+ let(:url) { "http://www.example.com" }
5
+ let(:options) { {hello: :options} }
6
+
7
+ let(:client) { mock get: "data" }
8
+ let(:cache) { mock }
9
+
10
+ subject { described_class.new client, cache }
11
+
12
+ describe "#get" do
13
+ it "returns from cache if it hits" do
14
+ cache.should_receive(:fetch).with(url).and_return "cached-data"
15
+ expect(subject.get(url, options)).to eq "cached-data"
16
+ end
17
+
18
+ it "asks client when cache miss" do
19
+ client.should_receive(:get).with(url, options).and_return "api-data"
20
+ cache.should_receive(:fetch) { |&block| block.call }
21
+
22
+ expect(subject.get(url, options)).to eq "api-data"
23
+ end
24
+ end
25
+ end
@@ -44,7 +44,7 @@ describe GoogleDistanceMatrix::Client, :request_recordings do
44
44
  before { stub_request(:get, url).to_return status: [500, "Internal Server Error"] }
45
45
 
46
46
  it "wraps the error http response" do
47
- expect { subject.get(url_builder.url) }.to raise_error GoogleDistanceMatrix::RequestError
47
+ expect { subject.get(url_builder.url) }.to raise_error GoogleDistanceMatrix::ServerError
48
48
  end
49
49
  end
50
50
 
@@ -52,7 +52,7 @@ describe GoogleDistanceMatrix::Client, :request_recordings do
52
52
  before { stub_request(:get, url).to_timeout }
53
53
 
54
54
  it "wraps the error from Net::HTTP" do
55
- expect { subject.get(url_builder.url).body }.to raise_error GoogleDistanceMatrix::RequestError
55
+ expect { subject.get(url_builder.url).body }.to raise_error GoogleDistanceMatrix::ServerError
56
56
  end
57
57
  end
58
58
 
@@ -60,7 +60,7 @@ describe GoogleDistanceMatrix::Client, :request_recordings do
60
60
  before { stub_request(:get, url).to_return status: [999, "Unknown"] }
61
61
 
62
62
  it "wraps the error http response" do
63
- expect { subject.get(url_builder.url) }.to raise_error GoogleDistanceMatrix::RequestError
63
+ expect { subject.get(url_builder.url) }.to raise_error GoogleDistanceMatrix::ServerError
64
64
  end
65
65
  end
66
66
  end
@@ -31,6 +31,7 @@ describe GoogleDistanceMatrix::Configuration do
31
31
  its(:google_business_api_private_key) { should be_nil }
32
32
 
33
33
  its(:logger) { should be_nil }
34
+ its(:cache) { should be_nil }
34
35
  end
35
36
 
36
37
 
@@ -93,6 +93,50 @@ describe GoogleDistanceMatrix::Matrix do
93
93
  end
94
94
  end
95
95
 
96
+ describe "making API requests", :request_recordings do
97
+ it "loads correctly" do
98
+ stub_request(:get, url).to_return body: recorded_request_for(:success)
99
+ expect(subject.data[0][0].distance_in_meters).to eq 2032
100
+ end
101
+
102
+ context "no cache" do
103
+ it "makes multiple requests to same url" do
104
+ stub = stub_request(:get, url).to_return body: recorded_request_for(:success)
105
+ subject.data
106
+ subject.reset!
107
+ subject.data
108
+
109
+ stub.should have_been_requested.twice
110
+ end
111
+ end
112
+
113
+ context "with cache" do
114
+ before do
115
+ subject.configure do |config|
116
+ config.cache = ActiveSupport::Cache.lookup_store :memory_store
117
+ end
118
+ end
119
+
120
+ it "makes one requests to same url" do
121
+ stub = stub_request(:get, url).to_return body: recorded_request_for(:success)
122
+ subject.data
123
+ subject.reset!
124
+ subject.data
125
+
126
+ stub.should have_been_requested.once
127
+ end
128
+
129
+ it "clears the cache key on reload" do
130
+ stub = stub_request(:get, url).to_return body: recorded_request_for(:success)
131
+ subject.data
132
+ subject.reload
133
+ subject.data
134
+
135
+ stub.should have_been_requested.twice
136
+ end
137
+ end
138
+ end
139
+
96
140
  describe "#data", :request_recordings do
97
141
  context "success" do
98
142
  let!(:api_request_stub) { stub_request(:get, url).to_return body: recorded_request_for(:success) }
metadata CHANGED
@@ -2,25 +2,25 @@
2
2
  name: google_distance_matrix
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.1
5
+ version: 0.0.2
6
6
  platform: ruby
7
7
  authors:
8
8
  - Thorbjørn Hermansen
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-16 00:00:00.000000000 Z
12
+ date: 2013-08-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- prerelease: false
16
- name: activesupport
17
- type: :runtime
18
15
  version_requirements: !ruby/object:Gem::Requirement
19
16
  requirements:
20
17
  - - ~>
21
18
  - !ruby/object:Gem::Version
22
19
  version: 3.2.13
23
20
  none: false
21
+ name: activesupport
22
+ type: :runtime
23
+ prerelease: false
24
24
  requirement: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - ~>
@@ -28,15 +28,15 @@ dependencies:
28
28
  version: 3.2.13
29
29
  none: false
30
30
  - !ruby/object:Gem::Dependency
31
- prerelease: false
32
- name: activemodel
33
- type: :runtime
34
31
  version_requirements: !ruby/object:Gem::Requirement
35
32
  requirements:
36
33
  - - ~>
37
34
  - !ruby/object:Gem::Version
38
35
  version: 3.2.13
39
36
  none: false
37
+ name: activemodel
38
+ type: :runtime
39
+ prerelease: false
40
40
  requirement: !ruby/object:Gem::Requirement
41
41
  requirements:
42
42
  - - ~>
@@ -44,15 +44,15 @@ dependencies:
44
44
  version: 3.2.13
45
45
  none: false
46
46
  - !ruby/object:Gem::Dependency
47
- prerelease: false
48
- name: google_business_api_url_signer
49
- type: :runtime
50
47
  version_requirements: !ruby/object:Gem::Requirement
51
48
  requirements:
52
49
  - - ~>
53
50
  - !ruby/object:Gem::Version
54
51
  version: 0.0.1
55
52
  none: false
53
+ name: google_business_api_url_signer
54
+ type: :runtime
55
+ prerelease: false
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - ~>
@@ -60,15 +60,15 @@ dependencies:
60
60
  version: 0.0.1
61
61
  none: false
62
62
  - !ruby/object:Gem::Dependency
63
- prerelease: false
64
- name: bundler
65
- type: :development
66
63
  version_requirements: !ruby/object:Gem::Requirement
67
64
  requirements:
68
65
  - - ~>
69
66
  - !ruby/object:Gem::Version
70
67
  version: '1.3'
71
68
  none: false
69
+ name: bundler
70
+ type: :development
71
+ prerelease: false
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - ~>
@@ -76,15 +76,15 @@ dependencies:
76
76
  version: '1.3'
77
77
  none: false
78
78
  - !ruby/object:Gem::Dependency
79
- prerelease: false
80
- name: rspec
81
- type: :development
82
79
  version_requirements: !ruby/object:Gem::Requirement
83
80
  requirements:
84
81
  - - ~>
85
82
  - !ruby/object:Gem::Version
86
83
  version: '2.13'
87
84
  none: false
85
+ name: rspec
86
+ type: :development
87
+ prerelease: false
88
88
  requirement: !ruby/object:Gem::Requirement
89
89
  requirements:
90
90
  - - ~>
@@ -92,15 +92,15 @@ dependencies:
92
92
  version: '2.13'
93
93
  none: false
94
94
  - !ruby/object:Gem::Dependency
95
- prerelease: false
96
- name: shoulda-matchers
97
- type: :development
98
95
  version_requirements: !ruby/object:Gem::Requirement
99
96
  requirements:
100
97
  - - ~>
101
98
  - !ruby/object:Gem::Version
102
99
  version: 2.0.0
103
100
  none: false
101
+ name: shoulda-matchers
102
+ type: :development
103
+ prerelease: false
104
104
  requirement: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - ~>
@@ -108,15 +108,15 @@ dependencies:
108
108
  version: 2.0.0
109
109
  none: false
110
110
  - !ruby/object:Gem::Dependency
111
- prerelease: false
112
- name: webmock
113
- type: :development
114
111
  version_requirements: !ruby/object:Gem::Requirement
115
112
  requirements:
116
113
  - - ~>
117
114
  - !ruby/object:Gem::Version
118
115
  version: '1.11'
119
116
  none: false
117
+ name: webmock
118
+ type: :development
119
+ prerelease: false
120
120
  requirement: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - ~>
@@ -124,15 +124,15 @@ dependencies:
124
124
  version: '1.11'
125
125
  none: false
126
126
  - !ruby/object:Gem::Dependency
127
- prerelease: false
128
- name: rake
129
- type: :development
130
127
  version_requirements: !ruby/object:Gem::Requirement
131
128
  requirements:
132
129
  - - ! '>='
133
130
  - !ruby/object:Gem::Version
134
131
  version: '0'
135
132
  none: false
133
+ name: rake
134
+ type: :development
135
+ prerelease: false
136
136
  requirement: !ruby/object:Gem::Requirement
137
137
  requirements:
138
138
  - - ! '>='
@@ -148,6 +148,7 @@ extra_rdoc_files: []
148
148
  files:
149
149
  - .gitignore
150
150
  - .rbenv-version
151
+ - CHANGELOG.md
151
152
  - Gemfile
152
153
  - LICENSE.txt
153
154
  - README.md
@@ -155,6 +156,7 @@ files:
155
156
  - google_distance_matrix.gemspec
156
157
  - lib/google_distance_matrix.rb
157
158
  - lib/google_distance_matrix/client.rb
159
+ - lib/google_distance_matrix/client_cache.rb
158
160
  - lib/google_distance_matrix/configuration.rb
159
161
  - lib/google_distance_matrix/errors.rb
160
162
  - lib/google_distance_matrix/log_subscriber.rb
@@ -167,6 +169,7 @@ files:
167
169
  - lib/google_distance_matrix/routes_finder.rb
168
170
  - lib/google_distance_matrix/url_builder.rb
169
171
  - lib/google_distance_matrix/version.rb
172
+ - spec/lib/google_distance_matrix/client_cache_spec.rb
170
173
  - spec/lib/google_distance_matrix/client_spec.rb
171
174
  - spec/lib/google_distance_matrix/configuration_spec.rb
172
175
  - spec/lib/google_distance_matrix/logger_spec.rb
@@ -190,18 +193,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
190
193
  requirements:
191
194
  - - ! '>='
192
195
  - !ruby/object:Gem::Version
193
- segments:
194
- - 0
195
- hash: -254032637546983924
196
196
  version: '0'
197
197
  none: false
198
198
  required_rubygems_version: !ruby/object:Gem::Requirement
199
199
  requirements:
200
200
  - - ! '>='
201
201
  - !ruby/object:Gem::Version
202
- segments:
203
- - 0
204
- hash: -254032637546983924
205
202
  version: '0'
206
203
  none: false
207
204
  requirements: []
@@ -211,6 +208,7 @@ signing_key:
211
208
  specification_version: 3
212
209
  summary: Ruby client for The Google Distance Matrix API
213
210
  test_files:
211
+ - spec/lib/google_distance_matrix/client_cache_spec.rb
214
212
  - spec/lib/google_distance_matrix/client_spec.rb
215
213
  - spec/lib/google_distance_matrix/configuration_spec.rb
216
214
  - spec/lib/google_distance_matrix/logger_spec.rb