fog-dragonfly 0.8.1

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.
Files changed (155) hide show
  1. data/.specopts +2 -0
  2. data/.yardopts +23 -0
  3. data/Gemfile +23 -0
  4. data/Gemfile.rails.2.3.5 +14 -0
  5. data/History.md +266 -0
  6. data/LICENSE +20 -0
  7. data/README.md +88 -0
  8. data/Rakefile +92 -0
  9. data/VERSION +1 -0
  10. data/config.ru +13 -0
  11. data/docs.watchr +1 -0
  12. data/dragonfly.gemspec +293 -0
  13. data/extra_docs/Analysers.md +108 -0
  14. data/extra_docs/Caching.md +23 -0
  15. data/extra_docs/Configuration.md +138 -0
  16. data/extra_docs/DataStorage.md +136 -0
  17. data/extra_docs/Encoding.md +96 -0
  18. data/extra_docs/GeneralUsage.md +121 -0
  19. data/extra_docs/Generators.md +102 -0
  20. data/extra_docs/Heroku.md +50 -0
  21. data/extra_docs/Index.md +36 -0
  22. data/extra_docs/MimeTypes.md +40 -0
  23. data/extra_docs/Models.md +266 -0
  24. data/extra_docs/Mongo.md +45 -0
  25. data/extra_docs/Processing.md +130 -0
  26. data/extra_docs/Rack.md +52 -0
  27. data/extra_docs/Rails2.md +55 -0
  28. data/extra_docs/Rails3.md +62 -0
  29. data/extra_docs/Sinatra.md +25 -0
  30. data/extra_docs/URLs.md +169 -0
  31. data/features/3.0.3.feature +8 -0
  32. data/features/images.feature +47 -0
  33. data/features/no_processing.feature +14 -0
  34. data/features/rails_2.3.5.feature +7 -0
  35. data/features/steps/common_steps.rb +8 -0
  36. data/features/steps/dragonfly_steps.rb +66 -0
  37. data/features/steps/rails_steps.rb +39 -0
  38. data/features/support/env.rb +40 -0
  39. data/fixtures/files/app/models/album.rb +3 -0
  40. data/fixtures/files/app/views/albums/new.html.erb +4 -0
  41. data/fixtures/files/app/views/albums/show.html.erb +4 -0
  42. data/fixtures/files/config/initializers/dragonfly.rb +4 -0
  43. data/fixtures/files/features/manage_album_images.feature +12 -0
  44. data/fixtures/files/features/step_definitions/image_steps.rb +15 -0
  45. data/fixtures/files/features/support/paths.rb +15 -0
  46. data/fixtures/files/features/text_images.feature +7 -0
  47. data/fixtures/rails_2.3.5/template.rb +10 -0
  48. data/fixtures/rails_3.0.3/template.rb +20 -0
  49. data/irbrc.rb +17 -0
  50. data/lib/dragonfly.rb +45 -0
  51. data/lib/dragonfly/active_model_extensions.rb +13 -0
  52. data/lib/dragonfly/active_model_extensions/attachment.rb +169 -0
  53. data/lib/dragonfly/active_model_extensions/class_methods.rb +45 -0
  54. data/lib/dragonfly/active_model_extensions/instance_methods.rb +28 -0
  55. data/lib/dragonfly/active_model_extensions/validations.rb +37 -0
  56. data/lib/dragonfly/analyser.rb +59 -0
  57. data/lib/dragonfly/analysis/file_command_analyser.rb +32 -0
  58. data/lib/dragonfly/analysis/image_magick_analyser.rb +47 -0
  59. data/lib/dragonfly/analysis/r_magick_analyser.rb +63 -0
  60. data/lib/dragonfly/app.rb +182 -0
  61. data/lib/dragonfly/config/heroku.rb +19 -0
  62. data/lib/dragonfly/config/image_magick.rb +41 -0
  63. data/lib/dragonfly/config/r_magick.rb +46 -0
  64. data/lib/dragonfly/config/rails.rb +17 -0
  65. data/lib/dragonfly/configurable.rb +119 -0
  66. data/lib/dragonfly/core_ext/object.rb +8 -0
  67. data/lib/dragonfly/core_ext/string.rb +9 -0
  68. data/lib/dragonfly/core_ext/symbol.rb +9 -0
  69. data/lib/dragonfly/data_storage.rb +9 -0
  70. data/lib/dragonfly/data_storage/file_data_store.rb +114 -0
  71. data/lib/dragonfly/data_storage/mongo_data_store.rb +82 -0
  72. data/lib/dragonfly/data_storage/s3data_store.rb +115 -0
  73. data/lib/dragonfly/encoder.rb +13 -0
  74. data/lib/dragonfly/encoding/image_magick_encoder.rb +57 -0
  75. data/lib/dragonfly/encoding/r_magick_encoder.rb +61 -0
  76. data/lib/dragonfly/function_manager.rb +69 -0
  77. data/lib/dragonfly/generation/hash_with_css_style_keys.rb +23 -0
  78. data/lib/dragonfly/generation/image_magick_generator.rb +140 -0
  79. data/lib/dragonfly/generation/r_magick_generator.rb +155 -0
  80. data/lib/dragonfly/generator.rb +9 -0
  81. data/lib/dragonfly/image_magick_utils.rb +81 -0
  82. data/lib/dragonfly/job.rb +371 -0
  83. data/lib/dragonfly/job_builder.rb +39 -0
  84. data/lib/dragonfly/job_definitions.rb +26 -0
  85. data/lib/dragonfly/job_endpoint.rb +15 -0
  86. data/lib/dragonfly/loggable.rb +28 -0
  87. data/lib/dragonfly/middleware.rb +34 -0
  88. data/lib/dragonfly/processing/image_magick_processor.rb +99 -0
  89. data/lib/dragonfly/processing/r_magick_processor.rb +126 -0
  90. data/lib/dragonfly/processor.rb +9 -0
  91. data/lib/dragonfly/r_magick_utils.rb +48 -0
  92. data/lib/dragonfly/rails/images.rb +22 -0
  93. data/lib/dragonfly/response.rb +82 -0
  94. data/lib/dragonfly/routed_endpoint.rb +40 -0
  95. data/lib/dragonfly/serializer.rb +32 -0
  96. data/lib/dragonfly/simple_cache.rb +23 -0
  97. data/lib/dragonfly/simple_endpoint.rb +63 -0
  98. data/lib/dragonfly/temp_object.rb +220 -0
  99. data/samples/beach.png +0 -0
  100. data/samples/egg.png +0 -0
  101. data/samples/round.gif +0 -0
  102. data/samples/sample.docx +0 -0
  103. data/samples/taj.jpg +0 -0
  104. data/spec/argument_matchers.rb +19 -0
  105. data/spec/dragonfly/active_model_extensions/active_model_setup.rb +97 -0
  106. data/spec/dragonfly/active_model_extensions/active_record_setup.rb +85 -0
  107. data/spec/dragonfly/active_model_extensions/model_spec.rb +723 -0
  108. data/spec/dragonfly/active_model_extensions/spec_helper.rb +11 -0
  109. data/spec/dragonfly/analyser_spec.rb +123 -0
  110. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +57 -0
  111. data/spec/dragonfly/analysis/image_magick_analyser_spec.rb +15 -0
  112. data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +27 -0
  113. data/spec/dragonfly/analysis/shared_analyser_spec.rb +51 -0
  114. data/spec/dragonfly/app_spec.rb +280 -0
  115. data/spec/dragonfly/config/r_magick_spec.rb +25 -0
  116. data/spec/dragonfly/configurable_spec.rb +220 -0
  117. data/spec/dragonfly/core_ext/string_spec.rb +17 -0
  118. data/spec/dragonfly/core_ext/symbol_spec.rb +17 -0
  119. data/spec/dragonfly/data_storage/data_store_spec.rb +76 -0
  120. data/spec/dragonfly/data_storage/file_data_store_spec.rb +169 -0
  121. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +38 -0
  122. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +94 -0
  123. data/spec/dragonfly/deprecation_spec.rb +20 -0
  124. data/spec/dragonfly/encoding/image_magick_encoder_spec.rb +41 -0
  125. data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +37 -0
  126. data/spec/dragonfly/function_manager_spec.rb +154 -0
  127. data/spec/dragonfly/generation/hash_with_css_style_keys_spec.rb +24 -0
  128. data/spec/dragonfly/generation/image_magick_generator_spec.rb +12 -0
  129. data/spec/dragonfly/generation/r_magick_generator_spec.rb +24 -0
  130. data/spec/dragonfly/generation/shared_generator_spec.rb +91 -0
  131. data/spec/dragonfly/image_magick_utils_spec.rb +16 -0
  132. data/spec/dragonfly/job_builder_spec.rb +37 -0
  133. data/spec/dragonfly/job_definitions_spec.rb +35 -0
  134. data/spec/dragonfly/job_endpoint_spec.rb +120 -0
  135. data/spec/dragonfly/job_spec.rb +773 -0
  136. data/spec/dragonfly/loggable_spec.rb +80 -0
  137. data/spec/dragonfly/middleware_spec.rb +68 -0
  138. data/spec/dragonfly/processing/image_magick_processor_spec.rb +29 -0
  139. data/spec/dragonfly/processing/r_magick_processor_spec.rb +26 -0
  140. data/spec/dragonfly/processing/shared_processing_spec.rb +215 -0
  141. data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
  142. data/spec/dragonfly/serializer_spec.rb +61 -0
  143. data/spec/dragonfly/simple_cache_spec.rb +27 -0
  144. data/spec/dragonfly/simple_endpoint_spec.rb +89 -0
  145. data/spec/dragonfly/temp_object_spec.rb +352 -0
  146. data/spec/image_matchers.rb +47 -0
  147. data/spec/simple_matchers.rb +44 -0
  148. data/spec/spec_helper.rb +58 -0
  149. data/yard/handlers/configurable_attr_handler.rb +38 -0
  150. data/yard/setup.rb +15 -0
  151. data/yard/templates/default/fulldoc/html/css/common.css +107 -0
  152. data/yard/templates/default/layout/html/layout.erb +87 -0
  153. data/yard/templates/default/module/html/configuration_summary.erb +31 -0
  154. data/yard/templates/default/module/setup.rb +17 -0
  155. metadata +550 -0
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -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?(Dragonfly::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,97 @@
1
+ # --------------------------------------------------------------- #
2
+ # MODELS
3
+ # --------------------------------------------------------------- #
4
+ class MyModel
5
+
6
+ # Callbacks
7
+ extend ActiveModel::Callbacks
8
+ define_model_callbacks :save, :destroy
9
+
10
+ include ActiveModel::Validations
11
+
12
+ class << self
13
+ def create!(attrs={})
14
+ new(attrs).save!
15
+ end
16
+
17
+ def find(id)
18
+ new(instances[id])
19
+ end
20
+
21
+ def instances
22
+ @instances ||= {}
23
+ end
24
+ end
25
+
26
+ def initialize(attrs={})
27
+ attrs.each do |key, value|
28
+ send("#{key}=", value)
29
+ end
30
+ end
31
+
32
+ attr_accessor :id
33
+
34
+ def to_hash
35
+ self.class::ATTRIBUTES.inject({}) do |hash, attr|
36
+ hash[attr] = send(attr)
37
+ hash
38
+ end
39
+ end
40
+
41
+ def save
42
+ _run_save_callbacks {
43
+ self.id ||= rand(1000)
44
+ self.class.instances[id] = self.to_hash
45
+ }
46
+ end
47
+ def save!
48
+ save
49
+ self
50
+ end
51
+
52
+ def destroy
53
+ _run_destroy_callbacks {}
54
+ end
55
+ end
56
+
57
+ class Item < MyModel
58
+
59
+ ATTRIBUTES = [
60
+ :title,
61
+ :preview_image_uid,
62
+ :preview_image_some_analyser_method,
63
+ :preview_image_size,
64
+ :preview_image_name,
65
+ :preview_image_ext,
66
+ :preview_image_blah_blah,
67
+ :other_image_uid,
68
+ :yet_another_image_uid,
69
+ :otra_imagen_uid,
70
+ :trailer_video_uid,
71
+ :created_at,
72
+ :updated_at
73
+ ]
74
+ attr_accessor *ATTRIBUTES
75
+ end
76
+
77
+ class Car < MyModel
78
+ ATTRIBUTES = [
79
+ :image_uid,
80
+ :reliant_image_uid,
81
+ :type
82
+ ]
83
+ attr_accessor *ATTRIBUTES
84
+ end
85
+
86
+ class Photo < MyModel
87
+ ATTRIBUTES = [:image_uid]
88
+ attr_accessor *ATTRIBUTES
89
+ end
90
+
91
+
92
+ # --------------------------------------------------------------- #
93
+ # HELPER METHODS
94
+ # --------------------------------------------------------------- #
95
+ def model_class
96
+ MyModel
97
+ end
@@ -0,0 +1,85 @@
1
+ # Seems to blow up on Rails 3 without this - only need temporarily
2
+ begin
3
+ require 'active_support/all'
4
+ rescue LoadError => e
5
+ puts "Couldn't load activesupport: #{e}"
6
+ end
7
+
8
+ require 'active_record'
9
+
10
+ # --------------------------------------------------------------- #
11
+ # MIGRATION
12
+ # --------------------------------------------------------------- #
13
+ class MigrationForTest < ActiveRecord::Migration
14
+
15
+ def self.up
16
+ create_table :items, :force => true do |t|
17
+ t.string :title
18
+ t.string :preview_image_uid
19
+ t.string :preview_image_some_analyser_method
20
+ t.integer :preview_image_size
21
+ t.string :preview_image_name
22
+ t.string :preview_image_ext
23
+ t.string :preview_image_blah_blah
24
+ t.string :other_image_uid
25
+ t.string :yet_another_image_uid
26
+ t.string :otra_imagen_uid
27
+ t.string :trailer_video_uid
28
+ t.timestamps
29
+ end
30
+
31
+ create_table :cars do |t|
32
+ t.string :image_uid
33
+ t.string :reliant_image_uid
34
+ t.string :type
35
+ end
36
+
37
+ create_table :photos do |t|
38
+ t.string :image_uid
39
+ end
40
+
41
+ end
42
+
43
+ def self.down
44
+ drop_table :items
45
+ end
46
+
47
+ end
48
+
49
+
50
+ # --------------------------------------------------------------- #
51
+ # MODELS
52
+ # --------------------------------------------------------------- #
53
+ class Item < ActiveRecord::Base
54
+ end
55
+ class Car < ActiveRecord::Base
56
+ end
57
+ class Photo < ActiveRecord::Base
58
+ end
59
+
60
+
61
+ # --------------------------------------------------------------- #
62
+ # SPEC CONFIG
63
+ # --------------------------------------------------------------- #
64
+ DB_FILE = File.expand_path(File.dirname(__FILE__)+'/db.sqlite3')
65
+
66
+ Spec::Runner.configure do |config|
67
+
68
+ config.before(:all) do
69
+ FileUtils.rm_f(DB_FILE)
70
+ ActiveRecord::Base.establish_connection(
71
+ :adapter => "sqlite3",
72
+ :database => DB_FILE
73
+ )
74
+ MigrationForTest.verbose = false
75
+ MigrationForTest.up
76
+ end
77
+
78
+ end
79
+
80
+ # --------------------------------------------------------------- #
81
+ # HELPER METHODS
82
+ # --------------------------------------------------------------- #
83
+ def model_class
84
+ ActiveRecord::Base
85
+ end
@@ -0,0 +1,723 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Item do
4
+
5
+ # See extra setup in models / initializer files
6
+
7
+ describe "defining accessors" do
8
+
9
+ let(:app1){ Dragonfly[:images] }
10
+ let(:app2){ Dragonfly[:videos] }
11
+
12
+ it "should return the mapping of apps to attributes" do
13
+ app1.define_macro(model_class, :image_accessor)
14
+ app2.define_macro(model_class, :video_accessor)
15
+ Item.class_eval do
16
+ image_accessor :preview_image
17
+ video_accessor :trailer_video
18
+ end
19
+ Item.dragonfly_apps_for_attributes.should == {:preview_image => app1, :trailer_video => app2}
20
+ end
21
+
22
+ it "should work for included modules (e.g. Mongoid::Document)" do
23
+ mongoid_document = Module.new
24
+ app1.define_macro_on_include(mongoid_document, :dog_accessor)
25
+ model_class = Class.new do
26
+ def self.before_save(*args); end
27
+ def self.before_destroy(*args); end
28
+ include mongoid_document
29
+ dog_accessor :doogie
30
+ end
31
+ model_class.dragonfly_apps_for_attributes.should == {:doogie => app1}
32
+ end
33
+
34
+ end
35
+
36
+ describe "correctly defined" do
37
+
38
+ before(:each) do
39
+ @app = Dragonfly[:images]
40
+ @app.define_macro(model_class, :image_accessor)
41
+ Item.class_eval do
42
+ image_accessor :preview_image
43
+ end
44
+ @item = Item.new
45
+ end
46
+
47
+ it "should provide a reader" do
48
+ @item.should respond_to(:preview_image)
49
+ end
50
+
51
+ it "should provide a writer" do
52
+ @item.should respond_to(:preview_image=)
53
+ end
54
+
55
+ describe "when there has been nothing assigned" do
56
+ it "the reader should return nil" do
57
+ @item.preview_image.should be_nil
58
+ end
59
+ it "the uid should be nil" do
60
+ @item.preview_image_uid.should be_nil
61
+ end
62
+ it "should not try to store anything on save" do
63
+ @app.datastore.should_not_receive(:store)
64
+ @item.save!
65
+ end
66
+ it "should not try to destroy anything on save" do
67
+ @app.datastore.should_not_receive(:destroy)
68
+ @item.save!
69
+ end
70
+ it "should not try to destroy anything on destroy" do
71
+ @app.datastore.should_not_receive(:destroy)
72
+ @item.destroy
73
+ end
74
+ end
75
+
76
+ describe "when the uid is set manually" do
77
+ before(:each) do
78
+ @item.preview_image_uid = 'some_known_uid'
79
+ end
80
+ it "should not try to retrieve any data" do
81
+ @app.datastore.should_not_receive(:retrieve)
82
+ @item.save!
83
+ end
84
+ it "should not try to destroy any data" do
85
+ @app.datastore.should_not_receive(:destroy)
86
+ @item.save!
87
+ end
88
+ it "should not try to store any data" do
89
+ @app.datastore.should_not_receive(:store)
90
+ @item.save!
91
+ end
92
+ end
93
+
94
+ describe "when there has been some thing assigned but not saved" do
95
+ before(:each) do
96
+ @item.preview_image = "DATASTRING"
97
+ end
98
+ it "the reader should return an attachment" do
99
+ @item.preview_image.should be_a(Dragonfly::ActiveModelExtensions::Attachment)
100
+ end
101
+ it "the uid should be nil" do
102
+ @item.preview_image_uid.should be_nil
103
+ end
104
+ it "should store the image when saved" do
105
+ @app.datastore.should_receive(:store).with(a_temp_object_with_data("DATASTRING"), {})
106
+ @item.save!
107
+ end
108
+ it "should not try to destroy anything on destroy" do
109
+ @app.datastore.should_not_receive(:destroy)
110
+ @item.destroy
111
+ end
112
+ it "should return nil for the url" do
113
+ @item.preview_image.url.should be_nil
114
+ end
115
+ describe "when the uid is set manually" do
116
+ before(:each) do
117
+ @item.preview_image_uid = 'some_known_uid'
118
+ end
119
+ it "should not try to retrieve any data" do
120
+ @app.datastore.should_not_receive(:retrieve)
121
+ @item.save!
122
+ end
123
+ it "should not try to destroy any data" do
124
+ @app.datastore.should_not_receive(:destroy)
125
+ @item.save!
126
+ end
127
+ it "should not try to store any data" do
128
+ @app.datastore.should_not_receive(:store)
129
+ @item.save!
130
+ end
131
+ end
132
+
133
+ end
134
+
135
+ describe "when something has been assigned and saved" do
136
+
137
+ before(:each) do
138
+ @item.preview_image = "DATASTRING"
139
+ @app.datastore.should_receive(:store).with(a_temp_object_with_data("DATASTRING"), {}).once.and_return('some_uid')
140
+ @item.save!
141
+ end
142
+
143
+ it "should have the correct uid" do
144
+ @item.preview_image_uid.should == 'some_uid'
145
+ end
146
+ it "should not try to store anything if saved again" do
147
+ @app.datastore.should_not_receive(:store)
148
+ @item.save!
149
+ end
150
+
151
+ it "should not try to destroy anything if saved again" do
152
+ @app.datastore.should_not_receive(:destroy)
153
+ @item.save!
154
+ end
155
+
156
+ it "should destroy the data on destroy" do
157
+ @app.datastore.should_receive(:destroy).with('some_uid')
158
+ @item.destroy
159
+ end
160
+
161
+ it "should return the url for the data" do
162
+ @app.should_receive(:url_for).with(an_instance_of(Dragonfly::Job)).and_return('some.url')
163
+ @item.preview_image.url.should == 'some.url'
164
+ end
165
+
166
+ it "should destroy the old data when the uid is set manually" do
167
+ @app.datastore.should_receive(:destroy).with('some_uid')
168
+ @item.preview_image_uid = 'some_known_uid'
169
+ @item.save!
170
+ end
171
+
172
+ describe "when accessed by a new model object" do
173
+ before(:each) do
174
+ @item = Item.find(@item.id)
175
+ end
176
+ it "should destroy the data on destroy" do
177
+ @app.datastore.should_receive(:destroy).with(@item.preview_image_uid)
178
+ @item.destroy
179
+ end
180
+ end
181
+
182
+ describe "when something new is assigned" do
183
+ before(:each) do
184
+ @item.preview_image = "ANEWDATASTRING"
185
+ @app.datastore.stub!(:store).and_return('some_uid')
186
+ end
187
+ it "should set the uid to nil" do
188
+ @item.preview_image_uid.should be_nil
189
+ end
190
+ it "should destroy the old data when saved" do
191
+ @app.datastore.should_receive(:destroy).with('some_uid')
192
+ @item.save!
193
+ end
194
+ it "should not try to destroy the old data if saved again" do
195
+ @app.datastore.should_receive(:destroy).with('some_uid')
196
+ @item.save!
197
+ @app.datastore.should_not_receive(:destroy).with('some_uid')
198
+ @item.save!
199
+ end
200
+ it "should destroy the old data when saved, even if yet another thing is assigned" do
201
+ @item.preview_image = "YET ANOTHER DATA STRING"
202
+ @app.datastore.should_receive(:destroy).with('some_uid')
203
+ @item.save!
204
+ end
205
+ it "should store the new data when saved" do
206
+ @app.datastore.should_receive(:store).with(a_temp_object_with_data("ANEWDATASTRING"), {})
207
+ @item.save!
208
+ end
209
+ it "should destroy the old data on destroy" do
210
+ @app.datastore.should_receive(:destroy).with('some_uid')
211
+ @item.destroy
212
+ end
213
+ it "should destroy the old data on destroy, even if yet another thing is assigned" do
214
+ @item.preview_image = "YET ANOTHER DATA STRING"
215
+ @app.datastore.should_receive(:destroy).with('some_uid')
216
+ @item.destroy
217
+ end
218
+ it "should destroy the old data when the uid has been set manually" do
219
+ @app.datastore.should_receive(:destroy).with('some_uid')
220
+ @item.preview_image_uid = 'some_known_uid'
221
+ @item.save!
222
+ end
223
+ it "should return the new size" do
224
+ @item.preview_image.size.should == 14
225
+ end
226
+ it "should return the new data" do
227
+ @item.preview_image.data.should == 'ANEWDATASTRING'
228
+ end
229
+ end
230
+
231
+ describe "when it is set to nil" do
232
+ before(:each) do
233
+ @item.preview_image = nil
234
+ end
235
+ it "should set the uid to nil" do
236
+ @item.preview_image_uid.should be_nil
237
+ end
238
+ it "should return the attribute as nil" do
239
+ @item.preview_image.should be_nil
240
+ end
241
+ it "should destroy the data on save" do
242
+ @app.datastore.should_receive(:destroy).with('some_uid')
243
+ @item.save!
244
+ @item.preview_image.should be_nil
245
+ end
246
+ it "should destroy the old data on destroy" do
247
+ @app.datastore.should_receive(:destroy).with('some_uid')
248
+ @item.destroy
249
+ end
250
+ end
251
+
252
+ describe "when the data can't be found" do
253
+ it "should log a warning if the data wasn't found on destroy" do
254
+ @app.datastore.should_receive(:destroy).with('some_uid').and_raise(Dragonfly::DataStorage::DataNotFound)
255
+ @app.log.should_receive(:warn)
256
+ @item.destroy
257
+ end
258
+ end
259
+
260
+ end
261
+
262
+ describe "other types of assignment" do
263
+ before(:each) do
264
+ @app.generator.add :egg do
265
+ "Gungedin"
266
+ end
267
+ @app.processor.add :doogie do |temp_object|
268
+ temp_object.data.upcase
269
+ end
270
+ end
271
+
272
+ describe "assigning with a job" do
273
+ before(:each) do
274
+ @job = @app.generate(:egg)
275
+ @item.preview_image = @job
276
+ end
277
+
278
+ it "should work" do
279
+ @item.preview_image.data.should == 'Gungedin'
280
+ end
281
+
282
+ it "should not be affected by subsequent changes to the job" do
283
+ @job.process!(:doogie)
284
+ @item.preview_image.data.should == 'Gungedin'
285
+ end
286
+ end
287
+
288
+ describe "assigning with another attachment" do
289
+ before(:each) do
290
+ Item.class_eval do
291
+ image_accessor :other_image
292
+ end
293
+ end
294
+ it "should work like assigning the job" do
295
+ @item.preview_image = 'eggheads'
296
+ @item.other_image = @item.preview_image
297
+ @item.preview_image = 'dogchin'
298
+ @item.other_image.data.should == 'eggheads'
299
+ end
300
+ end
301
+
302
+ describe "assigning by means of a bang method" do
303
+ before(:each) do
304
+ @app.processor.add :double do |temp_object|
305
+ temp_object.data * 2
306
+ end
307
+ @app.encoder.add do |temp_object, format|
308
+ temp_object.data.downcase + format.to_s
309
+ end
310
+ @item.preview_image = "HELLO"
311
+ end
312
+ it "should modify as if being assigned again" do
313
+ @item.preview_image.process!(:double)
314
+ @item.preview_image.data.should == 'HELLOHELLO'
315
+ end
316
+ it "should update the magic attributes" do
317
+ @item.preview_image.process!(:double)
318
+ @item.preview_image_size.should == 10
319
+ end
320
+ it "should work for encode" do
321
+ @item.preview_image.encode!(:egg)
322
+ @item.preview_image.data.should == 'helloegg'
323
+ end
324
+ it "should work repeatedly" do
325
+ @item.preview_image.process!(:double).encode!(:egg)
326
+ @item.preview_image.data.should == 'hellohelloegg'
327
+ @item.preview_image_size.should == 13
328
+ end
329
+ end
330
+ end
331
+
332
+ end
333
+
334
+ describe "validations" do
335
+
336
+ before(:all) do
337
+ @app = Dragonfly[:images]
338
+ @app.define_macro(model_class, :image_accessor)
339
+ end
340
+
341
+ describe "validates_presence_of" do
342
+
343
+ before(:all) do
344
+ Item.class_eval do
345
+ image_accessor :preview_image
346
+ validates_presence_of :preview_image
347
+ end
348
+ end
349
+
350
+ it "should be valid if set" do
351
+ Item.new(:preview_image => "1234567890").should be_valid
352
+ end
353
+
354
+ it "should be invalid if not set" do
355
+ Item.new.should_not be_valid
356
+ end
357
+
358
+ end
359
+
360
+ describe "validates_size_of" do
361
+
362
+ before(:all) do
363
+ Item.class_eval do
364
+ image_accessor :preview_image
365
+ validates_size_of :preview_image, :within => (6..10)
366
+ end
367
+ end
368
+
369
+ it "should be valid if ok" do
370
+ Item.new(:preview_image => "1234567890").should be_valid
371
+ end
372
+
373
+ it "should be invalid if too small" do
374
+ Item.new(:preview_image => "12345").should_not be_valid
375
+ end
376
+
377
+ end
378
+
379
+ describe "validates_property" do
380
+
381
+ before(:each) do
382
+ @item = Item.new(:preview_image => "1234567890")
383
+ end
384
+
385
+ before(:all) do
386
+ custom_analyser = Class.new do
387
+ def mime_type(temp_object)
388
+ case temp_object.data
389
+ when "WRONG TYPE" then 'wrong/type'
390
+ when "OTHER TYPE" then nil
391
+ else 'how/special'
392
+ end
393
+ end
394
+
395
+ def number_of_Gs(temp_object)
396
+ temp_object.data.count('G')
397
+ end
398
+ end
399
+ @app.analyser.register(custom_analyser)
400
+
401
+ Item.class_eval do
402
+ validates_property :mime_type, :of => :preview_image, :in => ['how/special', 'how/crazy'], :if => :its_friday
403
+ validates_property :mime_type, :of => [:other_image, :yet_another_image], :as => 'how/special'
404
+ validates_property :number_of_Gs, :of => :preview_image, :in => (0..2)
405
+ validates_property :mime_type, :of => :otra_imagen, :in => ['que/pasa', 'illo/tio'], :message => "tipo de contenido incorrecto. Que chungo tio"
406
+
407
+ image_accessor :preview_image
408
+ image_accessor :other_image
409
+ image_accessor :yet_another_image
410
+ image_accessor :otra_imagen
411
+
412
+ def its_friday
413
+ true
414
+ end
415
+
416
+ end
417
+ end
418
+
419
+ it "should be valid if nil, if not validated on presence (even with validates_property)" do
420
+ @item.other_image = nil
421
+ @item.should be_valid
422
+ end
423
+
424
+ it "should be invalid if the property is nil" do
425
+ @item.preview_image = "OTHER TYPE"
426
+ @item.should_not be_valid
427
+ @item.errors[:preview_image].should match_ar_error("mime type is incorrect. It needs to be one of 'how/special', 'how/crazy', but was ''")
428
+ end
429
+
430
+ it "should be invalid if the property is wrong" do
431
+ @item.preview_image = "WRONG TYPE"
432
+ @item.should_not be_valid
433
+ @item.errors[:preview_image].should match_ar_error("mime type is incorrect. It needs to be one of 'how/special', 'how/crazy', but was 'wrong/type'")
434
+ end
435
+
436
+ it "should work for a range" do
437
+ @item.preview_image = "GOOGLE GUM"
438
+ @item.should_not be_valid
439
+ @item.errors[:preview_image].should match_ar_error("number of gs is incorrect. It needs to be between 0 and 2, but was '3'")
440
+ end
441
+
442
+ it "should validate individually" do
443
+ @item.other_image = "1234567"
444
+ @item.yet_another_image = "WRONG TYPE"
445
+ @item.should_not be_valid
446
+ @item.errors[:other_image].should match_ar_error(nil)
447
+ @item.errors[:yet_another_image].should match_ar_error("mime type is incorrect. It needs to be 'how/special', but was 'wrong/type'")
448
+ end
449
+
450
+ it "should include standard extra options like 'if' on mime type validation" do
451
+ @item.should_receive(:its_friday).and_return(false)
452
+ @item.preview_image = "WRONG TYPE"
453
+ @item.should be_valid
454
+ end
455
+
456
+ it "should require either :as or :in as an argument" do
457
+ lambda{
458
+ Item.class_eval do
459
+ validates_property :mime_type, :of => :preview_image
460
+ end
461
+ }.should raise_error(ArgumentError)
462
+ end
463
+
464
+ it "should require :of as an argument" do
465
+ lambda{
466
+ Item.class_eval do
467
+ validates_property :mime_type, :as => 'hi/there'
468
+ end
469
+ }.should raise_error(ArgumentError)
470
+ end
471
+
472
+ it "should allow for custom messages" do
473
+ @item.otra_imagen = "WRONG TYPE"
474
+ @item.should_not be_valid
475
+ @item.errors[:otra_imagen].should match_ar_error("tipo de contenido incorrecto. Que chungo tio")
476
+ end
477
+
478
+ end
479
+
480
+ end
481
+
482
+ describe "extra properties" do
483
+
484
+ before(:each) do
485
+ @app = Dragonfly[:images]
486
+ custom_analyser = Class.new do
487
+ def some_analyser_method(temp_object)
488
+ "abc" + temp_object.data[0..0]
489
+ end
490
+ def number_of_As(temp_object); temp_object.data.count('A'); end
491
+ end
492
+ @app.analyser.register(custom_analyser)
493
+ @app.define_macro(model_class, :image_accessor)
494
+ Item.class_eval do
495
+ image_accessor :preview_image
496
+ end
497
+ @item = Item.new
498
+ end
499
+
500
+ describe "magic attributes" do
501
+
502
+ it "should default the magic attribute as nil" do
503
+ @item.preview_image_some_analyser_method.should be_nil
504
+ end
505
+
506
+ it "should set the magic attribute when assigned" do
507
+ @item.preview_image = '123'
508
+ @item.preview_image_some_analyser_method.should == 'abc1'
509
+ end
510
+
511
+ it "should not set non-magic attributes with the same prefix when assigned" do
512
+ @item.preview_image_blah_blah = 'wassup'
513
+ @item.preview_image = '123'
514
+ @item.preview_image_blah_blah.should == 'wassup'
515
+ end
516
+
517
+ it "should update the magic attribute when something else is assigned" do
518
+ @item.preview_image = '123'
519
+ @item.preview_image = '456'
520
+ @item.preview_image_some_analyser_method.should == 'abc4'
521
+ end
522
+
523
+ it "should reset the magic attribute when set to nil" do
524
+ @item.preview_image = '123'
525
+ @item.preview_image = nil
526
+ @item.preview_image_some_analyser_method.should be_nil
527
+ end
528
+
529
+ it "should not reset non-magic attributes with the same prefix when set to nil" do
530
+ @item.preview_image_blah_blah = 'wassup'
531
+ @item.preview_image = '123'
532
+ @item.preview_image = nil
533
+ @item.preview_image_blah_blah.should == 'wassup'
534
+ end
535
+
536
+ it "should work for size too" do
537
+ @item.preview_image = '123'
538
+ @item.preview_image_size.should == 3
539
+ end
540
+
541
+ it "should store the original file extension if it exists" do
542
+ data = 'jasdlkf sadjl'
543
+ data.stub!(:original_filename).and_return('hello.png')
544
+ @item.preview_image = data
545
+ @item.preview_image_ext.should == 'png'
546
+ end
547
+
548
+ it "should store the original file name if it exists" do
549
+ data = 'jasdlkf sadjl'
550
+ data.stub!(:original_filename).and_return('hello.png')
551
+ @item.preview_image = data
552
+ @item.preview_image_name.should == 'hello.png'
553
+ end
554
+ end
555
+
556
+
557
+ describe "delegating methods to the job" do
558
+ before(:each) do
559
+ @item.preview_image = "DATASTRING"
560
+ end
561
+ it "should have properties from the analyser" do
562
+ @item.preview_image.number_of_As.should == 2
563
+ end
564
+ it "should report that it responds to analyser methods" do
565
+ @item.preview_image.respond_to?(:number_of_As).should be_true
566
+ end
567
+ it "should include analyser methods in methods" do
568
+ @item.preview_image.methods.include?('number_of_As'.to_method_name).should be_true
569
+ end
570
+ it "should include analyser methods in public_methods" do
571
+ @item.preview_image.public_methods.include?('number_of_As'.to_method_name).should be_true
572
+ end
573
+
574
+ it "should update when something new is assigned" do
575
+ @item.preview_image = 'ANEWDATASTRING'
576
+ @item.preview_image.number_of_As.should == 3
577
+ end
578
+
579
+ describe "from a new model object" do
580
+ before(:each) do
581
+ @app.datastore.stub!(:store).and_return('my_uid')
582
+ item = Item.create!(:preview_image => 'DATASTRING')
583
+ @item = Item.find(item.id)
584
+ end
585
+ it "should load the content then delegate the method" do
586
+ @app.datastore.should_receive(:retrieve).with('my_uid').and_return(['DATASTRING', {}])
587
+ @item.preview_image.number_of_As.should == 2
588
+ end
589
+ it "should use the magic attribute if there is one, and not load the content" do
590
+ @app.datastore.should_not_receive(:retrieve)
591
+ @item.should_receive(:preview_image_some_analyser_method).and_return('result yo')
592
+ @item.preview_image.some_analyser_method.should == 'result yo'
593
+ end
594
+
595
+ %w(size name ext).each do |attr|
596
+ it "should use the magic attribute for #{attr} if there is one, and not load the content" do
597
+ @app.datastore.should_not_receive(:retrieve)
598
+ @item.should_receive("preview_image_#{attr}".to_sym).and_return('result yo')
599
+ @item.preview_image.send(attr).should == 'result yo'
600
+ end
601
+
602
+ it "should load the content then delegate '#{attr}' if there is no magic attribute for it" do
603
+ @item.should_receive(:public_methods).and_return(['preview_image_uid']) # no magic attributes
604
+ @app.datastore.should_receive(:retrieve).with('my_uid').and_return(['DATASTRING', {}])
605
+ @item.preview_image.send(attr).should == @item.preview_image.send(:job).send(attr)
606
+ end
607
+ end
608
+
609
+ end
610
+
611
+ it "should not raise an error if a non-existent method is called" do
612
+ # Just checking method missing works ok
613
+ lambda{
614
+ @item.preview_image.eggbert
615
+ }.should raise_error(NoMethodError)
616
+ end
617
+ end
618
+
619
+ describe "setting things on the attachment" do
620
+
621
+ before(:each) do
622
+ @item = Item.new
623
+ end
624
+
625
+ describe "name" do
626
+ before(:each) do
627
+ @item.preview_image = "Hello"
628
+ @item.preview_image.name = 'hello.there'
629
+ end
630
+ it "should allow for setting the name" do
631
+ @item.preview_image.name.should == 'hello.there'
632
+ end
633
+ it "should update the magic attribute" do
634
+ @item.preview_image_name.should == 'hello.there'
635
+ end
636
+ it "should return the name" do
637
+ (@item.preview_image.name = 'no.silly').should == 'no.silly'
638
+ end
639
+ end
640
+
641
+ describe "meta" do
642
+ before(:each) do
643
+ @item.preview_image = "Hello all"
644
+ @item.preview_image.meta = {:slime => 'balls'}
645
+ end
646
+ it "should allow for setting the meta" do
647
+ @item.preview_image.meta.should == {:slime => 'balls'}
648
+ end
649
+ it "should allow for updating the meta" do
650
+ @item.preview_image.meta[:numb] = 'nuts'
651
+ @item.preview_image.meta.should == {:slime => 'balls', :numb => 'nuts'}
652
+ end
653
+ it "should return the meta" do
654
+ (@item.preview_image.meta = {:doogs => 'boogs'}).should == {:doogs => 'boogs'}
655
+ end
656
+ it "should save it correctly" do
657
+ @item.save!
658
+ item = Item.find(@item.id)
659
+ item.preview_image.meta.should include_hash(:slime => 'balls')
660
+ end
661
+ it "should store meta info about the model" do
662
+ @item.save!
663
+ meta = {:model_class => 'Item', :model_attachment => :preview_image, :slime => 'balls'}
664
+ @app.fetch(@item.preview_image_uid).meta.should == meta
665
+ end
666
+ end
667
+
668
+ end
669
+
670
+ end
671
+
672
+ describe "inheritance" do
673
+
674
+ before(:all) do
675
+ @app = Dragonfly[:images]
676
+ @app2 = Dragonfly[:egg]
677
+ @app.define_macro(model_class, :image_accessor)
678
+ @app2.define_macro(model_class, :egg_accessor)
679
+ Car.class_eval do
680
+ image_accessor :image
681
+ end
682
+ Photo.class_eval do
683
+ egg_accessor :image
684
+ end
685
+
686
+ @base_class = Car
687
+ class ReliantRobin < Car; image_accessor :reliant_image; end
688
+ @subclass = ReliantRobin
689
+ class ReliantRobinWithModule < Car
690
+ include Module.new
691
+ image_accessor :reliant_image
692
+ end
693
+ @subclass_with_module = ReliantRobinWithModule
694
+ @unrelated_class = Photo
695
+ end
696
+
697
+ it "should allow assigning base class accessors" do
698
+ @base_class.create! :image => 'blah'
699
+ end
700
+ it "should not allow assigning subclass accessors in the base class" do
701
+ @base_class.new.should_not respond_to(:reliant_image=)
702
+ end
703
+ it "should allow assigning base class accessors in the subclass" do
704
+ @subclass.create! :image => 'blah'
705
+ end
706
+ it "should allow assigning subclass accessors in the subclass" do
707
+ @subclass.create! :reliant_image => 'blah'
708
+ end
709
+ it "should allow assigning base class accessors in the subclass, even if it has mixins" do
710
+ @subclass_with_module.create! :image => 'blah'
711
+ end
712
+ it "should allow assigning subclass accessors in the subclass, even if it has mixins" do
713
+ @subclass_with_module.create! :reliant_image => 'blah'
714
+ end
715
+ it "return the correct apps for each accessors, even when names clash" do
716
+ @base_class.dragonfly_apps_for_attributes.should == {:image => @app}
717
+ @subclass.dragonfly_apps_for_attributes.should == {:image => @app, :reliant_image => @app}
718
+ @subclass_with_module.dragonfly_apps_for_attributes.should == {:image => @app, :reliant_image => @app}
719
+ @unrelated_class.dragonfly_apps_for_attributes.should == {:image => @app2}
720
+ end
721
+ end
722
+
723
+ end