image_resizer 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.rspec +1 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +9 -0
  4. data/Gemfile.lock +45 -0
  5. data/LICENSE +22 -0
  6. data/README.md +56 -0
  7. data/Rakefile +53 -0
  8. data/VERSION +1 -0
  9. data/image_resizer.gemspec +93 -0
  10. data/lib/image_resizer/analyzer.rb +51 -0
  11. data/lib/image_resizer/configurable.rb +206 -0
  12. data/lib/image_resizer/encoder.rb +55 -0
  13. data/lib/image_resizer/has_filename.rb +24 -0
  14. data/lib/image_resizer/loggable.rb +28 -0
  15. data/lib/image_resizer/processor.rb +220 -0
  16. data/lib/image_resizer/shell.rb +48 -0
  17. data/lib/image_resizer/temp_object.rb +216 -0
  18. data/lib/image_resizer/utils.rb +44 -0
  19. data/lib/image_resizer.rb +10 -0
  20. data/samples/DSC02119.JPG +0 -0
  21. data/samples/a.jp2 +0 -0
  22. data/samples/beach.jpg +0 -0
  23. data/samples/beach.png +0 -0
  24. data/samples/egg.png +0 -0
  25. data/samples/landscape.png +0 -0
  26. data/samples/round.gif +0 -0
  27. data/samples/sample.docx +0 -0
  28. data/samples/taj.jpg +0 -0
  29. data/samples/white pixel.png +0 -0
  30. data/spec/image_resizer/analyzer_spec.rb +78 -0
  31. data/spec/image_resizer/configurable_spec.rb +479 -0
  32. data/spec/image_resizer/encoder_spec.rb +41 -0
  33. data/spec/image_resizer/has_filename_spec.rb +88 -0
  34. data/spec/image_resizer/loggable_spec.rb +80 -0
  35. data/spec/image_resizer/processor_spec.rb +590 -0
  36. data/spec/image_resizer/shell_spec.rb +34 -0
  37. data/spec/image_resizer/temp_object_spec.rb +442 -0
  38. data/spec/spec_helper.rb +61 -0
  39. data/spec/support/argument_matchers.rb +19 -0
  40. data/spec/support/image_matchers.rb +58 -0
  41. data/spec/support/simple_matchers.rb +53 -0
  42. data/tmp/test_file +1 -0
  43. metadata +147 -0
@@ -0,0 +1,442 @@
1
+ require 'spec_helper'
2
+
3
+ describe ImageResizer::TempObject do
4
+
5
+ ####### Helper Methods #######
6
+
7
+ def sample_path(filename)
8
+ SAMPLES_DIR.join(filename)
9
+ end
10
+
11
+ def new_tempfile(data='HELLO')
12
+ tempfile = Tempfile.new('test')
13
+ tempfile.write(data)
14
+ tempfile.rewind
15
+ tempfile
16
+ end
17
+
18
+ def new_file(data='HELLO', path="tmp/test_file")
19
+ File.open(path, 'w') do |f|
20
+ f.write(data)
21
+ end
22
+ File.new(path)
23
+ end
24
+
25
+ def new_pathname(data='HELLO', path="tmp/test_file")
26
+ File.open(path, 'w') do |f|
27
+ f.write(data)
28
+ end
29
+ Pathname.new(path)
30
+ end
31
+
32
+ def new_temp_object(data, klass=ImageResizer::TempObject)
33
+ klass.new(initialization_object(data))
34
+ end
35
+
36
+ def initialization_object(data)
37
+ raise NotImplementedError, "This should be implemented in the describe block!"
38
+ end
39
+
40
+ def get_parts(temp_object)
41
+ parts = []
42
+ temp_object.each do |bytes|
43
+ parts << bytes
44
+ end
45
+ parts.length.should >= 2 # Sanity check to check that the sample file is adequate for this test
46
+ parts
47
+ end
48
+
49
+ ###############################
50
+
51
+ it "should raise an error if initialized with a non-string/file/tempfile" do
52
+ lambda{
53
+ ImageResizer::TempObject.new(3)
54
+ }.should raise_error(ArgumentError)
55
+ end
56
+
57
+ shared_examples_for "common behaviour" do
58
+
59
+ describe "simple initialization" do
60
+
61
+ before(:each) do
62
+ @temp_object = new_temp_object('HELLO')
63
+ end
64
+
65
+ describe "data" do
66
+ it "should return the data correctly" do
67
+ @temp_object.data.should == 'HELLO'
68
+ end
69
+ end
70
+
71
+ describe "file" do
72
+ it "should return a readable file" do
73
+ @temp_object.file.should be_a(File)
74
+ end
75
+ it "should contain the correct data" do
76
+ @temp_object.file.read.should == 'HELLO'
77
+ end
78
+ it "should yield a file then close it if a block is given" do
79
+ @temp_object.file do |f|
80
+ f.read.should == 'HELLO'
81
+ f.should_receive :close
82
+ end
83
+ end
84
+ it "should return whatever is returned from the block if a block is given" do
85
+ @temp_object.file do |f|
86
+ 'doogie'
87
+ end.should == 'doogie'
88
+ end
89
+ it "should enable reading the file twice" do
90
+ @temp_object.file{|f| f.read }.should == "HELLO"
91
+ @temp_object.file{|f| f.read }.should == "HELLO"
92
+ end
93
+ end
94
+
95
+ describe "tempfile" do
96
+ it "should create a closed tempfile" do
97
+ @temp_object.tempfile.should be_a(Tempfile)
98
+ @temp_object.tempfile.should be_closed
99
+ end
100
+ it "should contain the correct data" do
101
+ @temp_object.tempfile.open.read.should == 'HELLO'
102
+ end
103
+ end
104
+
105
+ describe "path" do
106
+ it "should return an absolute file path" do
107
+ if running_on_windows?
108
+ @temp_object.path.should =~ %r{^[a-zA-Z]:/\w+}
109
+ else
110
+ @temp_object.path.should =~ %r{^/\w+}
111
+ end
112
+ end
113
+ end
114
+
115
+ describe "size" do
116
+ it "should return the size in bytes" do
117
+ @temp_object.size.should == 5
118
+ end
119
+ end
120
+
121
+ describe "to_file" do
122
+ before(:each) do
123
+ @filename = 'tmp/eggnog.txt'
124
+ FileUtils.rm_f(@filename) if File.exists?(@filename)
125
+ end
126
+ after(:each) do
127
+ FileUtils.rm_f(@filename) if File.exists?(@filename)
128
+ end
129
+ it "should write to a file" do
130
+ @temp_object.to_file(@filename)
131
+ File.exists?(@filename).should be_true
132
+ end
133
+ it "should write the correct data to the file" do
134
+ @temp_object.to_file(@filename)
135
+ File.read(@filename).should == 'HELLO'
136
+ end
137
+ it "should return a readable file" do
138
+ file = @temp_object.to_file(@filename)
139
+ file.should be_a(File)
140
+ file.read.should == 'HELLO'
141
+ end
142
+ it "should have 644 permissions" do
143
+ @temp_object.to_file(@filename)
144
+ File::Stat.new(@filename).mode.to_s(8).should =~ /644$/
145
+ end
146
+ it "should allow setting different permissions" do
147
+ @temp_object.to_file(@filename, :mode => 0755)
148
+ File::Stat.new(@filename).mode.to_s(8).should =~ /755$/
149
+ end
150
+ it "should create intermediate subdirs" do
151
+ filename = 'tmp/gog/mcgee'
152
+ @temp_object.to_file(filename)
153
+ File.exists?(filename).should be_true
154
+ FileUtils.rm_rf('tmp/gog')
155
+ end
156
+ it "should allow not creating intermediate subdirs" do
157
+ filename = 'tmp/gog/mcgee'
158
+ expect{ @temp_object.to_file(filename, :mkdirs => false) }.to raise_error()
159
+ end
160
+ end
161
+
162
+ end
163
+
164
+ describe "each" do
165
+ it "should yield 8192 bytes each time" do
166
+ temp_object = new_temp_object(File.read(sample_path('round.gif')))
167
+ parts = get_parts(temp_object)
168
+ parts[0...-1].each do |part|
169
+ part.bytesize.should == 8192
170
+ end
171
+ parts.last.bytesize.should <= 8192
172
+ end
173
+ end
174
+
175
+ describe "closing" do
176
+ before(:each) do
177
+ @temp_object = new_temp_object("wassup")
178
+ end
179
+ it "should delete its tempfile" do
180
+ tempfile = @temp_object.tempfile
181
+ path = tempfile.path
182
+ path.should_not be_empty
183
+ @temp_object.close
184
+ File.exist?(path).should be_false
185
+ end
186
+ %w(tempfile file data).each do |method|
187
+ it "should raise error when calling #{method}" do
188
+ @temp_object.close
189
+ expect{
190
+ @temp_object.send(method)
191
+ }.to raise_error(ImageResizer::TempObject::Closed)
192
+ end
193
+ end
194
+ it "should not report itself as closed to begin with" do
195
+ @temp_object.should_not be_closed
196
+ end
197
+ it "should report itself as closed after closing" do
198
+ @temp_object.close
199
+ @temp_object.should be_closed
200
+ end
201
+ end
202
+
203
+ end
204
+
205
+ describe "initializing from a string" do
206
+
207
+ def initialization_object(data)
208
+ data
209
+ end
210
+
211
+ it_should_behave_like "common behaviour"
212
+
213
+ it "should not create a file when calling each" do
214
+ temp_object = new_temp_object('HELLO')
215
+ temp_object.should_not_receive(:tempfile)
216
+ temp_object.each{}
217
+ end
218
+
219
+ it "should use set the file extension in path from the name" do
220
+ temp_object = ImageResizer::TempObject.new("hi", :name => 'dark.cloud')
221
+ temp_object.path.should =~ /\.cloud$/
222
+ end
223
+ end
224
+
225
+ describe "initializing from a tempfile" do
226
+
227
+ def initialization_object(data)
228
+ new_tempfile(data)
229
+ end
230
+
231
+ it_should_behave_like "common behaviour"
232
+
233
+ it "should not create a data string when calling each" do
234
+ temp_object = new_temp_object('HELLO')
235
+ temp_object.should_not_receive(:data)
236
+ temp_object.each{}
237
+ end
238
+
239
+ it "should return the tempfile's path" do
240
+ temp_object = new_temp_object('HELLO')
241
+ temp_object.path.should == temp_object.tempfile.path
242
+ end
243
+ end
244
+
245
+ describe "initializing from a file" do
246
+
247
+ def initialization_object(data)
248
+ new_file(data)
249
+ end
250
+
251
+ it_should_behave_like "common behaviour"
252
+
253
+ it "should not create a data string when calling each" do
254
+ temp_object = new_temp_object('HELLO')
255
+ temp_object.should_not_receive(:data)
256
+ temp_object.each{}
257
+ end
258
+
259
+ it "should return the file's path" do
260
+ file = new_file('HELLO')
261
+ temp_object = ImageResizer::TempObject.new(file)
262
+ temp_object.path.should == File.expand_path(file.path)
263
+ end
264
+
265
+ it "should return an absolute path even if the file wasn't instantiated like that" do
266
+ file = new_file('HELLO', 'tmp/bongo')
267
+ temp_object = ImageResizer::TempObject.new(file)
268
+ if running_on_windows?
269
+ temp_object.path.should =~ %r{^[a-zA-Z]:/\w.*bongo}
270
+ else
271
+ temp_object.path.should =~ %r{^/\w.*bongo}
272
+ end
273
+ file.close
274
+ FileUtils.rm(file.path)
275
+ end
276
+ end
277
+
278
+ describe "initializing from a pathname" do
279
+
280
+ def initialization_object(data)
281
+ new_pathname(data)
282
+ end
283
+
284
+ it_should_behave_like "common behaviour"
285
+
286
+ it "should not create a data string when calling each" do
287
+ temp_object = new_temp_object('HELLO')
288
+ temp_object.should_not_receive(:data)
289
+ temp_object.each{}
290
+ end
291
+
292
+ it "should return the file's path" do
293
+ pathname = new_pathname('HELLO')
294
+ temp_object = ImageResizer::TempObject.new(pathname)
295
+ temp_object.path.should == File.expand_path(pathname.to_s)
296
+ end
297
+
298
+ it "should return an absolute path even if the pathname is relative" do
299
+ pathname = new_pathname('HELLO', 'tmp/bingo')
300
+ temp_object = ImageResizer::TempObject.new(pathname)
301
+ if running_on_windows?
302
+ temp_object.path.should =~ %r{^[a-zA-Z]:/\w.*bingo}
303
+ else
304
+ temp_object.path.should =~ %r{^/\w.*bingo}
305
+ end
306
+ pathname.delete
307
+ end
308
+ end
309
+
310
+ describe "initializing from another temp object" do
311
+
312
+ def initialization_object(data)
313
+ ImageResizer::TempObject.new(data)
314
+ end
315
+
316
+ before(:each) do
317
+ @temp_object1 = ImageResizer::TempObject.new(new_tempfile('hello'))
318
+ @temp_object2 = ImageResizer::TempObject.new(@temp_object1)
319
+ end
320
+
321
+ it_should_behave_like "common behaviour"
322
+
323
+ it "should not be the same object" do
324
+ @temp_object1.should_not == @temp_object2
325
+ end
326
+ it "should have the same data" do
327
+ @temp_object1.data.should == @temp_object2.data
328
+ end
329
+ it "should have the same file path" do
330
+ @temp_object1.path.should == @temp_object2.path
331
+ end
332
+ end
333
+
334
+ describe "initialize from a Rack::Test::UploadedFile" do
335
+ def initialization_object(data)
336
+ # The criteria we're using to determine if an object is a
337
+ # Rack::Test::UploadedFile is if it responds to path
338
+ #
339
+ # We can't just check if it is_a?(Rack::Test::UploadedFile) because that
340
+ # class may not always be present.
341
+ uploaded_file = mock("mock_uploadedfile")
342
+ uploaded_file.stub!(:path).and_return File.expand_path('tmp/test_file')
343
+ uploaded_file.stub!(:original_filename).and_return('foo.jpg')
344
+
345
+ # Create a real file with the contents required at the correct path
346
+ new_file(data, 'tmp/test_file')
347
+
348
+ uploaded_file
349
+ end
350
+
351
+ it_should_behave_like "common behaviour"
352
+ end
353
+
354
+ describe "original_filename" do
355
+ before(:each) do
356
+ @obj = new_tempfile
357
+ end
358
+ it "should set the original_filename if the initial object responds to 'original filename'" do
359
+ def @obj.original_filename
360
+ 'jimmy.page'
361
+ end
362
+ ImageResizer::TempObject.new(@obj).original_filename.should == 'jimmy.page'
363
+ end
364
+ it "should not set the name if the initial object doesn't respond to 'original filename'" do
365
+ ImageResizer::TempObject.new(@obj).original_filename.should be_nil
366
+ end
367
+ it "should set the name if the initial object is a file object" do
368
+ file = File.new(SAMPLES_DIR.join('round.gif'))
369
+ temp_object = ImageResizer::TempObject.new(file)
370
+ temp_object.original_filename.should == 'round.gif'
371
+ end
372
+ it "should set the name if the initial object is a pathname" do
373
+ pathname = Pathname.new(SAMPLES_DIR + '/round.gif')
374
+ temp_object = ImageResizer::TempObject.new(pathname)
375
+ temp_object.original_filename.should == 'round.gif'
376
+ end
377
+ end
378
+
379
+ describe "meta" do
380
+ it "should default to an empty hash" do
381
+ ImageResizer::TempObject.new('sdf').meta.should == {}
382
+ end
383
+ it "should allow setting on initialize" do
384
+ ImageResizer::TempObject.new('sdf', :dub => 'wub').meta.should == {:dub => 'wub'}
385
+ end
386
+ it "should allow setting" do
387
+ temp_object = ImageResizer::TempObject.new('boo')
388
+ temp_object.meta = {:far => 'gone'}
389
+ temp_object.meta.should == {:far => 'gone'}
390
+ end
391
+ end
392
+
393
+ describe "name" do
394
+ it "should default to nil" do
395
+ ImageResizer::TempObject.new("HELLO").name.should be_nil
396
+ end
397
+ it "should allow setting the name via the meta" do
398
+ ImageResizer::TempObject.new("HELLO", :name => 'gosh.pig').name.should == "gosh.pig"
399
+ end
400
+ it "should fallback to the original filename if not set" do
401
+ content = "HELLO"
402
+ content.should_receive(:original_filename).and_return("some.egg")
403
+ temp_object = ImageResizer::TempObject.new(content)
404
+ temp_object.name.should == "some.egg"
405
+ end
406
+ it "should prefer the specified name to the original filename" do
407
+ content = "HELLO"
408
+ content.stub!(:original_filename).and_return("brase.nose")
409
+ temp_object = ImageResizer::TempObject.new("HELLO", :name => 'some.gug')
410
+ temp_object.name.should == "some.gug"
411
+ end
412
+ it "should allow setting with a setter" do
413
+ temp_object = ImageResizer::TempObject.new("HELLO")
414
+ temp_object.name = 'bugs'
415
+ temp_object.name.should == "bugs"
416
+ end
417
+ end
418
+
419
+ describe "sanity check for using HasFilename" do
420
+ it "should act like ImageResizer::HasFilename" do
421
+ temp_object = ImageResizer::TempObject.new('h', :name => 'one.big.park')
422
+ temp_object.ext = 'smeagol'
423
+ temp_object.name.should == 'one.big.smeagol'
424
+ end
425
+ end
426
+
427
+ describe "unique_id" do
428
+ before(:each) do
429
+ @temp_object = ImageResizer::TempObject.new('hello')
430
+ end
431
+ it "should return a unique id" do
432
+ @temp_object.unique_id.should =~ /^\d+$/
433
+ end
434
+ it "should be unique" do
435
+ @temp_object.unique_id.should_not == ImageResizer::TempObject.new('hello').unique_id
436
+ end
437
+ it "should not change" do
438
+ @temp_object.unique_id.should == @temp_object.unique_id
439
+ end
440
+ end
441
+
442
+ end
@@ -0,0 +1,61 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.setup(:default, :test)
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'rspec'
8
+ require 'image_resizer'
9
+ require 'fileutils'
10
+ require 'pry'
11
+ require 'pry-nav'
12
+ require 'pry-stack_explorer'
13
+
14
+ # Requires supporting files with custom matchers and macros, etc,
15
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
16
+
17
+ SAMPLES_DIR = Pathname.new(File.expand_path(File.dirname(__FILE__) + '/../samples')) unless defined?(SAMPLES_DIR)
18
+
19
+ def todo
20
+ raise "TODO"
21
+ end
22
+
23
+
24
+ # require 'logger'
25
+ # LOG_FILE = 'tmp/test.log' unless defined?(LOG_FILE)
26
+ # FileUtils.rm_rf(LOG_FILE)
27
+ # def mock_app(extra_stubs={})
28
+ # mock('app', {
29
+ # :datastore => mock('datastore', :store => 'some_uid', :retrieve => ["SOME_DATA", {}], :destroy => nil),
30
+ # :processor => mock('processor', :process => "SOME_PROCESSED_DATA"),
31
+ # :encoder => mock('encoder', :encode => "SOME_ENCODED_DATA"),
32
+ # :analyzer => mock('analyzer', :analyse => "some_result", :analysis_methods => Module.new),
33
+ # :generator => mock('generator', :generate => "SOME_GENERATED_DATA"),
34
+ # :log => Logger.new(LOG_FILE),
35
+ # :cache_duration => 10000,
36
+ # :job_definitions => Module.new
37
+ # }.merge(extra_stubs)
38
+ # )
39
+ # end
40
+
41
+ # def test_app
42
+ # time = Time.now
43
+ # app = ImageResizer::App.send(:new, "test_#{time.sec}_#{time.usec}".to_sym)
44
+ # app.log = Logger.new(LOG_FILE)
45
+ # app.datastore.root_path = 'tmp/file_data_store_test'
46
+ # app
47
+ # end
48
+
49
+ def suppressing_stderr
50
+ original_stderr = $stderr.dup
51
+ tempfile = Tempfile.new('stderr')
52
+ $stderr.reopen(tempfile) rescue
53
+ yield
54
+ ensure
55
+ tempfile.close!
56
+ $stderr.reopen(original_stderr)
57
+ end
58
+
59
+ def running_on_windows?
60
+ ENV['OS'] && ENV['OS'].downcase == 'windows_nt'
61
+ end
@@ -0,0 +1,19 @@
1
+ def string_matching(regexp)
2
+ Spec::Mocks::ArgumentMatchers::RegexpMatcher.new(regexp)
3
+ end
4
+
5
+ class TempObjectArgumentMatcher
6
+ def initialize(data, opts)
7
+ @data = data
8
+ @opts = opts
9
+ end
10
+ def ==(actual)
11
+ actual.is_a?(ImageResizer::TempObject) &&
12
+ actual.data == @data &&
13
+ @opts.all?{|k,v| actual.send(k) == v }
14
+ end
15
+ end
16
+
17
+ def a_temp_object_with_data(data, opts={})
18
+ TempObjectArgumentMatcher.new(data, opts)
19
+ end
@@ -0,0 +1,58 @@
1
+ def image_properties(image)
2
+ if image.is_a?(Tempfile)
3
+ tempfile = image
4
+ else
5
+ tempfile = Tempfile.new('image')
6
+ tempfile.binmode
7
+ tempfile.write(image.is_a?(ImageResizer::TempObject) ? image.data : image)
8
+ tempfile.close
9
+ end
10
+ details = `identify #{tempfile.path}`
11
+ # example of details string:
12
+ # myimage.png PNG 200x100 200x100+0+0 8-bit DirectClass 31.2kb
13
+ filename, format, geometry, geometry_2, depth, image_class, size = details.split(' ')
14
+ width, height = geometry.split('x')
15
+ {
16
+ :filename => filename,
17
+ :format => format.downcase,
18
+ :width => width,
19
+ :height => height,
20
+ :depth => depth,
21
+ :image_class => image_class,
22
+ :size => size
23
+ }
24
+ end
25
+
26
+ RSpec::Matchers.define :have_width do |width|
27
+ match do |given|
28
+ width.should === image_properties(given)[:width].to_i
29
+ end
30
+ end
31
+
32
+ RSpec::Matchers.define :have_height do |height|
33
+ match do |given|
34
+ height.should === image_properties(given)[:height].to_i
35
+ end
36
+ end
37
+
38
+ RSpec::Matchers.define :have_format do |format|
39
+ match do |given|
40
+ image_properties(given)[:format].should == format
41
+ end
42
+ end
43
+
44
+ RSpec::Matchers.define :have_size do |size|
45
+ match do |given|
46
+ image_properties(given)[:size].should == size
47
+ end
48
+ end
49
+
50
+ RSpec::Matchers.define :equal_image do |other|
51
+ match do |given|
52
+ image_data = given.open.read
53
+ other_image_data = other.open.read
54
+ given.close
55
+ other.close
56
+ image_data == other_image_data
57
+ end
58
+ end
@@ -0,0 +1,53 @@
1
+ RSpec::Matchers.define :match_url do |url|
2
+ match do |given|
3
+ given_path, given_query_string = given.split('?')
4
+ path, query_string = url.split('?')
5
+
6
+ path == given_path && given_query_string.split('&').sort == query_string.split('&').sort
7
+ end
8
+ end
9
+
10
+ RSpec::Matchers.define :be_an_empty_directory do
11
+ match do |given|
12
+ Dir.entries(given) == ['.','..']
13
+ end
14
+ end
15
+
16
+ RSpec::Matchers.define :include_hash do |hash|
17
+ match do |given|
18
+ given.merge(hash) == given
19
+ end
20
+ end
21
+
22
+ def memory_usage
23
+ GC.start # Garbage collect
24
+ `ps -o rss= -p #{$$}`.strip.to_i
25
+ end
26
+
27
+ RSpec::Matchers.define :leak_memory do
28
+ match do |given|
29
+ memory_before = memory_usage
30
+ given.call
31
+ memory_after = memory_usage
32
+ result = memory_after > memory_before
33
+ puts "#{memory_after} > #{memory_before}" if result
34
+ result
35
+ end
36
+ end
37
+
38
+ RSpec::Matchers.define :match_attachment_classes do |classes|
39
+ match do |given_classes|
40
+ given_classes.length == classes.length &&
41
+ classes.zip(given_classes).all? do |(klass, given)|
42
+ given.model_class == klass[0] && given.attribute == klass[1] && given.app == klass[2]
43
+ end
44
+ end
45
+ end
46
+
47
+ RSpec::Matchers.define :be_a_text_response do
48
+ match do |given_response|
49
+ given_response.status.should == 200
50
+ given_response.body.length.should > 0
51
+ given_response.content_type.should == 'text/plain'
52
+ end
53
+ end
data/tmp/test_file ADDED
@@ -0,0 +1 @@
1
+ wassup