jd-paperclip-azure 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.autotest +25 -0
- data/.github/workflows/tests.yml +22 -0
- data/.gitignore +58 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/History.txt +6 -0
- data/Manifest.txt +9 -0
- data/README.md +138 -0
- data/Rakefile +29 -0
- data/lib/paperclip/azure.rb +5 -0
- data/lib/paperclip/storage/azure/environment.rb +19 -0
- data/lib/paperclip/storage/azure.rb +271 -0
- data/lib/paperclip-azure.rb +1 -0
- data/paperclip-azure.gemspec +33 -0
- data/spec/database.yml +3 -0
- data/spec/paperclip/storage/azure/environment_spec.rb +26 -0
- data/spec/paperclip/storage/azure_spec.rb +426 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/support/fake_model.rb +25 -0
- data/spec/support/fake_rails.rb +12 -0
- data/spec/support/fixtures/12k.png +0 -0
- data/spec/support/fixtures/50x50.png +0 -0
- data/spec/support/fixtures/5k.png +0 -0
- data/spec/support/fixtures/animated +0 -0
- data/spec/support/fixtures/animated.gif +0 -0
- data/spec/support/fixtures/animated.unknown +0 -0
- data/spec/support/fixtures/azure.yml +8 -0
- data/spec/support/fixtures/bad.png +1 -0
- data/spec/support/fixtures/empty.html +1 -0
- data/spec/support/fixtures/empty.xlsx +0 -0
- data/spec/support/fixtures/fog.yml +8 -0
- data/spec/support/fixtures/rotated.jpg +0 -0
- data/spec/support/fixtures/spaced file.jpg +0 -0
- data/spec/support/fixtures/spaced file.png +0 -0
- data/spec/support/fixtures/text.txt +1 -0
- data/spec/support/fixtures/twopage.pdf +0 -0
- data/spec/support/fixtures/uppercase.PNG +0 -0
- data/spec/support/mock_attachment.rb +24 -0
- data/spec/support/mock_interpolator.rb +24 -0
- data/spec/support/mock_url_generator_builder.rb +27 -0
- data/spec/support/model_reconstruction.rb +68 -0
- data/spec/support/test_data.rb +13 -0
- data/spec/support/version_helper.rb +9 -0
- metadata +256 -0
@@ -0,0 +1,426 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "base64"
|
3
|
+
|
4
|
+
describe Paperclip::Storage::Azure do
|
5
|
+
let(:storage_access_key) { 'kiaY4+GkLMVxnfOK2X+eCJOE06J8QtHC6XNuXVwt8Pp4kMezYaa7cNjtYnZr4/b732RKdz5pZwl8RN9yb8gBCg==' }
|
6
|
+
|
7
|
+
describe "#parse_credentials" do
|
8
|
+
let(:credentials) {{
|
9
|
+
'production' => {key: '12345'},
|
10
|
+
development: {key: "54321"}
|
11
|
+
}}
|
12
|
+
|
13
|
+
before do
|
14
|
+
@proxy_settings = {host: "127.0.0.1", port: 8888, user: "foo", password: "bar"}
|
15
|
+
rebuild_model storage: :azure,
|
16
|
+
container: "testing",
|
17
|
+
azure_credentials: {not: :important}
|
18
|
+
@dummy = Dummy.new
|
19
|
+
@avatar = @dummy.avatar
|
20
|
+
end
|
21
|
+
|
22
|
+
it "gets the correct credentials when RAILS_ENV is production" do
|
23
|
+
rails_env("production") do
|
24
|
+
expect(@avatar.parse_credentials(credentials)).to eq({key: "12345"})
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "gets the correct credentials when RAILS_ENV is development" do
|
29
|
+
rails_env("development") do
|
30
|
+
expect(@avatar.parse_credentials(credentials)).to eq({key: "54321"})
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns the argument if the key does not exist" do
|
35
|
+
rails_env("not really an env") do
|
36
|
+
expect(@avatar.parse_credentials(test: "12345")).to eq({test: "12345"})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#container_name' do
|
43
|
+
describe ":container option via :azure_credentials" do
|
44
|
+
before do
|
45
|
+
rebuild_model storage: :azure,
|
46
|
+
azure_credentials: {container: 'testing'}
|
47
|
+
@dummy = Dummy.new
|
48
|
+
end
|
49
|
+
|
50
|
+
it "populates #container_name" do
|
51
|
+
expect(@dummy.avatar.container_name).to eq('testing')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ":container option" do
|
56
|
+
before do
|
57
|
+
rebuild_model storage: :azure,
|
58
|
+
container: "testing",
|
59
|
+
azure_credentials: {}
|
60
|
+
@dummy = Dummy.new
|
61
|
+
end
|
62
|
+
|
63
|
+
it "populates #container_name" do
|
64
|
+
expect(@dummy.avatar.container_name).to eq('testing')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "missing :container option" do
|
69
|
+
before do
|
70
|
+
rebuild_model storage: :azure,
|
71
|
+
azure_credentials: {not: :important}
|
72
|
+
|
73
|
+
@dummy = Dummy.new
|
74
|
+
@dummy.avatar = stringy_file
|
75
|
+
end
|
76
|
+
|
77
|
+
it "raises an argument error" do
|
78
|
+
expect{ @dummy.avatar.container_name }.to raise_error(ArgumentError, /missing required :container option/)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "" do
|
84
|
+
before do
|
85
|
+
rebuild_model storage: :azure,
|
86
|
+
azure_credentials: {
|
87
|
+
storage_account_name: 'storage',
|
88
|
+
storage_access_key: storage_access_key
|
89
|
+
},
|
90
|
+
container: "container",
|
91
|
+
path: ":attachment/:basename:dotextension",
|
92
|
+
url: ":azure_path_url"
|
93
|
+
|
94
|
+
@dummy = Dummy.new
|
95
|
+
@dummy.avatar = stringy_file
|
96
|
+
|
97
|
+
allow(@dummy).to receive(:new_record?).and_return(false)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "returns urls based on Azure paths" do
|
101
|
+
expect(@dummy.avatar.url).to match(%r{^https://storage.blob.core.windows.net/container/avatars/data[^\.]})
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "An attachment that uses Azure for storage and has styles that return different file types" do
|
106
|
+
before do
|
107
|
+
rebuild_model storage: :azure,
|
108
|
+
styles: { large: ['500x500#', :jpg] },
|
109
|
+
container: "container",
|
110
|
+
path: ":attachment/:basename:dotextension",
|
111
|
+
azure_credentials: {
|
112
|
+
'access_key_id' => "12345",
|
113
|
+
'storage_access_key' => "54321"
|
114
|
+
}
|
115
|
+
|
116
|
+
File.open(fixture_file('5k.png'), 'rb') do |file|
|
117
|
+
@dummy = Dummy.new
|
118
|
+
@dummy.avatar = file
|
119
|
+
|
120
|
+
allow(@dummy).to receive(:new_record?).and_return(false)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it "returns a url containing the correct original file mime type" do
|
125
|
+
expect(@dummy.avatar.url).to match(/.+\/5k.png/)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns a url containing the correct processed file mime type" do
|
129
|
+
expect(@dummy.avatar.url(:large)).to match(/.+\/5k.jpg/)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "An attachment that uses Azure for storage and has spaces in file name" do
|
134
|
+
before do
|
135
|
+
rebuild_model storage: :azure,
|
136
|
+
styles: { large: ["500x500#", :jpg] },
|
137
|
+
container: "container",
|
138
|
+
azure_credentials: {
|
139
|
+
"storage_access_key" => "54321"
|
140
|
+
}
|
141
|
+
|
142
|
+
File.open(fixture_file("spaced file.png"), "rb") do |file|
|
143
|
+
@dummy = Dummy.new
|
144
|
+
@dummy.avatar = file
|
145
|
+
|
146
|
+
allow(@dummy).to receive(:new_record?).and_return(false)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
it "returns a replaced version for path" do
|
151
|
+
expect(@dummy.avatar.path).to match(/.+\/spaced_file\.png/)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "returns a replaced version for url" do
|
155
|
+
expect(@dummy.avatar.url).to match(/.+\/spaced_file\.png/)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe "An attachment that uses Azure for storage and has a question mark in file name" do
|
160
|
+
before do
|
161
|
+
rebuild_model storage: :azure,
|
162
|
+
styles: { large: ['500x500#', :jpg] },
|
163
|
+
container: "container",
|
164
|
+
azure_credentials: {
|
165
|
+
'storage_access_key' => "54321"
|
166
|
+
}
|
167
|
+
|
168
|
+
stringio = stringy_file
|
169
|
+
class << stringio
|
170
|
+
def original_filename
|
171
|
+
"question?mark.png"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
file = Paperclip.io_adapters.for(stringio, Paperclip::Attachment.default_options[:adapter_options])
|
176
|
+
|
177
|
+
@dummy = Dummy.new
|
178
|
+
@dummy.avatar = file
|
179
|
+
@dummy.save
|
180
|
+
|
181
|
+
allow(@dummy).to receive(:new_record?).and_return(false)
|
182
|
+
end
|
183
|
+
|
184
|
+
it "returns a replaced version for path" do
|
185
|
+
expect(@dummy.avatar.path).to match(/.+\/question_mark\.png/)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "returns a replaced version for url" do
|
189
|
+
expect(@dummy.avatar.url).to match(/.+\/question_mark\.png/)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe ":asset_host path Interpolations" do
|
194
|
+
before do
|
195
|
+
rebuild_model storage: :azure,
|
196
|
+
azure_credentials: {},
|
197
|
+
container: "container",
|
198
|
+
path: ":attachment/:basename:dotextension",
|
199
|
+
url: ":asset_host"
|
200
|
+
@dummy = Dummy.new
|
201
|
+
@dummy.avatar = stringy_file
|
202
|
+
allow(@dummy).to receive(:new_record?).and_return(false)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "returns a relative URL for Rails to calculate assets host" do
|
206
|
+
expect(@dummy.avatar.url).to match(%r{^avatars/data[^\.]})
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe "#expiring_url" do
|
211
|
+
before { @dummy = Dummy.new }
|
212
|
+
|
213
|
+
describe "with no attachment" do
|
214
|
+
before { expect(@dummy.avatar.exists?).to be_falsey }
|
215
|
+
|
216
|
+
it "returns the default URL" do
|
217
|
+
expect(@dummy.avatar.expiring_url).to eq(@dummy.avatar.url)
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'generates a url for a style when a file does not exist' do
|
221
|
+
expect(@dummy.avatar.expiring_url(3600, :thumb)).to eq(@dummy.avatar.url(:thumb))
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe "Generating a url with an expiration for each style" do
|
227
|
+
before do
|
228
|
+
rebuild_model storage: :azure,
|
229
|
+
azure_credentials: {
|
230
|
+
production: {
|
231
|
+
storage_account_name: 'prod_storage',
|
232
|
+
storage_access_key: 'YWNjZXNzLWtleQ==',
|
233
|
+
container: "prod_container"
|
234
|
+
},
|
235
|
+
development: {
|
236
|
+
storage_account_name: 'dev_storage',
|
237
|
+
storage_access_key: 'YWNjZXNzLWtleQ==',
|
238
|
+
container: "dev_container"
|
239
|
+
}
|
240
|
+
},
|
241
|
+
path: ":attachment/:style/:basename:dotextension"
|
242
|
+
|
243
|
+
rails_env("production") do
|
244
|
+
@dummy = Dummy.new
|
245
|
+
@dummy.avatar = stringy_file
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
it "generates a url for the thumb" do
|
250
|
+
rails_env("production") do
|
251
|
+
expect(@dummy.avatar.expiring_url(1800, :thumb)).to include("https://prod_storage.blob.core.windows.net/prod_container/avatars/thumb/data")
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
it "generates a url for the default style" do
|
256
|
+
rails_env("production") do
|
257
|
+
expect(@dummy.avatar.expiring_url(1800)).to include("https://prod_storage.blob.core.windows.net/prod_container/avatars/original/data")
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context "Parsing Azure credentials with a container in them" do
|
263
|
+
before do
|
264
|
+
rebuild_model storage: :azure,
|
265
|
+
azure_credentials: {
|
266
|
+
production: { container: "prod_container" },
|
267
|
+
development: { container: "dev_container" }
|
268
|
+
}
|
269
|
+
@dummy = Dummy.new
|
270
|
+
end
|
271
|
+
|
272
|
+
it "gets the right container in production" do
|
273
|
+
rails_env("production") do
|
274
|
+
expect(@dummy.avatar.container_name).to eq("prod_container")
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
it "gets the right container in development" do
|
279
|
+
rails_env("development") do
|
280
|
+
expect(@dummy.avatar.container_name).to eq("dev_container")
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
context "An attachment with Azure storage" do
|
286
|
+
before do
|
287
|
+
rebuild_model storage: :azure,
|
288
|
+
container: "testing",
|
289
|
+
path: ":attachment/:style/:basename:dotextension",
|
290
|
+
azure_credentials: {
|
291
|
+
storage_account_name: 'storage',
|
292
|
+
storage_access_key: storage_access_key
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
it "is extended by the Azure module" do
|
297
|
+
expect(Dummy.new.avatar).to be_a(Paperclip::Storage::Azure)
|
298
|
+
end
|
299
|
+
|
300
|
+
it "won't be extended by the Filesystem module" do
|
301
|
+
expect(Dummy.new.avatar).not_to be_a(Paperclip::Storage::Filesystem)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "An attachment with Azure storage and container defined as a Proc" do
|
306
|
+
before do
|
307
|
+
rebuild_model storage: :azure,
|
308
|
+
container: lambda { |attachment| "container_#{attachment.instance.other}" },
|
309
|
+
azure_credentials: {not: :important}
|
310
|
+
end
|
311
|
+
|
312
|
+
it "gets the right container name" do
|
313
|
+
expect(Dummy.new(other: 'a').avatar.container_name).to eq("container_a")
|
314
|
+
expect(Dummy.new(other: 'b').avatar.container_name).to eq("container_b")
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
context "An attachment with Azure storage and Azure credentials defined as a Proc" do
|
319
|
+
before do
|
320
|
+
rebuild_model storage: :azure,
|
321
|
+
container: {not: :important},
|
322
|
+
azure_credentials: lambda { |attachment|
|
323
|
+
Hash['storage_access_key' => "secret#{attachment.instance.other}"]
|
324
|
+
}
|
325
|
+
end
|
326
|
+
|
327
|
+
it "gets the right credentials" do
|
328
|
+
expect(Dummy.new(other: '1234').avatar.azure_credentials[:storage_access_key]).to eq("secret1234")
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
context "An attachment with Azure storage and Azure credentials in an unsupported manor" do
|
333
|
+
before do
|
334
|
+
rebuild_model storage: :azure,
|
335
|
+
container: "testing",
|
336
|
+
azure_credentials: ["unsupported"]
|
337
|
+
@dummy = Dummy.new
|
338
|
+
end
|
339
|
+
|
340
|
+
it "does not accept the credentials" do
|
341
|
+
expect { @dummy.avatar.azure_credentials }.to raise_error(ArgumentError)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
context "An attachment with Azure storage and Azure credentials not supplied" do
|
346
|
+
before do
|
347
|
+
rebuild_model storage: :azure, container: "testing"
|
348
|
+
@dummy = Dummy.new
|
349
|
+
end
|
350
|
+
|
351
|
+
it "does not parse any credentials" do
|
352
|
+
expect(@dummy.avatar.azure_credentials).to eq({})
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
describe "with Azure credentials supplied as Pathname" do
|
357
|
+
before do
|
358
|
+
ENV['AZURE_CONTAINER'] = 'pathname_container'
|
359
|
+
ENV['AZURE_STORAGE_ACCOUNT'] = 'pathname_storage_account'
|
360
|
+
ENV['AZURE_STORAGE_ACCESS_KEY'] = storage_access_key
|
361
|
+
|
362
|
+
rails_env('test') do
|
363
|
+
rebuild_model storage: :azure,
|
364
|
+
azure_credentials: Pathname.new(fixture_file('azure.yml'))
|
365
|
+
|
366
|
+
Dummy.delete_all
|
367
|
+
@dummy = Dummy.new
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
it "parses the credentials" do
|
372
|
+
expect(@dummy.avatar.container_name).to eq('pathname_container')
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
describe "with Azure credentials in a YAML file" do
|
377
|
+
before do
|
378
|
+
ENV['AZURE_CONTAINER'] = 'pathname_container'
|
379
|
+
ENV['AZURE_STORAGE_ACCOUNT'] = 'pathname_storage_account'
|
380
|
+
ENV['AZURE_STORAGE_ACCESS_KEY'] = storage_access_key
|
381
|
+
|
382
|
+
rails_env('test') do
|
383
|
+
rebuild_model storage: :azure,
|
384
|
+
azure_credentials: File.new(fixture_file('azure.yml'))
|
385
|
+
|
386
|
+
Dummy.delete_all
|
387
|
+
|
388
|
+
@dummy = Dummy.new
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
it "runs the file through ERB" do
|
393
|
+
expect(@dummy.avatar.container_name).to eq('pathname_container')
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
describe "path is a proc" do
|
398
|
+
before do
|
399
|
+
rebuild_model storage: :azure,
|
400
|
+
path: ->(attachment) { attachment.instance.attachment_path }
|
401
|
+
|
402
|
+
@dummy = Dummy.new
|
403
|
+
@dummy.class_eval do
|
404
|
+
def attachment_path
|
405
|
+
'/some/dynamic/path'
|
406
|
+
end
|
407
|
+
end
|
408
|
+
@dummy.avatar = stringy_file
|
409
|
+
end
|
410
|
+
|
411
|
+
it "returns a correct path" do
|
412
|
+
expect(@dummy.avatar.path).to eq('/some/dynamic/path')
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
private
|
417
|
+
|
418
|
+
def rails_env(env)
|
419
|
+
stored_env, Rails.env = Rails.env, env
|
420
|
+
begin
|
421
|
+
yield
|
422
|
+
ensure
|
423
|
+
Rails.env = stored_env
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec'
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_record/version'
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/core_ext'
|
7
|
+
require 'ostruct'
|
8
|
+
require 'pathname'
|
9
|
+
require 'activerecord-import'
|
10
|
+
require 'yaml'
|
11
|
+
require 'paperclip'
|
12
|
+
|
13
|
+
ROOT = Pathname(File.expand_path(File.join(File.dirname(__FILE__), '..')))
|
14
|
+
|
15
|
+
$LOAD_PATH << File.join(ROOT, 'lib')
|
16
|
+
$LOAD_PATH << File.join(ROOT, 'lib', 'paperclip')
|
17
|
+
require File.join(ROOT, 'lib', 'paperclip-azure.rb')
|
18
|
+
|
19
|
+
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
20
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
21
|
+
ActiveRecord::Base.establish_connection(config['test'])
|
22
|
+
if ActiveRecord::VERSION::STRING >= "4.2" &&
|
23
|
+
ActiveRecord::VERSION::STRING < "5.0"
|
24
|
+
ActiveRecord::Base.raise_in_transactional_callbacks = true
|
25
|
+
end
|
26
|
+
Paperclip.options[:logger] = ActiveRecord::Base.logger
|
27
|
+
|
28
|
+
Dir[File.join(ROOT, 'spec', 'support', '**', '*.rb')].each{|f| require f }
|
29
|
+
|
30
|
+
Rails = FakeRails.new('test', Pathname.new(ROOT).join('tmp'))
|
31
|
+
ActiveSupport::Deprecation.silenced = true
|
32
|
+
|
33
|
+
RSpec.configure do |config|
|
34
|
+
config.include ModelReconstruction
|
35
|
+
config.include TestData
|
36
|
+
config.extend VersionHelper
|
37
|
+
config.before(:all) do
|
38
|
+
rebuild_model
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class FakeModel
|
2
|
+
attr_accessor(
|
3
|
+
:avatar_file_name,
|
4
|
+
:avatar_file_size,
|
5
|
+
:avatar_updated_at,
|
6
|
+
:avatar_content_type,
|
7
|
+
:avatar_fingerprint,
|
8
|
+
:id
|
9
|
+
)
|
10
|
+
|
11
|
+
def errors
|
12
|
+
@errors ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def run_paperclip_callbacks name, *args
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid?
|
19
|
+
errors.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
def new_record?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
This is not an image.
|
@@ -0,0 +1 @@
|
|
1
|
+
<html></html>
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
paperclip!
|
Binary file
|
Binary file
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class MockAttachment
|
2
|
+
attr_accessor :updated_at, :original_filename
|
3
|
+
attr_reader :options
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@options = options
|
7
|
+
@model = options[:model]
|
8
|
+
@responds_to_updated_at = options[:responds_to_updated_at]
|
9
|
+
@updated_at = options[:updated_at]
|
10
|
+
@original_filename = options[:original_filename]
|
11
|
+
end
|
12
|
+
|
13
|
+
def instance
|
14
|
+
@model
|
15
|
+
end
|
16
|
+
|
17
|
+
def respond_to?(meth)
|
18
|
+
if meth.to_s == "updated_at"
|
19
|
+
@responds_to_updated_at || @updated_at
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class MockInterpolator
|
2
|
+
def initialize(options = {})
|
3
|
+
@options = options
|
4
|
+
end
|
5
|
+
|
6
|
+
def interpolate(pattern, attachment, style_name)
|
7
|
+
@interpolated_pattern = pattern
|
8
|
+
@interpolated_attachment = attachment
|
9
|
+
@interpolated_style_name = style_name
|
10
|
+
@options[:result]
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_interpolated_pattern?(pattern)
|
14
|
+
@interpolated_pattern == pattern
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_interpolated_style_name?(style_name)
|
18
|
+
@interpolated_style_name == style_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def has_interpolated_attachment?(attachment)
|
22
|
+
@interpolated_attachment == attachment
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class MockUrlGeneratorBuilder
|
2
|
+
def initializer
|
3
|
+
end
|
4
|
+
|
5
|
+
def new(attachment)
|
6
|
+
@attachment = attachment
|
7
|
+
@attachment_options = @attachment.options
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def for(style_name, options)
|
12
|
+
@generated_url_with_style_name = style_name
|
13
|
+
@generated_url_with_options = options
|
14
|
+
"hello"
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_generated_url_with_options?(options)
|
18
|
+
# options.is_a_subhash_of(@generated_url_with_options)
|
19
|
+
options.inject(true) do |acc,(k,v)|
|
20
|
+
acc && @generated_url_with_options[k] == v
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_generated_url_with_style_name?(style_name)
|
25
|
+
@generated_url_with_style_name == style_name
|
26
|
+
end
|
27
|
+
end
|