puffing-billy 0.2.1 → 0.2.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 +7 -0
- data/.gitignore +1 -0
- data/Gemfile.lock +78 -66
- data/README.md +111 -6
- data/lib/billy.rb +6 -4
- data/lib/billy/cache.rb +71 -38
- data/lib/billy/config.rb +10 -2
- data/lib/billy/json_utils.rb +40 -0
- data/lib/billy/proxy.rb +4 -2
- data/lib/billy/proxy_connection.rb +85 -19
- data/lib/billy/proxy_request_stub.rb +1 -1
- data/lib/billy/railtie.rb +9 -0
- data/lib/billy/rspec.rb +0 -1
- data/lib/billy/version.rb +1 -1
- data/lib/puffing-billy.rb +2 -0
- data/lib/puffing-billy/rspec.rb +2 -0
- data/lib/tasks/billy.rake +87 -0
- data/puffing-billy.gemspec +2 -2
- data/spec/features/examples/facebook_api_spec.rb +2 -1
- data/spec/features/examples/tumblr_api_spec.rb +4 -4
- data/spec/lib/billy/cache_spec.rb +37 -0
- data/spec/lib/billy/proxy_request_stub_spec.rb +42 -32
- data/spec/lib/billy/resource_utils_spec.rb +55 -0
- data/spec/lib/proxy_spec.rb +233 -45
- data/spec/support/test_server.rb +44 -28
- metadata +55 -82
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 77894c8ea71e9f2b91cb3aae48c6bc1311ab45ba
|
4
|
+
data.tar.gz: 2af9cc1a8fafdfafa15ac9bf1d088e2cb648ae59
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 00faa0d3c37550847f947b5440770e628978593476771c27f26d31516b8ff176540340a00cba098b2fca5ebcd86bc53b29a94d53af57c4b92547be3a3b07f603
|
7
|
+
data.tar.gz: 642297885633e8f29efab30ad78faef48d173dd4b4a902cb92cbef118c616b349a01671d27f5b3b38af8b8ad4b341f251d0d23fdff9e1b7b3096b118efd246a6
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
puffing-billy (0.2.
|
4
|
+
puffing-billy (0.2.3)
|
5
5
|
capybara
|
6
6
|
em-http-request
|
7
7
|
eventmachine
|
@@ -12,108 +12,120 @@ PATH
|
|
12
12
|
GEM
|
13
13
|
remote: https://rubygems.org/
|
14
14
|
specs:
|
15
|
-
addressable (2.3.
|
16
|
-
builder (3.2.
|
17
|
-
capybara (2.0
|
15
|
+
addressable (2.3.5)
|
16
|
+
builder (3.2.2)
|
17
|
+
capybara (2.1.0)
|
18
18
|
mime-types (>= 1.16)
|
19
19
|
nokogiri (>= 1.3.3)
|
20
20
|
rack (>= 1.0.0)
|
21
21
|
rack-test (>= 0.5.4)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
capybara (~> 2.0, >= 2.0.2)
|
22
|
+
xpath (~> 2.0)
|
23
|
+
capybara-webkit (1.1.1)
|
24
|
+
capybara (>= 2.0.2, < 2.2.0)
|
26
25
|
json
|
27
|
-
|
26
|
+
celluloid (0.15.2)
|
27
|
+
timers (~> 1.1.0)
|
28
|
+
childprocess (0.4.0)
|
28
29
|
ffi (~> 1.0, >= 1.0.11)
|
29
|
-
|
30
|
+
cliver (0.3.2)
|
31
|
+
coderay (1.1.0)
|
30
32
|
cookiejar (0.3.0)
|
31
|
-
cucumber (1.
|
33
|
+
cucumber (1.3.10)
|
32
34
|
builder (>= 2.1.2)
|
33
35
|
diff-lcs (>= 1.1.3)
|
34
|
-
gherkin (~> 2.
|
35
|
-
multi_json (
|
36
|
+
gherkin (~> 2.12)
|
37
|
+
multi_json (>= 1.7.5, < 2.0)
|
38
|
+
multi_test (>= 0.0.2)
|
36
39
|
daemons (1.1.9)
|
37
|
-
diff-lcs (1.2.
|
38
|
-
em-http-request (1.
|
39
|
-
addressable (>= 2.
|
40
|
+
diff-lcs (1.2.5)
|
41
|
+
em-http-request (1.1.2)
|
42
|
+
addressable (>= 2.3.4)
|
40
43
|
cookiejar
|
41
|
-
em-socksify
|
42
|
-
eventmachine (>= 1.0.
|
43
|
-
http_parser.rb (>= 0.
|
44
|
-
em-socksify (0.
|
44
|
+
em-socksify (>= 0.3)
|
45
|
+
eventmachine (>= 1.0.3)
|
46
|
+
http_parser.rb (>= 0.6.0)
|
47
|
+
em-socksify (0.3.0)
|
45
48
|
eventmachine (>= 1.0.0.beta.4)
|
46
|
-
eventmachine (1.0.
|
49
|
+
eventmachine (1.0.3)
|
47
50
|
eventmachine_httpserver (0.2.1)
|
48
|
-
faraday (0.
|
49
|
-
multipart-post (
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
gherkin (2.11.8)
|
51
|
+
faraday (0.9.0)
|
52
|
+
multipart-post (>= 1.2, < 3)
|
53
|
+
ffi (1.9.3)
|
54
|
+
formatador (0.2.4)
|
55
|
+
gherkin (2.12.2)
|
54
56
|
multi_json (~> 1.3)
|
55
|
-
guard (
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
thor (>= 0.
|
61
|
-
http_parser.rb (0.
|
62
|
-
json (1.
|
63
|
-
listen (
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
57
|
+
guard (2.4.0)
|
58
|
+
formatador (>= 0.2.4)
|
59
|
+
listen (~> 2.1)
|
60
|
+
lumberjack (~> 1.0)
|
61
|
+
pry (>= 0.9.12)
|
62
|
+
thor (>= 0.18.1)
|
63
|
+
http_parser.rb (0.6.0)
|
64
|
+
json (1.8.1)
|
65
|
+
listen (2.4.1)
|
66
|
+
celluloid (>= 0.15.2)
|
67
|
+
rb-fsevent (>= 0.9.3)
|
68
|
+
rb-inotify (>= 0.9)
|
69
|
+
lumberjack (1.0.4)
|
70
|
+
method_source (0.8.2)
|
71
|
+
mime-types (2.1)
|
72
|
+
mini_portile (0.5.2)
|
73
|
+
multi_json (1.8.4)
|
74
|
+
multi_test (0.0.3)
|
75
|
+
multipart-post (2.0.0)
|
76
|
+
nokogiri (1.6.1)
|
77
|
+
mini_portile (~> 0.5.0)
|
78
|
+
poltergeist (1.5.0)
|
79
|
+
capybara (~> 2.1)
|
80
|
+
cliver (~> 0.3.1)
|
81
|
+
multi_json (~> 1.0)
|
82
|
+
websocket-driver (>= 0.2.0)
|
83
|
+
pry (0.9.12.6)
|
84
|
+
coderay (~> 1.0)
|
76
85
|
method_source (~> 0.8)
|
77
86
|
slop (~> 3.4)
|
78
87
|
rack (1.5.2)
|
79
88
|
rack-test (0.6.2)
|
80
89
|
rack (>= 1.0)
|
81
|
-
rb-
|
90
|
+
rb-fsevent (0.9.4)
|
91
|
+
rb-inotify (0.9.3)
|
82
92
|
ffi (>= 0.5.0)
|
83
|
-
rspec (2.
|
84
|
-
rspec-core (~> 2.
|
85
|
-
rspec-expectations (~> 2.
|
86
|
-
rspec-mocks (~> 2.
|
87
|
-
rspec-core (2.
|
88
|
-
rspec-expectations (2.
|
93
|
+
rspec (2.14.1)
|
94
|
+
rspec-core (~> 2.14.0)
|
95
|
+
rspec-expectations (~> 2.14.0)
|
96
|
+
rspec-mocks (~> 2.14.0)
|
97
|
+
rspec-core (2.14.7)
|
98
|
+
rspec-expectations (2.14.5)
|
89
99
|
diff-lcs (>= 1.1.3, < 2.0)
|
90
|
-
rspec-mocks (2.
|
91
|
-
rubyzip (
|
92
|
-
selenium-webdriver (2.
|
100
|
+
rspec-mocks (2.14.5)
|
101
|
+
rubyzip (1.1.0)
|
102
|
+
selenium-webdriver (2.39.0)
|
93
103
|
childprocess (>= 0.2.5)
|
94
104
|
multi_json (~> 1.0)
|
95
|
-
rubyzip
|
105
|
+
rubyzip (~> 1.0)
|
96
106
|
websocket (~> 1.0.4)
|
97
|
-
slop (3.4.
|
98
|
-
|
99
|
-
thin (1.5.0)
|
107
|
+
slop (3.4.7)
|
108
|
+
thin (1.6.1)
|
100
109
|
daemons (>= 1.0.9)
|
101
|
-
eventmachine (>= 0.
|
110
|
+
eventmachine (>= 1.0.0)
|
102
111
|
rack (>= 1.0.0)
|
103
|
-
thor (0.
|
112
|
+
thor (0.18.1)
|
113
|
+
timers (1.1.0)
|
104
114
|
websocket (1.0.7)
|
105
|
-
|
115
|
+
websocket-driver (0.3.2)
|
116
|
+
xpath (2.0.0)
|
106
117
|
nokogiri (~> 1.3)
|
107
118
|
|
108
119
|
PLATFORMS
|
109
120
|
ruby
|
110
121
|
|
111
122
|
DEPENDENCIES
|
112
|
-
capybara-webkit
|
123
|
+
capybara-webkit (~> 1.0)
|
113
124
|
cucumber
|
114
125
|
faraday
|
115
126
|
guard
|
116
127
|
poltergeist
|
128
|
+
pry
|
117
129
|
puffing-billy!
|
118
130
|
rack
|
119
131
|
rb-inotify
|
data/README.md
CHANGED
@@ -57,6 +57,10 @@ Capybara.javascript_driver = :selenium_billy
|
|
57
57
|
# Capybara.javascript_driver = :poltergeist_billy
|
58
58
|
```
|
59
59
|
|
60
|
+
Note: :poltergeist_billy doesn't support proxying any localhosts, so you must use
|
61
|
+
:webkit_billy for headless specs when using puffing-billy for other local rack apps.
|
62
|
+
See [this phantomjs issue](https://github.com/ariya/phantomjs/issues/11342) for any updates.
|
63
|
+
|
60
64
|
In your tests:
|
61
65
|
|
62
66
|
```ruby
|
@@ -150,6 +154,16 @@ Billy.configure do |c|
|
|
150
154
|
end
|
151
155
|
```
|
152
156
|
|
157
|
+
If you would like to cache other local rack apps, you must whitelist only the
|
158
|
+
specific port for the application that is executing tests. If you are using
|
159
|
+
[Capybara](https://github.com/jnicklas/capybara), this can be accomplished by
|
160
|
+
adding this in your `spec_helper.rb`:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
server = Capybara.current_session.server
|
164
|
+
Billy.config.whitelist = ["#{server.host}:#{server.port}"]
|
165
|
+
```
|
166
|
+
|
153
167
|
If you want to use puffing-billy like you would [VCR](https://github.com/vcr/vcr)
|
154
168
|
you can turn on cache persistence. This way you don't have to manually mock out
|
155
169
|
everything as requests are automatically recorded and played back. With cache
|
@@ -158,6 +172,7 @@ persistence you can take tests completely offline.
|
|
158
172
|
```ruby
|
159
173
|
Billy.configure do |c|
|
160
174
|
c.cache = true
|
175
|
+
c.cache_request_headers = false
|
161
176
|
c.ignore_params = ["http://www.google-analytics.com/__utm.gif",
|
162
177
|
"https://r.twimg.com/jot",
|
163
178
|
"http://p.twitter.com/t.gif",
|
@@ -165,22 +180,112 @@ Billy.configure do |c|
|
|
165
180
|
"http://www.facebook.com/plugins/like.php",
|
166
181
|
"https://www.facebook.com/dialog/oauth",
|
167
182
|
"http://cdn.api.twitter.com/1/urls/count.json"]
|
183
|
+
c.path_blacklist = []
|
168
184
|
c.persist_cache = true
|
185
|
+
c.ignore_cache_port = true # defaults to true
|
186
|
+
c.non_successful_cache_disabled = false
|
187
|
+
c.non_successful_error_level = :warn
|
188
|
+
c.non_whitelisted_requests_disabled = false
|
169
189
|
c.cache_path = 'spec/req_cache/'
|
170
190
|
end
|
171
|
-
|
172
|
-
# need to call this because of a race condition between persist_cache
|
173
|
-
# being set and the proxy being loaded for the first time
|
174
|
-
Billy.proxy.restore_cache
|
175
191
|
```
|
176
192
|
|
193
|
+
The cache works with all types of requests and will distinguish between
|
194
|
+
different POST requests to the same URL.
|
195
|
+
|
196
|
+
`c.cache_request_headers` is used to store the outgoing request headers in the cache.
|
197
|
+
It is also saved to yml if `persist_cache` is enabled. This additional information
|
198
|
+
is useful for debugging (for example: viewing the referer of the request).
|
199
|
+
|
177
200
|
`c.ignore_params` is used to ignore parameters of certain requests when
|
178
201
|
caching. You should mostly use this for analytics and various social buttons as
|
179
202
|
they use cache avoidance techniques, but return practically the same response
|
180
203
|
that most often does not affect your test results.
|
181
204
|
|
182
|
-
|
183
|
-
|
205
|
+
`c.path_blacklist = []` is used to always cache specific paths on any hostnames,
|
206
|
+
including whitelisted ones. This is useful if your AUT has routes that get data
|
207
|
+
from external services, such as `/api` where the ajax request is a local URL but
|
208
|
+
the actual data is coming from a different application that you want to cache.
|
209
|
+
|
210
|
+
`c.ignore_cache_port` is used to strip the port from the URL if it exists. This
|
211
|
+
is useful when caching local paths (via `path_blacklist`) or other local rack apps
|
212
|
+
that are running on random ports.
|
213
|
+
|
214
|
+
`c.non_successful_cache_disabled` is used to not cache responses without 200-series
|
215
|
+
or 304 status codes. This prevents unauthorized or internal server errors from
|
216
|
+
being cached and used for future test runs.
|
217
|
+
|
218
|
+
`c.non_successful_error_level` is used to log when non-successful resposnes are
|
219
|
+
received. By default, it just writes to the log file, but when set to `:error`
|
220
|
+
it throws an error with the URL and status code received for easier debugging.
|
221
|
+
|
222
|
+
`c.non_whitelisted_requests_disabled` is used to disable hitting new URLs when
|
223
|
+
no cache file exists. Only whitelisted URLs (on non-blacklisted paths) are
|
224
|
+
allowed, all others will throw an error with the URL attempted to be accessed.
|
225
|
+
This is useful for debugging issues in isolated environments (ie.
|
226
|
+
continuous integration).
|
227
|
+
|
228
|
+
### Cache Scopes
|
229
|
+
|
230
|
+
If you need to cache different responses to the same HTTP request, you can use
|
231
|
+
cache scoping.
|
232
|
+
|
233
|
+
For example, an index page may return zero or more items in a list, with or
|
234
|
+
without pagination, depending on the number of entries in a database.
|
235
|
+
|
236
|
+
There are a few different ways to use cache scopes:
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
# If you do nothing, it uses the default cache scope:
|
240
|
+
it 'defaults to nil scope' do
|
241
|
+
expect(proxy.cache.scope).to be_nil
|
242
|
+
end
|
243
|
+
|
244
|
+
# You can change context indefinitely to a specific cache scope:
|
245
|
+
context 'with a cache scope' do
|
246
|
+
before do
|
247
|
+
proxy.cache.scope_to "my_cache"
|
248
|
+
end
|
249
|
+
|
250
|
+
# Remember to set the cache scope back to the default in an after block
|
251
|
+
# within the context it is used, and/or at the global spec_helper level!
|
252
|
+
after do
|
253
|
+
proxy.cache.use_default_scope
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'uses the cache scope' do
|
257
|
+
expect(proxy.cache.scope).to eq("my_cache")
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'can be reset to the default scope' do
|
261
|
+
proxy.cache.use_default_scope
|
262
|
+
expect(proxy.cache.scope).to be_nil
|
263
|
+
end
|
264
|
+
|
265
|
+
# Or you can run a block within the context of a cache scope:
|
266
|
+
# Note: When using scope blocks, be sure that both the action that triggers a
|
267
|
+
# request and the assertion that a response has been received are within the block
|
268
|
+
it 'can execute a block against a named cache' do
|
269
|
+
expect(proxy.cache.scope).to eq("my_cache")
|
270
|
+
proxy.cache.with_scope "another_cache" do
|
271
|
+
expect(proxy.cache.scope).to eq "another_cache"
|
272
|
+
end
|
273
|
+
# It
|
274
|
+
expect(proxy.cache.scope).to eq("my_cache")
|
275
|
+
end
|
276
|
+
end
|
277
|
+
```
|
278
|
+
|
279
|
+
If you use named caches it is highly recommend that you use a global
|
280
|
+
hook to set the cache back to the default before or after each test.
|
281
|
+
|
282
|
+
In Rspec:
|
283
|
+
|
284
|
+
```ruby
|
285
|
+
RSpec.configure do |config|
|
286
|
+
config.before :each { proxy.cache.use_default_scope }
|
287
|
+
end
|
288
|
+
```
|
184
289
|
|
185
290
|
## Customising the javascript driver
|
186
291
|
|
data/lib/billy.rb
CHANGED
@@ -4,13 +4,15 @@ require "billy/proxy_request_stub"
|
|
4
4
|
require "billy/cache"
|
5
5
|
require "billy/proxy"
|
6
6
|
require "billy/proxy_connection"
|
7
|
-
|
8
|
-
$billy_proxy = Billy::Proxy.new
|
9
|
-
$billy_proxy.start
|
7
|
+
require "billy/railtie" if defined?(Rails)
|
10
8
|
|
11
9
|
module Billy
|
12
10
|
def self.proxy
|
13
|
-
|
11
|
+
@billy_proxy ||= (
|
12
|
+
proxy = Billy::Proxy.new
|
13
|
+
proxy.start
|
14
|
+
proxy
|
15
|
+
)
|
14
16
|
end
|
15
17
|
|
16
18
|
def self.register_drivers
|
data/lib/billy/cache.rb
CHANGED
@@ -1,49 +1,61 @@
|
|
1
1
|
require 'resolv'
|
2
2
|
require 'uri'
|
3
3
|
require 'yaml'
|
4
|
+
require 'billy/json_utils'
|
4
5
|
|
5
6
|
module Billy
|
6
7
|
class Cache
|
8
|
+
attr_reader :scope
|
9
|
+
|
7
10
|
def initialize
|
8
11
|
reset
|
9
|
-
load_dir
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# TODO test headers for cacheability
|
17
|
-
end
|
14
|
+
def cached?(method, url, body)
|
15
|
+
# Only log the key the first time it's looked up (in this method)
|
16
|
+
key = key(method, url, body, true)
|
17
|
+
!@cache[key].nil? or persisted?(key)
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
|
20
|
+
def persisted?(key)
|
21
|
+
Billy.config.persist_cache and File.exists?(cache_file(key))
|
22
22
|
end
|
23
23
|
|
24
24
|
def fetch(method, url, body)
|
25
|
-
|
25
|
+
key = key(method, url, body)
|
26
|
+
@cache[key] or fetch_from_persistence(key)
|
26
27
|
end
|
27
28
|
|
28
|
-
def
|
29
|
+
def fetch_from_persistence(key)
|
30
|
+
begin
|
31
|
+
@cache[key] = YAML.load(File.open(cache_file(key))) if persisted?(key)
|
32
|
+
rescue ArgumentError => e
|
33
|
+
puts "Could not parse YAML: #{e.message}"
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def store(method, url, request_headers, body, response_headers, status, content)
|
29
39
|
cached = {
|
30
|
-
:
|
40
|
+
:scope => scope,
|
41
|
+
:url => format_url(url),
|
31
42
|
:body => body,
|
32
43
|
:status => status,
|
33
44
|
:method => method,
|
34
|
-
:headers =>
|
45
|
+
:headers => response_headers,
|
35
46
|
:content => content
|
36
47
|
}
|
37
48
|
|
38
|
-
|
49
|
+
cached.merge!({:request_headers => request_headers}) if Billy.config.cache_request_headers
|
50
|
+
|
51
|
+
key = key(method, url, body)
|
52
|
+
@cache[key] = cached
|
39
53
|
|
40
54
|
if Billy.config.persist_cache
|
41
55
|
Dir.mkdir(Billy.config.cache_path) unless File.exists?(Billy.config.cache_path)
|
42
56
|
|
43
57
|
begin
|
44
|
-
|
45
|
-
"#{key(method, url, body)}.yml")
|
46
|
-
File.open(path, 'w') do |f|
|
58
|
+
File.open(cache_file(key), 'w') do |f|
|
47
59
|
f.write(cached.to_yaml(:Encoding => :Utf8))
|
48
60
|
end
|
49
61
|
rescue StandardError => e
|
@@ -55,35 +67,56 @@ module Billy
|
|
55
67
|
@cache = {}
|
56
68
|
end
|
57
69
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
}
|
70
|
+
def key(method, orig_url, body, log_key = false)
|
71
|
+
ignore_params = Billy.config.ignore_params.include?(format_url(orig_url, true))
|
72
|
+
url = URI(format_url(orig_url, ignore_params))
|
73
|
+
key = method+'_'+url.host+'_'+Digest::SHA1.hexdigest(scope.to_s + url.to_s)
|
74
|
+
body_msg = ''
|
75
|
+
|
76
|
+
if method == 'post' and !ignore_params
|
77
|
+
body_formatted = JSONUtils::json?(body.to_s) ? JSONUtils::sort_json(body.to_s) : body.to_s
|
78
|
+
body_msg = " with body '#{body_formatted}'"
|
79
|
+
key += '_'+Digest::SHA1.hexdigest(body_formatted)
|
69
80
|
end
|
81
|
+
|
82
|
+
Billy.log(:info, "puffing-billy: CACHE KEY for '#{orig_url}#{body_msg}' is '#{key}'") if log_key
|
83
|
+
key
|
70
84
|
end
|
71
85
|
|
72
|
-
def
|
86
|
+
def format_url(url, ignore_params=false)
|
73
87
|
url = URI(url)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
url
|
88
|
+
port_to_include = Billy.config.ignore_cache_port ? '' : ":#{url.port}"
|
89
|
+
formatted_url = url.scheme+'://'+url.host+port_to_include+url.path
|
90
|
+
unless ignore_params
|
91
|
+
formatted_url += '?'+url.query if url.query
|
92
|
+
formatted_url += '#'+url.fragment if url.fragment
|
78
93
|
end
|
94
|
+
formatted_url
|
95
|
+
end
|
79
96
|
|
80
|
-
|
97
|
+
def cache_file(key)
|
98
|
+
File.join(Billy.config.cache_path, "#{key}.yml")
|
99
|
+
end
|
81
100
|
|
82
|
-
|
83
|
-
|
84
|
-
|
101
|
+
def scope_to(new_scope = nil)
|
102
|
+
self.scope = new_scope
|
103
|
+
end
|
85
104
|
|
86
|
-
|
105
|
+
def with_scope(use_scope = nil, &block)
|
106
|
+
raise ArgumentError, 'Expected a block but none was received.' if block.nil?
|
107
|
+
original_scope = scope
|
108
|
+
scope_to use_scope
|
109
|
+
block.call()
|
110
|
+
ensure
|
111
|
+
scope_to original_scope
|
112
|
+
end
|
113
|
+
|
114
|
+
def use_default_scope
|
115
|
+
scope_to nil
|
87
116
|
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
attr_writer :scope
|
88
121
|
end
|
89
122
|
end
|