dragonfly 0.1.6 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of dragonfly might be problematic. Click here for more details.

Files changed (69) hide show
  1. data/.yardopts +4 -0
  2. data/{README.markdown → README.md} +12 -24
  3. data/Rakefile +6 -6
  4. data/VERSION +1 -1
  5. data/config.rb +1 -3
  6. data/docs.watchr +1 -1
  7. data/dragonfly-rails.gemspec +2 -5
  8. data/dragonfly.gemspec +32 -12
  9. data/extra_docs/ActiveRecord.md +195 -0
  10. data/extra_docs/Analysers.md +63 -0
  11. data/extra_docs/DataStorage.md +33 -0
  12. data/extra_docs/Encoding.md +58 -0
  13. data/extra_docs/GettingStarted.md +114 -0
  14. data/extra_docs/Processing.md +58 -0
  15. data/extra_docs/Shortcuts.md +118 -0
  16. data/extra_docs/UsingWithRails.md +104 -0
  17. data/features/{dragonfly.feature → images.feature} +14 -4
  18. data/features/no_processing.feature +20 -0
  19. data/features/steps/dragonfly_steps.rb +29 -8
  20. data/features/support/env.rb +20 -8
  21. data/generators/dragonfly_app/USAGE +0 -1
  22. data/generators/dragonfly_app/dragonfly_app_generator.rb +1 -13
  23. data/generators/dragonfly_app/templates/metal_file.erb +1 -1
  24. data/lib/dragonfly/active_record_extensions.rb +1 -0
  25. data/lib/dragonfly/active_record_extensions/attachment.rb +52 -6
  26. data/lib/dragonfly/active_record_extensions/validations.rb +26 -6
  27. data/lib/dragonfly/analysis/base.rb +6 -3
  28. data/lib/dragonfly/analysis/r_magick_analyser.rb +0 -6
  29. data/lib/dragonfly/app.rb +53 -35
  30. data/lib/dragonfly/configurable.rb +1 -1
  31. data/lib/dragonfly/data_storage/file_data_store.rb +8 -8
  32. data/lib/dragonfly/delegatable.rb +14 -0
  33. data/lib/dragonfly/delegator.rb +50 -0
  34. data/lib/dragonfly/encoding/base.rb +7 -7
  35. data/lib/dragonfly/encoding/r_magick_encoder.rb +3 -0
  36. data/lib/dragonfly/encoding/transparent_encoder.rb +1 -1
  37. data/lib/dragonfly/extended_temp_object.rb +13 -7
  38. data/lib/dragonfly/parameters.rb +17 -11
  39. data/lib/dragonfly/processing/base.rb +9 -0
  40. data/lib/dragonfly/processing/r_magick_processor.rb +15 -1
  41. data/lib/dragonfly/r_magick_configuration.rb +12 -8
  42. data/lib/dragonfly/rails/images.rb +1 -1
  43. data/lib/dragonfly/temp_object.rb +14 -2
  44. data/lib/dragonfly/url_handler.rb +5 -6
  45. data/samples/sample.docx +0 -0
  46. data/spec/dragonfly/active_record_extensions/model_spec.rb +175 -84
  47. data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +0 -8
  48. data/spec/dragonfly/app_spec.rb +3 -3
  49. data/spec/dragonfly/configurable_spec.rb +1 -1
  50. data/spec/dragonfly/data_storage/file_data_store_spec.rb +55 -40
  51. data/spec/dragonfly/delegatable_spec.rb +32 -0
  52. data/spec/dragonfly/delegator_spec.rb +133 -0
  53. data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +28 -0
  54. data/spec/dragonfly/extended_temp_object_spec.rb +5 -5
  55. data/spec/dragonfly/parameters_spec.rb +22 -32
  56. data/spec/dragonfly/processing/rmagick_processor_spec.rb +1 -2
  57. data/spec/dragonfly/temp_object_spec.rb +51 -0
  58. data/spec/dragonfly/url_handler_spec.rb +10 -15
  59. data/yard/handlers/configurable_attr_handler.rb +38 -0
  60. data/yard/setup.rb +9 -0
  61. data/yard/templates/default/fulldoc/html/css/common.css +27 -0
  62. data/yard/templates/default/module/html/configuration_summary.erb +31 -0
  63. data/yard/templates/default/module/setup.rb +17 -0
  64. metadata +31 -12
  65. data/features/support/image_helpers.rb +0 -9
  66. data/generators/dragonfly_app/templates/custom_processing.erb +0 -13
  67. data/lib/dragonfly/analysis/analyser.rb +0 -45
  68. data/lib/dragonfly/processing/processor.rb +0 -14
  69. data/spec/dragonfly/analysis/analyser_spec.rb +0 -85
@@ -0,0 +1,9 @@
1
+ module Dragonfly
2
+ module Processing
3
+ class Base
4
+
5
+ include Delegatable
6
+
7
+ end
8
+ end
9
+ end
@@ -3,7 +3,7 @@ require 'rmagick'
3
3
  module Dragonfly
4
4
  module Processing
5
5
 
6
- module RMagickProcessor
6
+ class RMagickProcessor < Base
7
7
 
8
8
  GRAVITY_MAPPINGS = {
9
9
  'nw' => Magick::NorthWestGravity,
@@ -34,6 +34,20 @@ module Dragonfly
34
34
  image.crop(gravity, x, y, width, height).to_blob
35
35
  end
36
36
 
37
+ def generate(width, height)
38
+ image = Magick::Image.new(width, height)
39
+ num_points = 7
40
+ args = []
41
+ num_points.times do
42
+ args << rand(width)
43
+ args << rand(height)
44
+ args << "rgb(#{rand(256)},#{rand(256)},#{rand(256)})"
45
+ end
46
+ image = image.sparse_color(Magick::ShepardsColorInterpolate, *args)
47
+ image.format = 'png'
48
+ image.to_blob
49
+ end
50
+
37
51
  def resize(temp_object, opts={})
38
52
  rmagick_image(temp_object).change_geometry!(opts[:geometry]) do |cols, rows, img|
39
53
  img.resize!(cols, rows)
@@ -1,16 +1,20 @@
1
1
  module Dragonfly
2
+
3
+ # RMagickConfiguration is a saved configuration for Dragonfly apps, which does the following:
4
+ # - registers an rmagick analyser
5
+ # - registers an rmagick processor
6
+ # - registers an rmagick encoder
7
+ # - adds parameter shortcuts like '280x140!', etc.
8
+ # Look at the source code for apply_configuration to see exactly how it configures the app.
2
9
  module RMagickConfiguration
3
10
 
4
11
  def self.apply_configuration(app)
5
12
  app.configure do |c|
6
- c.analyser do |a|
7
- a.register(Analysis::RMagickAnalyser.new)
8
- end
9
- c.processor do |p|
10
- p.register(Processing::RMagickProcessor)
11
- end
12
- c.encoder = Encoding::RMagickEncoder.new
13
- c.parameters do |p|
13
+ c.register_analyser(Analysis::FileCommandAnalyser)
14
+ c.register_analyser(Analysis::RMagickAnalyser)
15
+ c.register_processor(Processing::RMagickProcessor)
16
+ c.register_encoder(Encoding::RMagickEncoder)
17
+ c.parameters.configure do |p|
14
18
  p.default_format = :jpg
15
19
  # Standard resizing like '30x40!', etc.
16
20
  p.add_shortcut(Symbol) do |format|
@@ -9,7 +9,7 @@ app.configure do |c|
9
9
  c.datastore.configure do |d|
10
10
  d.root_path = "#{Rails.root}/public/system/dragonfly/#{Rails.env}"
11
11
  end
12
- c.url_handler do |u|
12
+ c.url_handler.configure do |u|
13
13
  u.protect_from_dos_attacks = false
14
14
  u.path_prefix = '/media'
15
15
  end
@@ -20,8 +20,10 @@ module Dragonfly
20
20
  attr_accessor :name
21
21
 
22
22
  def modify_self!(obj)
23
- reset!
24
- initialize_from_object!(obj)
23
+ unless obj == self
24
+ reset!
25
+ initialize_from_object!(obj)
26
+ end
25
27
  self
26
28
  end
27
29
 
@@ -79,6 +81,15 @@ module Dragonfly
79
81
  end
80
82
  end
81
83
 
84
+ def to_file(path)
85
+ if initialized_data
86
+ File.open(path, 'w'){|f| f.write(initialized_data) }
87
+ else
88
+ FileUtils.cp(self.path, path)
89
+ end
90
+ File.new(path)
91
+ end
92
+
82
93
  protected
83
94
 
84
95
  attr_accessor :initialized_data, :initialized_tempfile, :initialized_file
@@ -103,6 +114,7 @@ module Dragonfly
103
114
  @initialized_tempfile = obj
104
115
  when File
105
116
  @initialized_file = obj
117
+ self.name = File.basename(obj.path)
106
118
  else
107
119
  raise ArgumentError, "#{self.class.name} must be initialized with a String, a File or a Tempfile"
108
120
  end
@@ -52,12 +52,12 @@ module Dragonfly
52
52
  end
53
53
 
54
54
  def parameters_to_url(parameters)
55
- parameters.validate!
56
55
  query_string = [:processing_method, :processing_options, :encoding].map do |attribute|
57
56
  build_query(MAPPINGS[attribute] => parameters[attribute]) unless parameters[attribute].blank?
58
57
  end.compact.join('&')
59
58
  sha_string = "&#{MAPPINGS[:sha]}=#{sha_from_parameters(parameters)}" if protect_from_dos_attacks?
60
- url = "#{path_prefix}/#{escape_except_for_slashes(parameters.uid)}.#{parameters.format}?#{query_string}#{sha_string}"
59
+ ext = ".#{parameters.format}" if parameters.format
60
+ url = "#{path_prefix}/#{escape_except_for_slashes(parameters.uid)}#{ext}?#{query_string}#{sha_string}"
61
61
  url.sub!(/\?$/,'')
62
62
  url
63
63
  end
@@ -82,7 +82,8 @@ module Dragonfly
82
82
  end
83
83
 
84
84
  def extract_format(path, query)
85
- path.sub(/^\//,'').split('.').last
85
+ bits = path.sub(/^\//,'').split('.')
86
+ bits.last if bits.length > 1
86
87
  end
87
88
 
88
89
  def extract_encoding(path, query)
@@ -140,9 +141,7 @@ module Dragonfly
140
141
  end
141
142
 
142
143
  def validate_format!(path)
143
- if path !~ /^#{path_prefix}/ || path !~ /^.*[^\/].*\..*[^\/].*$/
144
- raise UnknownUrl, "path '#{path}' not found"
145
- end
144
+ raise UnknownUrl, "path '#{path}' not found" unless path =~ %r(^#{path_prefix}/[^.]+)
146
145
  end
147
146
 
148
147
  end
Binary file
@@ -93,9 +93,6 @@ describe Item do
93
93
  it "should return nil for the url" do
94
94
  @item.preview_image.url.should be_nil
95
95
  end
96
- it "should return the size" do
97
- @item.preview_image.size.should == 10
98
- end
99
96
  it "should return the temp_object" do
100
97
  temp_object = @item.preview_image.temp_object
101
98
  temp_object.should be_a(Dragonfly::ExtendedTempObject)
@@ -135,39 +132,35 @@ describe Item do
135
132
  @item.preview_image.url(:arg).should == 'some.url'
136
133
  end
137
134
 
138
- describe "when reloaded" do
135
+ describe "when accessed by a new model object" do
139
136
  before(:each) do
140
- @item.reload
137
+ @item = Item.find(@item.id)
141
138
  end
142
139
  it "should destroy the data on destroy" do
143
140
  @app.datastore.should_receive(:destroy).with(@item.preview_image_uid)
144
141
  @item.destroy
145
142
  end
146
- it "should return the size" do
147
- @item.preview_image.size.should == 10
148
- end
149
143
  it "should return the temp_object" do
150
- temp_object = @item.preview_image.temp_object
151
- temp_object.should be_a(Dragonfly::ExtendedTempObject)
152
- temp_object.data.should == 'DATASTRING'
144
+ @app.should_receive(:fetch).with('some_uid').and_return(temp_object = mock('extended temp_object'))
145
+ @item.preview_image.temp_object.should == temp_object
153
146
  end
154
147
  end
155
148
 
156
149
  describe "when something new is assigned" do
157
150
  before(:each) do
158
- @item.preview_image = "NEWDATASTRING"
151
+ @item.preview_image = "ANEWDATASTRING"
159
152
  end
160
153
  it "should set the uid to pending" do
161
154
  @item.preview_image_uid.should be_a(Dragonfly::ActiveRecordExtensions::PendingUID)
162
155
  end
163
156
  it "should destroy the old data when saved" do
164
- @app.datastore.should_receive(:store).with(a_temp_object_with_data("NEWDATASTRING")).once.and_return('some_uid')
157
+ @app.datastore.should_receive(:store).with(a_temp_object_with_data("ANEWDATASTRING")).once.and_return('some_uid')
165
158
 
166
159
  @app.datastore.should_receive(:destroy).with('some_uid')
167
160
  @item.save!
168
161
  end
169
162
  it "should store the new data when saved" do
170
- @app.datastore.should_receive(:store).with(a_temp_object_with_data("NEWDATASTRING"))
163
+ @app.datastore.should_receive(:store).with(a_temp_object_with_data("ANEWDATASTRING"))
171
164
  @item.save!
172
165
  end
173
166
  it "should destroy the old data on destroy" do
@@ -175,12 +168,12 @@ describe Item do
175
168
  @item.destroy
176
169
  end
177
170
  it "should return the new size" do
178
- @item.preview_image.size.should == 13
171
+ @item.preview_image.size.should == 14
179
172
  end
180
173
  it "should return the new temp_object" do
181
174
  temp_object = @item.preview_image.temp_object
182
175
  temp_object.should be_a(Dragonfly::ExtendedTempObject)
183
- temp_object.data.should == 'NEWDATASTRING'
176
+ temp_object.data.should == 'ANEWDATASTRING'
184
177
  end
185
178
  end
186
179
 
@@ -216,9 +209,8 @@ describe Item do
216
209
  end
217
210
 
218
211
  end
219
-
220
212
  end
221
- end
213
+ end
222
214
 
223
215
  describe "validations" do
224
216
 
@@ -265,26 +257,33 @@ describe Item do
265
257
 
266
258
  end
267
259
 
268
- describe "validates_mime_type_of" do
260
+ describe "validates_property" do
269
261
 
270
262
  before(:each) do
271
263
  @item = Item.new(:preview_image => "1234567890")
272
264
  end
273
265
 
274
266
  before(:all) do
275
- custom_analyser = Object.new
276
- def custom_analyser.mime_type(temp_object)
277
- case temp_object.data
278
- when "WRONG TYPE" then 'wrong/type'
279
- when "OTHER TYPE" then nil
280
- else 'how/special'
267
+ custom_analyser = Class.new(Dragonfly::Analysis::Base)
268
+ custom_analyser.class_eval do
269
+ def mime_type(temp_object)
270
+ case temp_object.data
271
+ when "WRONG TYPE" then 'wrong/type'
272
+ when "OTHER TYPE" then nil
273
+ else 'how/special'
274
+ end
275
+ end
276
+
277
+ def number_of_Gs(temp_object)
278
+ temp_object.data.count('G')
281
279
  end
282
280
  end
283
- @app.analyser.register(custom_analyser)
281
+ @app.register_analyser(custom_analyser)
284
282
 
285
283
  Item.class_eval do
286
- validates_mime_type_of :preview_image, :in => ['how/special', 'how/crazy'], :if => :its_friday
287
- validates_mime_type_of :other_image, :yet_another_image, :as => 'how/special'
284
+ validates_property :mime_type, :of => :preview_image, :in => ['how/special', 'how/crazy'], :if => :its_friday
285
+ validates_property :mime_type, :of => [:other_image, :yet_another_image], :as => 'how/special'
286
+ validates_property :number_of_Gs, :of => :preview_image, :in => (0..2)
288
287
 
289
288
  image_accessor :preview_image
290
289
  image_accessor :other_image
@@ -297,21 +296,27 @@ describe Item do
297
296
  end
298
297
  end
299
298
 
300
- it "should be valid if nil, if not validated on presence (even with validates_mime_type_of)" do
299
+ it "should be valid if nil, if not validated on presence (even with validates_property)" do
301
300
  @item.other_image = nil
302
301
  @item.should be_valid
303
302
  end
304
303
 
305
- it "should be invalid if the mime_type is unknown" do
304
+ it "should be invalid if the property is nil" do
306
305
  @item.preview_image = "OTHER TYPE"
307
306
  @item.should_not be_valid
308
- @item.errors.on(:preview_image).should == "doesn't have the correct MIME-type. It needs to be one of 'how/special', 'how/crazy', but was 'unknown'"
307
+ @item.errors.on(:preview_image).should == "mime type is incorrect. It needs to be one of 'how/special', 'how/crazy', but was ''"
309
308
  end
310
309
 
311
- it "should be invalid if the mime_type is wrong" do
310
+ it "should be invalid if the property is wrong" do
312
311
  @item.preview_image = "WRONG TYPE"
313
312
  @item.should_not be_valid
314
- @item.errors.on(:preview_image).should == "doesn't have the correct MIME-type. It needs to be one of 'how/special', 'how/crazy', but was 'wrong/type'"
313
+ @item.errors.on(:preview_image).should == "mime type is incorrect. It needs to be one of 'how/special', 'how/crazy', but was 'wrong/type'"
314
+ end
315
+
316
+ it "should work for a range" do
317
+ @item.preview_image = "GOOGLE GUM"
318
+ @item.should_not be_valid
319
+ @item.errors.on(:preview_image).should == "number of gs is incorrect. It needs to be between 0 and 2, but was '3'"
315
320
  end
316
321
 
317
322
  it "should validate individually" do
@@ -319,7 +324,7 @@ describe Item do
319
324
  @item.yet_another_image = "WRONG TYPE"
320
325
  @item.should_not be_valid
321
326
  @item.errors.on(:other_image).should be_nil
322
- @item.errors.on(:yet_another_image).should == "doesn't have the correct MIME-type. It needs to be 'how/special', but was 'wrong/type'"
327
+ @item.errors.on(:yet_another_image).should == "mime type is incorrect. It needs to be 'how/special', but was 'wrong/type'"
323
328
  end
324
329
 
325
330
  it "should include standard extra options like 'if' on mime type validation" do
@@ -331,24 +336,44 @@ describe Item do
331
336
  it "should require either :as or :in as an argument" do
332
337
  lambda{
333
338
  Item.class_eval do
334
- validates_mime_type_of :preview_image
339
+ validates_property :mime_type, :of => :preview_image
340
+ end
341
+ }.should raise_error(ArgumentError)
342
+ end
343
+
344
+ it "should require :of as an argument" do
345
+ lambda{
346
+ Item.class_eval do
347
+ validates_property :mime_type, :as => 'hi/there'
335
348
  end
336
349
  }.should raise_error(ArgumentError)
337
350
  end
338
351
 
339
352
  end
340
353
 
354
+ describe "validates_mime_type_of" do
355
+ it "should provide validates_mime_type as a convenience wrapper for validates_property" do
356
+ Item.should_receive(:validates_property).with(:mime_type, :of => :preview_image, :in => ['how/special', 'how/crazy'], :if => :its_friday)
357
+ Item.class_eval do
358
+ validates_mime_type_of :preview_image, :in => ['how/special', 'how/crazy'], :if => :its_friday
359
+ end
360
+ end
361
+ end
362
+
341
363
  end
342
364
 
343
- describe "magic attributes" do
344
-
365
+ describe "extra properties" do
366
+
345
367
  before(:each) do
346
368
  @app = Dragonfly::App[:images]
347
- custom_analyser = Object.new
348
- def custom_analyser.some_analyser_method(temp_object)
349
- "abc" + temp_object.data[0..0]
369
+ custom_analyser = Class.new(Dragonfly::Analysis::Base)
370
+ custom_analyser.class_eval do
371
+ def some_analyser_method(temp_object)
372
+ "abc" + temp_object.data[0..0]
373
+ end
374
+ def number_of_As(temp_object); temp_object.data.count('A'); end
350
375
  end
351
- @app.analyser.register(custom_analyser)
376
+ @app.register_analyser(custom_analyser)
352
377
  ActiveRecord::Base.register_dragonfly_app(:image, @app)
353
378
  Item.class_eval do
354
379
  image_accessor :preview_image
@@ -356,59 +381,125 @@ describe Item do
356
381
  @item = Item.new
357
382
  end
358
383
 
359
- it "should default the magic attribute as nil" do
360
- @item.preview_image_some_analyser_method.should be_nil
361
- end
384
+ describe "magic attributes" do
362
385
 
363
- it "should set the magic attribute when assigned" do
364
- @item.preview_image = '123'
365
- @item.preview_image_some_analyser_method.should == 'abc1'
366
- end
386
+ it "should default the magic attribute as nil" do
387
+ @item.preview_image_some_analyser_method.should be_nil
388
+ end
367
389
 
368
- it "should not set non-magic attributes with the same prefix when assigned" do
369
- @item.preview_image_blah_blah = 'wassup'
370
- @item.preview_image = '123'
371
- @item.preview_image_blah_blah.should == 'wassup'
372
- end
390
+ it "should set the magic attribute when assigned" do
391
+ @item.preview_image = '123'
392
+ @item.preview_image_some_analyser_method.should == 'abc1'
393
+ end
373
394
 
374
- it "should update the magic attribute when something else is assigned" do
375
- @item.preview_image = '123'
376
- @item.preview_image = '456'
377
- @item.preview_image_some_analyser_method.should == 'abc4'
378
- end
395
+ it "should not set non-magic attributes with the same prefix when assigned" do
396
+ @item.preview_image_blah_blah = 'wassup'
397
+ @item.preview_image = '123'
398
+ @item.preview_image_blah_blah.should == 'wassup'
399
+ end
379
400
 
380
- it "should reset the magic attribute when set to nil" do
381
- @item.preview_image = '123'
382
- @item.preview_image = nil
383
- @item.preview_image_some_analyser_method.should be_nil
384
- end
401
+ it "should update the magic attribute when something else is assigned" do
402
+ @item.preview_image = '123'
403
+ @item.preview_image = '456'
404
+ @item.preview_image_some_analyser_method.should == 'abc4'
405
+ end
385
406
 
386
- it "should not reset non-magic attributes with the same prefix when set to nil" do
387
- @item.preview_image_blah_blah = 'wassup'
388
- @item.preview_image = '123'
389
- @item.preview_image = nil
390
- @item.preview_image_blah_blah.should == 'wassup'
391
- end
407
+ it "should reset the magic attribute when set to nil" do
408
+ @item.preview_image = '123'
409
+ @item.preview_image = nil
410
+ @item.preview_image_some_analyser_method.should be_nil
411
+ end
392
412
 
393
- it "should work for size too" do
394
- @item.preview_image = '123'
395
- @item.preview_image_size.should == 3
396
- end
413
+ it "should not reset non-magic attributes with the same prefix when set to nil" do
414
+ @item.preview_image_blah_blah = 'wassup'
415
+ @item.preview_image = '123'
416
+ @item.preview_image = nil
417
+ @item.preview_image_blah_blah.should == 'wassup'
418
+ end
397
419
 
398
- it "should store the original file extension if it exists" do
399
- data = 'jasdlkf sadjl'
400
- data.stub!(:original_filename).and_return('hello.png')
401
- @item.preview_image = data
402
- @item.preview_image_ext.should == 'png'
403
- end
420
+ it "should work for size too" do
421
+ @item.preview_image = '123'
422
+ @item.preview_image_size.should == 3
423
+ end
424
+
425
+ it "should store the original file extension if it exists" do
426
+ data = 'jasdlkf sadjl'
427
+ data.stub!(:original_filename).and_return('hello.png')
428
+ @item.preview_image = data
429
+ @item.preview_image_ext.should == 'png'
430
+ end
404
431
 
405
- it "should store the original file name if it exists" do
406
- data = 'jasdlkf sadjl'
407
- data.stub!(:original_filename).and_return('hello.png')
408
- @item.preview_image = data
409
- @item.preview_image_name.should == 'hello.png'
432
+ it "should store the original file name if it exists" do
433
+ data = 'jasdlkf sadjl'
434
+ data.stub!(:original_filename).and_return('hello.png')
435
+ @item.preview_image = data
436
+ @item.preview_image_name.should == 'hello.png'
437
+ end
410
438
  end
439
+
440
+
441
+ describe "delegating methods to the temp_object" do
442
+ before(:each) do
443
+ @item.preview_image = "DATASTRING"
444
+ end
445
+ it "should have properties from the analyser" do
446
+ @item.preview_image.number_of_As.should == 2
447
+ end
448
+ it "should report that it responds to analyser methods" do
449
+ @item.preview_image.respond_to?(:number_of_As).should be_true
450
+ end
451
+ it "should include analyser methods in methods" do
452
+ @item.preview_image.methods.include?('number_of_As').should be_true
453
+ end
454
+ it "should include analyser methods in public_methods" do
455
+ @item.preview_image.public_methods.include?('number_of_As').should be_true
456
+ end
457
+ it "should update when something new is assigned" do
458
+ @item.preview_image = 'ANEWDATASTRING'
459
+ @item.preview_image.number_of_As.should == 3
460
+ end
461
+
462
+ describe "from a new model object" do
463
+ before(:each) do
464
+ @app.datastore.stub!(:store).and_return('my_uid')
465
+ item = Item.create!(:preview_image => 'DATASTRING')
466
+ @item = Item.find(item.id)
467
+ @temp_object = @app.create_object('DATASTRING')
468
+ @temp_object.name = 'jonny.briggs'
469
+ end
470
+ it "should load the temp_object then delegate the method" do
471
+ @app.should_receive(:fetch).with('my_uid').and_return(@temp_object)
472
+ @item.preview_image.number_of_As.should == 2
473
+ end
474
+ it "should use the magic attribute if there is one, and not load the temp_object" do
475
+ @app.should_not_receive(:fetch)
476
+ @item.should_receive(:preview_image_some_analyser_method).and_return('result yo')
477
+ @item.preview_image.some_analyser_method.should == 'result yo'
478
+ end
479
+
480
+ %w(size name ext).each do |attr|
481
+ it "should use the magic attribute for #{attr} if there is one, and not load the temp_object" do
482
+ @app.should_not_receive(:fetch)
483
+ @item.should_receive("preview_image_#{attr}".to_sym).and_return('result yo')
484
+ @item.preview_image.send(attr).should == 'result yo'
485
+ end
486
+ it "should load the temp_object then delegate '#{attr}' if there is no magic attribute for it" do
487
+ Item.should_receive(:column_names).and_return(['preview_image_uid']) # no magic attributes
488
+
489
+ @app.should_receive(:fetch).with('my_uid').and_return(@temp_object)
490
+ @item.preview_image.send(attr).should == @temp_object.send(attr)
491
+ end
492
+ end
493
+
494
+ end
411
495
 
496
+ it "should not raise an error if a non-existent method is called" do
497
+ # Just checking method missing works ok
498
+ lambda{
499
+ @item.preview_image.eggbert
500
+ }.should raise_error(NoMethodError)
501
+ end
502
+ end
412
503
  end
413
504
 
414
505
  end