oahu-dragonfly 0.8.2

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 (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