oahu-dragonfly 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. data/.rspec +1 -0
  2. data/.yardopts +24 -0
  3. data/Gemfile +30 -0
  4. data/History.md +323 -0
  5. data/LICENSE +20 -0
  6. data/README.md +88 -0
  7. data/Rakefile +50 -0
  8. data/VERSION +1 -0
  9. data/config.ru +14 -0
  10. data/docs.watchr +1 -0
  11. data/dragonfly.gemspec +297 -0
  12. data/extra_docs/Analysers.md +66 -0
  13. data/extra_docs/Caching.md +23 -0
  14. data/extra_docs/Configuration.md +124 -0
  15. data/extra_docs/Couch.md +49 -0
  16. data/extra_docs/DataStorage.md +153 -0
  17. data/extra_docs/Encoding.md +67 -0
  18. data/extra_docs/GeneralUsage.md +121 -0
  19. data/extra_docs/Generators.md +60 -0
  20. data/extra_docs/Heroku.md +50 -0
  21. data/extra_docs/ImageMagick.md +125 -0
  22. data/extra_docs/Index.md +33 -0
  23. data/extra_docs/MimeTypes.md +40 -0
  24. data/extra_docs/Models.md +272 -0
  25. data/extra_docs/Mongo.md +45 -0
  26. data/extra_docs/Processing.md +77 -0
  27. data/extra_docs/Rack.md +52 -0
  28. data/extra_docs/Rails2.md +57 -0
  29. data/extra_docs/Rails3.md +62 -0
  30. data/extra_docs/Sinatra.md +25 -0
  31. data/extra_docs/URLs.md +169 -0
  32. data/features/images.feature +47 -0
  33. data/features/no_processing.feature +14 -0
  34. data/features/rails_3.0.5.feature +8 -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 +28 -0
  38. data/features/support/env.rb +13 -0
  39. data/features/support/setup.rb +32 -0
  40. data/fixtures/rails_3.0.5/files/app/models/album.rb +7 -0
  41. data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
  42. data/fixtures/rails_3.0.5/files/app/views/albums/show.html.erb +6 -0
  43. data/fixtures/rails_3.0.5/files/config/initializers/dragonfly.rb +4 -0
  44. data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
  45. data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
  46. data/fixtures/rails_3.0.5/files/features/step_definitions/image_steps.rb +25 -0
  47. data/fixtures/rails_3.0.5/files/features/support/paths.rb +17 -0
  48. data/fixtures/rails_3.0.5/files/features/text_images.feature +7 -0
  49. data/fixtures/rails_3.0.5/template.rb +20 -0
  50. data/irbrc.rb +18 -0
  51. data/lib/dragonfly.rb +55 -0
  52. data/lib/dragonfly/active_model_extensions.rb +13 -0
  53. data/lib/dragonfly/active_model_extensions/attachment.rb +250 -0
  54. data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +148 -0
  55. data/lib/dragonfly/active_model_extensions/class_methods.rb +95 -0
  56. data/lib/dragonfly/active_model_extensions/instance_methods.rb +28 -0
  57. data/lib/dragonfly/active_model_extensions/validations.rb +41 -0
  58. data/lib/dragonfly/analyser.rb +58 -0
  59. data/lib/dragonfly/analysis/file_command_analyser.rb +32 -0
  60. data/lib/dragonfly/analysis/image_magick_analyser.rb +6 -0
  61. data/lib/dragonfly/app.rb +172 -0
  62. data/lib/dragonfly/config/heroku.rb +19 -0
  63. data/lib/dragonfly/config/image_magick.rb +6 -0
  64. data/lib/dragonfly/config/rails.rb +20 -0
  65. data/lib/dragonfly/configurable.rb +207 -0
  66. data/lib/dragonfly/core_ext/array.rb +7 -0
  67. data/lib/dragonfly/core_ext/hash.rb +7 -0
  68. data/lib/dragonfly/core_ext/object.rb +12 -0
  69. data/lib/dragonfly/core_ext/string.rb +9 -0
  70. data/lib/dragonfly/core_ext/symbol.rb +9 -0
  71. data/lib/dragonfly/data_storage.rb +9 -0
  72. data/lib/dragonfly/data_storage/couch_data_store.rb +64 -0
  73. data/lib/dragonfly/data_storage/file_data_store.rb +141 -0
  74. data/lib/dragonfly/data_storage/mongo_data_store.rb +86 -0
  75. data/lib/dragonfly/data_storage/s3data_store.rb +145 -0
  76. data/lib/dragonfly/encoder.rb +13 -0
  77. data/lib/dragonfly/encoding/image_magick_encoder.rb +6 -0
  78. data/lib/dragonfly/function_manager.rb +71 -0
  79. data/lib/dragonfly/generation/image_magick_generator.rb +6 -0
  80. data/lib/dragonfly/generator.rb +9 -0
  81. data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
  82. data/lib/dragonfly/image_magick/analyser.rb +51 -0
  83. data/lib/dragonfly/image_magick/config.rb +41 -0
  84. data/lib/dragonfly/image_magick/encoder.rb +57 -0
  85. data/lib/dragonfly/image_magick/generator.rb +145 -0
  86. data/lib/dragonfly/image_magick/processor.rb +99 -0
  87. data/lib/dragonfly/image_magick/utils.rb +72 -0
  88. data/lib/dragonfly/image_magick_utils.rb +4 -0
  89. data/lib/dragonfly/job.rb +451 -0
  90. data/lib/dragonfly/job_builder.rb +39 -0
  91. data/lib/dragonfly/job_definitions.rb +26 -0
  92. data/lib/dragonfly/job_endpoint.rb +15 -0
  93. data/lib/dragonfly/loggable.rb +28 -0
  94. data/lib/dragonfly/middleware.rb +20 -0
  95. data/lib/dragonfly/processing/image_magick_processor.rb +6 -0
  96. data/lib/dragonfly/processor.rb +9 -0
  97. data/lib/dragonfly/rails/images.rb +27 -0
  98. data/lib/dragonfly/response.rb +97 -0
  99. data/lib/dragonfly/routed_endpoint.rb +40 -0
  100. data/lib/dragonfly/serializer.rb +32 -0
  101. data/lib/dragonfly/server.rb +113 -0
  102. data/lib/dragonfly/simple_cache.rb +23 -0
  103. data/lib/dragonfly/temp_object.rb +175 -0
  104. data/lib/dragonfly/url_mapper.rb +78 -0
  105. data/samples/beach.png +0 -0
  106. data/samples/egg.png +0 -0
  107. data/samples/round.gif +0 -0
  108. data/samples/sample.docx +0 -0
  109. data/samples/taj.jpg +0 -0
  110. data/spec/dragonfly/active_model_extensions/model_spec.rb +1426 -0
  111. data/spec/dragonfly/active_model_extensions/spec_helper.rb +91 -0
  112. data/spec/dragonfly/analyser_spec.rb +123 -0
  113. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +48 -0
  114. data/spec/dragonfly/app_spec.rb +135 -0
  115. data/spec/dragonfly/configurable_spec.rb +461 -0
  116. data/spec/dragonfly/core_ext/array_spec.rb +19 -0
  117. data/spec/dragonfly/core_ext/hash_spec.rb +19 -0
  118. data/spec/dragonfly/core_ext/string_spec.rb +17 -0
  119. data/spec/dragonfly/core_ext/symbol_spec.rb +17 -0
  120. data/spec/dragonfly/data_storage/couch_data_store_spec.rb +76 -0
  121. data/spec/dragonfly/data_storage/file_data_store_spec.rb +296 -0
  122. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +57 -0
  123. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +258 -0
  124. data/spec/dragonfly/data_storage/shared_data_store_examples.rb +77 -0
  125. data/spec/dragonfly/function_manager_spec.rb +154 -0
  126. data/spec/dragonfly/hash_with_css_style_keys_spec.rb +24 -0
  127. data/spec/dragonfly/image_magick/analyser_spec.rb +64 -0
  128. data/spec/dragonfly/image_magick/encoder_spec.rb +41 -0
  129. data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
  130. data/spec/dragonfly/image_magick/processor_spec.rb +233 -0
  131. data/spec/dragonfly/image_magick/utils_spec.rb +18 -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 +173 -0
  135. data/spec/dragonfly/job_spec.rb +1046 -0
  136. data/spec/dragonfly/loggable_spec.rb +80 -0
  137. data/spec/dragonfly/middleware_spec.rb +47 -0
  138. data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
  139. data/spec/dragonfly/serializer_spec.rb +61 -0
  140. data/spec/dragonfly/server_spec.rb +278 -0
  141. data/spec/dragonfly/simple_cache_spec.rb +27 -0
  142. data/spec/dragonfly/temp_object_spec.rb +306 -0
  143. data/spec/dragonfly/url_mapper_spec.rb +126 -0
  144. data/spec/functional/deprecations_spec.rb +51 -0
  145. data/spec/functional/image_magick_app_spec.rb +27 -0
  146. data/spec/functional/model_urls_spec.rb +85 -0
  147. data/spec/functional/remote_on_the_fly_spec.rb +51 -0
  148. data/spec/functional/to_response_spec.rb +31 -0
  149. data/spec/spec_helper.rb +51 -0
  150. data/spec/support/argument_matchers.rb +19 -0
  151. data/spec/support/image_matchers.rb +47 -0
  152. data/spec/support/simple_matchers.rb +53 -0
  153. data/yard/handlers/configurable_attr_handler.rb +38 -0
  154. data/yard/setup.rb +15 -0
  155. data/yard/templates/default/fulldoc/html/css/common.css +107 -0
  156. data/yard/templates/default/layout/html/layout.erb +89 -0
  157. data/yard/templates/default/module/html/configuration_summary.erb +31 -0
  158. data/yard/templates/default/module/setup.rb +17 -0
  159. metadata +544 -0
@@ -0,0 +1,13 @@
1
+ module Dragonfly
2
+
3
+ module ActiveModelExtensions
4
+
5
+ def self.extended(klass)
6
+ unless klass.include?(InstanceMethods)
7
+ klass.extend(ClassMethods)
8
+ klass.class_eval{ include InstanceMethods }
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,250 @@
1
+ require 'forwardable'
2
+ require 'dragonfly/active_model_extensions/attachment_class_methods'
3
+
4
+ module Dragonfly
5
+ module ActiveModelExtensions
6
+
7
+ class Attachment
8
+
9
+ # Exceptions
10
+ class BadAssignmentKey < RuntimeError; end
11
+
12
+ extend Forwardable
13
+ def_delegators :job,
14
+ :data, :to_file, :file, :tempfile, :path,
15
+ :process, :encode, :analyse,
16
+ :meta, :meta=,
17
+ :name, :basename, :ext, :size,
18
+ :url
19
+
20
+ def initialize(model)
21
+ @model = model
22
+ self.uid = model_uid
23
+ update_from_uid if uid
24
+ @should_run_callbacks = true
25
+ self.class.ensure_uses_cached_magic_attributes
26
+ end
27
+
28
+ def app
29
+ self.class.app
30
+ end
31
+
32
+ def attribute
33
+ self.class.attribute
34
+ end
35
+
36
+ def assign(value)
37
+ self.changed = true
38
+ destroy_retained! if retained?
39
+ set_uid_and_model_uid(nil)
40
+ if value.nil?
41
+ self.job = nil
42
+ reset_magic_attributes
43
+ self.class.run_callbacks(:after_unassign, model, self) if should_run_callbacks?
44
+ else
45
+ self.job = case value
46
+ when Job then value.dup
47
+ when self.class then value.job.dup
48
+ else app.new_job(value)
49
+ end
50
+ set_magic_attributes
51
+ update_meta
52
+ self.class.run_callbacks(:after_assign, model, self) if should_run_callbacks?
53
+ end
54
+ value
55
+ end
56
+
57
+ def changed?
58
+ !!@changed
59
+ end
60
+
61
+ def destroy!
62
+ destroy_previous!
63
+ destroy_content(uid) if uid
64
+ end
65
+
66
+ def save!
67
+ sync_with_model
68
+ store_job! if job && !uid
69
+ destroy_previous!
70
+ self.changed = false
71
+ self.retained = false
72
+ end
73
+
74
+ def to_value
75
+ self if job
76
+ end
77
+
78
+ def name=(name)
79
+ job.name = name
80
+ set_magic_attribute(:name, name) if has_magic_attribute_for?(:name)
81
+ name
82
+ end
83
+
84
+ def process!(*args)
85
+ assign(process(*args))
86
+ self
87
+ end
88
+
89
+ def encode!(*args)
90
+ assign(encode(*args))
91
+ self
92
+ end
93
+
94
+ def remote_url(opts={})
95
+ app.remote_url_for(uid, opts) if uid
96
+ end
97
+
98
+ def apply
99
+ job.apply
100
+ self
101
+ end
102
+
103
+ attr_writer :should_run_callbacks
104
+
105
+ def should_run_callbacks?
106
+ @should_run_callbacks
107
+ end
108
+
109
+ # Retaining for avoiding uploading more than once
110
+
111
+ def retain!
112
+ if changed?
113
+ store_job!
114
+ self.retained = true
115
+ end
116
+ end
117
+
118
+ def retained?
119
+ !!@retained
120
+ end
121
+
122
+ def destroy_retained!
123
+ destroy_content(retained_attrs[:uid])
124
+ end
125
+
126
+ def retained_attrs
127
+ attribute_keys.inject({}) do |hash, key|
128
+ hash[key] = send(key)
129
+ hash
130
+ end if retained?
131
+ end
132
+
133
+ def retained_attrs=(attrs)
134
+ if changed? # if already set, ignore and destroy this retained content
135
+ destroy_content(attrs[:uid])
136
+ else
137
+ attrs.each do |key, value|
138
+ unless attribute_keys.include?(key)
139
+ raise BadAssignmentKey, "trying to call #{attribute}_#{key} = #{value.inspect} via retained_#{attribute} but this is not allowed!"
140
+ end
141
+ model.send("#{attribute}_#{key}=", value)
142
+ end
143
+ sync_with_model
144
+ update_from_uid
145
+ self.retained = true
146
+ end
147
+ end
148
+
149
+ def inspect
150
+ "<Dragonfly Attachment uid=#{uid.inspect}, app=#{app.inspect}>"
151
+ end
152
+
153
+ protected
154
+
155
+ attr_reader :job
156
+
157
+ private
158
+
159
+ attr_writer :changed, :retained
160
+
161
+ def attribute_keys
162
+ [:uid] + magic_attributes
163
+ end
164
+
165
+ def store_job!
166
+ opts = self.class.evaluate_storage_opts(model, self)
167
+ set_uid_and_model_uid job.store(opts)
168
+ self.job = job.to_fetched_job(uid)
169
+ end
170
+
171
+ def destroy_content(uid)
172
+ app.datastore.destroy(uid)
173
+ rescue DataStorage::DataNotFound => e
174
+ app.log.warn("*** WARNING ***: tried to destroy data with uid #{uid}, but got error: #{e}")
175
+ end
176
+
177
+ def destroy_previous!
178
+ if previous_uid
179
+ destroy_content(previous_uid)
180
+ self.previous_uid = nil
181
+ end
182
+ end
183
+
184
+ def sync_with_model
185
+ # If the model uid has been set manually
186
+ if uid != model_uid
187
+ self.uid = model_uid
188
+ end
189
+ end
190
+
191
+ def set_uid_and_model_uid(uid)
192
+ self.uid = uid
193
+ self.model_uid = uid
194
+ end
195
+
196
+ def model_uid=(uid)
197
+ model.send("#{attribute}_uid=", uid)
198
+ end
199
+
200
+ def model_uid
201
+ model.send("#{attribute}_uid")
202
+ end
203
+
204
+ attr_reader :model, :uid
205
+ attr_writer :job
206
+ attr_accessor :previous_uid
207
+
208
+ def uid=(uid)
209
+ self.previous_uid = @uid if @uid
210
+ @uid = uid
211
+ end
212
+
213
+ def update_meta
214
+ magic_attributes.each{|property| meta[property] = model.send("#{attribute}_#{property}") }
215
+ meta[:model_class] = model.class.name
216
+ meta[:model_attachment] = attribute
217
+ end
218
+
219
+ def update_from_uid
220
+ self.job = app.fetch(uid)
221
+ update_meta
222
+ end
223
+
224
+ def magic_attributes
225
+ self.class.magic_attributes
226
+ end
227
+
228
+ def set_magic_attribute(property, value)
229
+ model.send("#{attribute}_#{property}=", value)
230
+ end
231
+
232
+ def set_magic_attributes
233
+ magic_attributes.each{|property| set_magic_attribute(property, job.send(property)) }
234
+ end
235
+
236
+ def reset_magic_attributes
237
+ magic_attributes.each{|property| set_magic_attribute(property, nil) }
238
+ end
239
+
240
+ def has_magic_attribute_for?(property)
241
+ magic_attributes.include?(property.to_sym)
242
+ end
243
+
244
+ def magic_attribute_for(property)
245
+ model.send("#{attribute}_#{property}")
246
+ end
247
+
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,148 @@
1
+ module Dragonfly
2
+ module ActiveModelExtensions
3
+ class Attachment
4
+ class << self
5
+
6
+ class ConfigProxy
7
+
8
+ def initialize(spec, block)
9
+ @spec = spec
10
+ instance_eval(&block)
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :spec
16
+
17
+ def after_assign(*callbacks, &block)
18
+ add_callbacks(:after_assign, *callbacks, &block)
19
+ end
20
+
21
+ def after_unassign(*callbacks, &block)
22
+ add_callbacks(:after_unassign, *callbacks, &block)
23
+ end
24
+
25
+ def copy_to(accessor, &block)
26
+ after_assign do |a|
27
+ self.send "#{accessor}=", (block_given? ? instance_exec(a, &block) : a)
28
+ end
29
+ after_unassign{|a| self.send("#{accessor}=", nil) }
30
+ end
31
+
32
+ def retain
33
+ after_assign{|a| a.retain! }
34
+ end
35
+
36
+ def storage_opts(opts=nil, &block)
37
+ spec.storage_opts_specs << (opts || block)
38
+ end
39
+
40
+ def storage_opt(key, value, &block)
41
+ if value.is_a? Symbol
42
+ storage_opts{|a| {key => send(value)} }
43
+ elsif block_given?
44
+ storage_opts{|a| {key => instance_exec(a, &block)} }
45
+ else
46
+ storage_opts{|a| {key => value} }
47
+ end
48
+ end
49
+
50
+ def add_callbacks(name, *callbacks, &block)
51
+ if block_given?
52
+ spec.callbacks[name] << block
53
+ else
54
+ spec.callbacks[name].push(*callbacks)
55
+ end
56
+ end
57
+
58
+ def method_missing(meth, *args, &block)
59
+ if meth.to_s =~ /^storage_(.*)$/
60
+ storage_opt($1.to_sym, args.first, &block)
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ def init(model_class, attribute, app, config_block)
69
+ @model_class, @attribute, @app, @config_block = model_class, attribute, app, config_block
70
+ include app.analyser.analysis_methods
71
+ include app.job_definitions
72
+ define_method :format do
73
+ job.format
74
+ end
75
+ define_method :mime_type do
76
+ job.mime_type
77
+ end
78
+ ConfigProxy.new(self, config_block) if config_block
79
+ self
80
+ end
81
+
82
+ attr_reader :model_class, :attribute, :app, :config_block
83
+
84
+ # Callbacks
85
+ def callbacks
86
+ @callbacks ||= Hash.new{|h,k| h[k] = [] }
87
+ end
88
+
89
+ def run_callbacks(name, model, attachment)
90
+ attachment.should_run_callbacks = false
91
+ callbacks[name].each do |callback|
92
+ case callback
93
+ when Symbol then model.send(callback)
94
+ when Proc then model.instance_exec(attachment, &callback)
95
+ end
96
+ end
97
+ attachment.should_run_callbacks = true
98
+ end
99
+
100
+ # Magic attributes
101
+ def allowed_magic_attributes
102
+ app.analyser.analysis_method_names + [:size, :basename, :name, :ext, :meta]
103
+ end
104
+
105
+ def magic_attributes
106
+ @magic_attributes ||= begin
107
+ prefix = attribute.to_s + '_'
108
+ model_class.public_instance_methods.inject([]) do |attrs, name|
109
+ _, __, suffix = name.to_s.partition(prefix)
110
+ if !suffix.empty? && allowed_magic_attributes.include?(suffix.to_sym)
111
+ attrs << suffix.to_sym
112
+ end
113
+ attrs
114
+ end
115
+ end
116
+ end
117
+
118
+ def ensure_uses_cached_magic_attributes
119
+ return if @uses_cached_magic_attributes
120
+ magic_attributes.each do |attr|
121
+ define_method attr do
122
+ magic_attribute_for(attr)
123
+ end
124
+ end
125
+ @uses_cached_magic_attributes = true
126
+ end
127
+
128
+ # Storage options
129
+ def storage_opts_specs
130
+ @storage_opts_specs ||= []
131
+ end
132
+
133
+ def evaluate_storage_opts(model, attachment)
134
+ storage_opts_specs.inject({}) do |opts, spec|
135
+ options = case spec
136
+ when Proc then model.instance_exec(attachment, &spec)
137
+ when Symbol then model.send(spec)
138
+ else spec
139
+ end
140
+ opts.merge!(options)
141
+ opts
142
+ end
143
+ end
144
+
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,95 @@
1
+ module Dragonfly
2
+ module ActiveModelExtensions
3
+ module ClassMethods
4
+
5
+ include Validations
6
+
7
+ def register_dragonfly_app(macro_name, app)
8
+ (class << self; self; end).class_eval do
9
+
10
+ # Defines e.g. 'image_accessor' for any activemodel class body
11
+ define_method macro_name do |attribute, &config_block|
12
+
13
+ # Add callbacks
14
+ before_save :save_dragonfly_attachments
15
+ before_destroy :destroy_dragonfly_attachments
16
+
17
+ # Register the new attribute
18
+ dragonfly_attachment_classes << new_dragonfly_attachment_class(attribute, app, config_block)
19
+
20
+ # Define the setter for the attribute
21
+ define_method "#{attribute}=" do |value|
22
+ dragonfly_attachments[attribute].assign(value)
23
+ end
24
+
25
+ # Define the getter for the attribute
26
+ define_method attribute do
27
+ dragonfly_attachments[attribute].to_value
28
+ end
29
+
30
+ # Define the URL setter
31
+ define_method "#{attribute}_url=" do |url|
32
+ unless url.blank?
33
+ dragonfly_attachments[attribute].assign(app.fetch_url(url))
34
+ end
35
+ end
36
+
37
+ # Define the URL getter
38
+ define_method "#{attribute}_url" do
39
+ nil
40
+ end
41
+
42
+ # Define the remove setter
43
+ define_method "remove_#{attribute}=" do |value|
44
+ unless [0, "0", false, "false", "", nil].include?(value)
45
+ dragonfly_attachments[attribute].assign(nil)
46
+ instance_variable_set("@remove_#{attribute}", true)
47
+ end
48
+ end
49
+
50
+ # Define the remove getter
51
+ attr_reader "remove_#{attribute}"
52
+
53
+ # Define the retained setter
54
+ define_method "retained_#{attribute}=" do |string|
55
+ unless string.blank?
56
+ begin
57
+ dragonfly_attachments[attribute].retained_attrs = Serializer.marshal_decode(string)
58
+ rescue Serializer::BadString => e
59
+ app.log.warn("*** WARNING ***: couldn't update attachment with serialized retained_#{attribute} string #{string.inspect}")
60
+ end
61
+ end
62
+ end
63
+
64
+ # Define the retained getter
65
+ define_method "retained_#{attribute}" do
66
+ attrs = dragonfly_attachments[attribute].retained_attrs
67
+ Serializer.marshal_encode(attrs) if attrs
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+ app
74
+ end
75
+
76
+ def dragonfly_attachment_classes
77
+ @dragonfly_attachment_classes ||= begin
78
+ parent_class = ancestors.select{|a| a.is_a?(Class) }[1]
79
+ if parent_class.respond_to?(:dragonfly_attachment_classes)
80
+ parent_class.dragonfly_attachment_classes.map do |klass|
81
+ new_dragonfly_attachment_class(klass.attribute, klass.app, klass.config_block)
82
+ end
83
+ else
84
+ []
85
+ end
86
+ end
87
+ end
88
+
89
+ def new_dragonfly_attachment_class(attribute, app, config_block)
90
+ Class.new(Attachment).init(self, attribute, app, config_block)
91
+ end
92
+
93
+ end
94
+ end
95
+ end