dragonfly 0.8.6 → 0.9.0

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 (155) hide show
  1. data/{.specopts → .rspec} +0 -1
  2. data/.yardopts +6 -2
  3. data/Gemfile +14 -13
  4. data/History.md +47 -9
  5. data/README.md +25 -5
  6. data/Rakefile +37 -79
  7. data/VERSION +1 -1
  8. data/dragonfly.gemspec +140 -89
  9. data/extra_docs/Analysers.md +8 -48
  10. data/extra_docs/Configuration.md +40 -25
  11. data/extra_docs/Couch.md +49 -0
  12. data/extra_docs/DataStorage.md +94 -24
  13. data/extra_docs/Encoding.md +6 -35
  14. data/extra_docs/ExampleUseCases.md +113 -0
  15. data/extra_docs/GeneralUsage.md +7 -23
  16. data/extra_docs/Generators.md +15 -49
  17. data/extra_docs/Heroku.md +7 -8
  18. data/extra_docs/ImageMagick.md +126 -0
  19. data/extra_docs/MimeTypes.md +3 -3
  20. data/extra_docs/Models.md +163 -0
  21. data/extra_docs/Mongo.md +1 -4
  22. data/extra_docs/Processing.md +7 -60
  23. data/extra_docs/Rails2.md +3 -1
  24. data/extra_docs/Rails3.md +2 -10
  25. data/extra_docs/ServingRemotely.md +83 -0
  26. data/extra_docs/Sinatra.md +3 -3
  27. data/extra_docs/URLs.md +60 -33
  28. data/features/rails_3.0.5.feature +8 -0
  29. data/features/steps/rails_steps.rb +7 -18
  30. data/features/support/env.rb +10 -37
  31. data/features/support/setup.rb +32 -0
  32. data/fixtures/rails_3.0.5/files/app/models/album.rb +5 -0
  33. data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
  34. data/fixtures/{files → rails_3.0.5/files}/app/views/albums/show.html.erb +2 -0
  35. data/fixtures/{files → rails_3.0.5/files}/config/initializers/dragonfly.rb +0 -0
  36. data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
  37. data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
  38. data/fixtures/{files → rails_3.0.5/files}/features/step_definitions/image_steps.rb +11 -1
  39. data/fixtures/{files → rails_3.0.5/files}/features/support/paths.rb +2 -0
  40. data/fixtures/{files → rails_3.0.5/files}/features/text_images.feature +0 -0
  41. data/fixtures/{rails_3.0.3 → rails_3.0.5}/template.rb +2 -2
  42. data/irbrc.rb +2 -1
  43. data/lib/dragonfly.rb +7 -0
  44. data/lib/dragonfly/active_model_extensions/attachment.rb +134 -46
  45. data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +144 -0
  46. data/lib/dragonfly/active_model_extensions/class_methods.rb +62 -9
  47. data/lib/dragonfly/active_model_extensions/instance_methods.rb +2 -2
  48. data/lib/dragonfly/active_model_extensions/validations.rb +10 -6
  49. data/lib/dragonfly/analyser.rb +0 -1
  50. data/lib/dragonfly/analysis/file_command_analyser.rb +1 -1
  51. data/lib/dragonfly/analysis/image_magick_analyser.rb +2 -43
  52. data/lib/dragonfly/app.rb +64 -55
  53. data/lib/dragonfly/config/heroku.rb +1 -1
  54. data/lib/dragonfly/config/image_magick.rb +2 -37
  55. data/lib/dragonfly/config/rails.rb +5 -2
  56. data/lib/dragonfly/configurable.rb +115 -35
  57. data/lib/dragonfly/core_ext/object.rb +1 -1
  58. data/lib/dragonfly/core_ext/string.rb +1 -1
  59. data/lib/dragonfly/data_storage/couch_data_store.rb +84 -0
  60. data/lib/dragonfly/data_storage/file_data_store.rb +43 -18
  61. data/lib/dragonfly/data_storage/mongo_data_store.rb +8 -4
  62. data/lib/dragonfly/data_storage/s3data_store.rb +82 -38
  63. data/lib/dragonfly/encoding/image_magick_encoder.rb +2 -53
  64. data/lib/dragonfly/function_manager.rb +4 -2
  65. data/lib/dragonfly/generation/image_magick_generator.rb +2 -136
  66. data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
  67. data/lib/dragonfly/image_magick/analyser.rb +51 -0
  68. data/lib/dragonfly/image_magick/config.rb +44 -0
  69. data/lib/dragonfly/{encoding/r_magick_encoder.rb → image_magick/encoder.rb} +10 -14
  70. data/lib/dragonfly/image_magick/generator.rb +145 -0
  71. data/lib/dragonfly/image_magick/processor.rb +104 -0
  72. data/lib/dragonfly/image_magick/utils.rb +72 -0
  73. data/lib/dragonfly/image_magick_utils.rb +2 -79
  74. data/lib/dragonfly/job.rb +152 -90
  75. data/lib/dragonfly/middleware.rb +5 -19
  76. data/lib/dragonfly/processing/image_magick_processor.rb +2 -95
  77. data/lib/dragonfly/rails/images.rb +15 -10
  78. data/lib/dragonfly/response.rb +26 -12
  79. data/lib/dragonfly/serializer.rb +1 -4
  80. data/lib/dragonfly/server.rb +103 -0
  81. data/lib/dragonfly/temp_object.rb +56 -101
  82. data/lib/dragonfly/url_mapper.rb +78 -0
  83. data/spec/dragonfly/active_model_extensions/model_spec.rb +772 -65
  84. data/spec/dragonfly/active_model_extensions/spec_helper.rb +90 -10
  85. data/spec/dragonfly/analyser_spec.rb +1 -1
  86. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +5 -14
  87. data/spec/dragonfly/app_spec.rb +35 -180
  88. data/spec/dragonfly/configurable_spec.rb +259 -18
  89. data/spec/dragonfly/core_ext/string_spec.rb +2 -2
  90. data/spec/dragonfly/core_ext/symbol_spec.rb +1 -1
  91. data/spec/dragonfly/data_storage/couch_data_store_spec.rb +84 -0
  92. data/spec/dragonfly/data_storage/file_data_store_spec.rb +149 -22
  93. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +21 -2
  94. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +207 -43
  95. data/spec/dragonfly/data_storage/{data_store_spec.rb → shared_data_store_examples.rb} +16 -15
  96. data/spec/dragonfly/function_manager_spec.rb +2 -2
  97. data/spec/dragonfly/{generation/hash_with_css_style_keys_spec.rb → hash_with_css_style_keys_spec.rb} +2 -2
  98. data/spec/dragonfly/{analysis/shared_analyser_spec.rb → image_magick/analyser_spec.rb} +19 -6
  99. data/spec/dragonfly/{encoding/image_magick_encoder_spec.rb → image_magick/encoder_spec.rb} +2 -2
  100. data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
  101. data/spec/dragonfly/{processing/shared_processing_spec.rb → image_magick/processor_spec.rb} +55 -6
  102. data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
  103. data/spec/dragonfly/job_builder_spec.rb +1 -1
  104. data/spec/dragonfly/job_definitions_spec.rb +1 -1
  105. data/spec/dragonfly/job_endpoint_spec.rb +26 -3
  106. data/spec/dragonfly/job_spec.rb +426 -208
  107. data/spec/dragonfly/loggable_spec.rb +2 -2
  108. data/spec/dragonfly/middleware_spec.rb +5 -26
  109. data/spec/dragonfly/routed_endpoint_spec.rb +1 -1
  110. data/spec/dragonfly/serializer_spec.rb +1 -14
  111. data/spec/dragonfly/server_spec.rb +261 -0
  112. data/spec/dragonfly/simple_cache_spec.rb +1 -1
  113. data/spec/dragonfly/temp_object_spec.rb +84 -130
  114. data/spec/dragonfly/url_mapper_spec.rb +130 -0
  115. data/spec/functional/deprecations_spec.rb +51 -0
  116. data/spec/functional/image_magick_app_spec.rb +27 -0
  117. data/spec/functional/model_urls_spec.rb +85 -0
  118. data/spec/functional/remote_on_the_fly_spec.rb +51 -0
  119. data/spec/functional/to_response_spec.rb +31 -0
  120. data/spec/spec_helper.rb +12 -22
  121. data/spec/{argument_matchers.rb → support/argument_matchers.rb} +0 -0
  122. data/spec/{image_matchers.rb → support/image_matchers.rb} +4 -4
  123. data/spec/support/simple_matchers.rb +53 -0
  124. data/yard/handlers/configurable_attr_handler.rb +2 -2
  125. data/yard/templates/default/fulldoc/html/css/common.css +12 -10
  126. data/yard/templates/default/layout/html/layout.erb +6 -0
  127. metadata +267 -308
  128. data/Gemfile.rails.2.3.5 +0 -20
  129. data/features/3.0.3.feature +0 -8
  130. data/features/rails_2.3.5.feature +0 -7
  131. data/fixtures/files/app/models/album.rb +0 -3
  132. data/fixtures/files/app/views/albums/new.html.erb +0 -4
  133. data/fixtures/files/features/manage_album_images.feature +0 -12
  134. data/fixtures/rails_2.3.5/template.rb +0 -10
  135. data/lib/dragonfly/analysis/r_magick_analyser.rb +0 -63
  136. data/lib/dragonfly/config/r_magick.rb +0 -46
  137. data/lib/dragonfly/generation/hash_with_css_style_keys.rb +0 -23
  138. data/lib/dragonfly/generation/r_magick_generator.rb +0 -155
  139. data/lib/dragonfly/processing/r_magick_processor.rb +0 -126
  140. data/lib/dragonfly/r_magick_utils.rb +0 -48
  141. data/lib/dragonfly/simple_endpoint.rb +0 -76
  142. data/spec/dragonfly/active_model_extensions/active_model_setup.rb +0 -97
  143. data/spec/dragonfly/active_model_extensions/active_record_setup.rb +0 -85
  144. data/spec/dragonfly/analysis/image_magick_analyser_spec.rb +0 -15
  145. data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +0 -31
  146. data/spec/dragonfly/config/r_magick_spec.rb +0 -29
  147. data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +0 -41
  148. data/spec/dragonfly/generation/image_magick_generator_spec.rb +0 -12
  149. data/spec/dragonfly/generation/r_magick_generator_spec.rb +0 -28
  150. data/spec/dragonfly/generation/shared_generator_spec.rb +0 -91
  151. data/spec/dragonfly/image_magick_utils_spec.rb +0 -16
  152. data/spec/dragonfly/processing/image_magick_processor_spec.rb +0 -29
  153. data/spec/dragonfly/processing/r_magick_processor_spec.rb +0 -30
  154. data/spec/dragonfly/simple_endpoint_spec.rb +0 -97
  155. data/spec/simple_matchers.rb +0 -44
@@ -1,40 +1,13 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
- require 'dragonfly'
3
- require 'spec/expectations'
4
- require 'test/unit/assertions'
5
- require File.dirname(__FILE__) + '/../../spec/image_matchers.rb'
6
-
7
- ROOT_PATH = File.expand_path(File.dirname(__FILE__) + "/../..")
8
-
9
- # A hash of <name for reference> => <dragonfly uid> pairs
10
- TEMP_FILES = {}
11
-
12
- Dragonfly[:images].configure_with(:imagemagick)
13
- Dragonfly[:files].configure do |c|
14
- c.analyser.register(Dragonfly::Analysis::FileCommandAnalyser)
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :cucumber)
4
+ rescue Bundler::BundlerError => e
5
+ $stderr.puts e.message
6
+ $stderr.puts "Run `bundle install` to install missing gems"
7
+ exit e.status_code
15
8
  end
16
9
 
17
- SAMPLE_IMAGE_PATH = File.dirname(__FILE__)+'/../../samples/beach.png'
18
-
19
- Before do
20
- # Remove temporary images
21
- TEMP_FILES.each do |name, uid|
22
- $app.datastore.destroy(uid)
23
- TEMP_FILES.delete(name)
24
- end
25
- end
26
-
27
- AfterStep do |scenario|
28
- Dir["#{ROOT_PATH}/Gemfile*.lock"].each{|f| FileUtils.rm_f(f) }
29
- end
30
-
31
- module MyHelpers
32
-
33
- def make_request(job)
34
- request = Rack::MockRequest.new($app)
35
- @response = request.get(job.url)
36
- end
37
-
38
- end
10
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
11
+ require 'dragonfly'
39
12
 
40
- World(MyHelpers)
13
+ require 'rspec/expectations'
@@ -0,0 +1,32 @@
1
+ ROOT_PATH = File.expand_path(File.dirname(__FILE__) + "/../..")
2
+
3
+ require ROOT_PATH + '/spec/support/image_matchers.rb'
4
+
5
+ # A hash of <name for reference> => <dragonfly uid> pairs
6
+ TEMP_FILES = {}
7
+
8
+ Dragonfly[:images].configure_with(:imagemagick)
9
+ Dragonfly[:files].configure do |c|
10
+ c.analyser.register(Dragonfly::Analysis::FileCommandAnalyser)
11
+ end
12
+
13
+ SAMPLE_IMAGE_PATH = ROOT_PATH + '/samples/beach.png'
14
+
15
+ Before do
16
+ # Remove temporary images
17
+ TEMP_FILES.each do |name, uid|
18
+ $app.datastore.destroy(uid)
19
+ TEMP_FILES.delete(name)
20
+ end
21
+ end
22
+
23
+ module MyHelpers
24
+
25
+ def make_request(job)
26
+ request = Rack::MockRequest.new($app)
27
+ @response = request.get(job.url)
28
+ end
29
+
30
+ end
31
+
32
+ World(MyHelpers)
@@ -0,0 +1,5 @@
1
+ class Album < ActiveRecord::Base
2
+ validates_property :format, :of => :cover_image, :in => [:jpg, :png, :gif]
3
+ validates_length_of :name, :in => 0..5
4
+ image_accessor :cover_image
5
+ end
@@ -0,0 +1,7 @@
1
+ <%= form_for @album, :html => {:multipart => true} do |f| %>
2
+ <%= @album.errors.full_messages %>
3
+ <%= f.text_field :name %>
4
+ <%= f.file_field :cover_image %>
5
+ <%= f.hidden_field :retained_cover_image %>
6
+ <%= f.submit :value => 'Create' %>
7
+ <% end %>
@@ -1,4 +1,6 @@
1
1
  <%= flash[:notice] %>
2
+ <%= @album.name %>
2
3
  Look at this cover image!
3
4
  <%= image_tag @album.cover_image.thumb('200x100!').url %>
5
+ <%= image_tag @album.cover_image.remote_url %>
4
6
  <%= link_to 'Index', albums_path %>
@@ -0,0 +1,38 @@
1
+ Feature: champion adds cover images to his albums
2
+ In order to be a champion
3
+ A user adds an image to his album
4
+
5
+ Scenario: Add and view image
6
+ When I go to the new album page
7
+ And I attach the file "../../../samples/beach.png" to "album[cover_image]"
8
+ And I press "Create"
9
+ Then I should see "successfully created"
10
+ And I should see "Look at this cover image!"
11
+ When I look at the generated image
12
+ Then I should see a PNG image of size 200x100
13
+
14
+ Scenario: validation fails
15
+ When I go to the new album page
16
+ And I attach the file "../../../samples/sample.docx" to "album[cover_image]"
17
+ And I press "Create"
18
+ Then I should see "Cover image format is incorrect. It needs to be one of 'jpg', 'png', 'gif', but was 'docx'"
19
+
20
+ Scenario: other validation fails
21
+ When I go to the new album page
22
+ And I fill in "album[name]" with "too long"
23
+ And I attach the file "../../../samples/beach.png" to "album[cover_image]"
24
+ And I press "Create"
25
+ Then I should see "Name is too long"
26
+ When I fill in "album[name]" with "short"
27
+ And I press "Create"
28
+ Then I should see "successfully created"
29
+ And I should see "short"
30
+ And I should see "Look at this cover image!"
31
+ When I look at the generated image
32
+ Then I should see a PNG image of size 200x100
33
+
34
+ Scenario: view remote image
35
+ Given an album "good" with attached file "beach.png"
36
+ When I go to the page for album "good"
37
+ And I look at the original image
38
+ Then I should see a PNG image of size 280x355
@@ -0,0 +1,7 @@
1
+ Then "show me the html" do
2
+ puts page.body
3
+ end
4
+
5
+ Then 'show "(.*)"' do |code|
6
+ eval "puts #{code}"
7
+ end
@@ -1,9 +1,19 @@
1
- When /^I look at the generated (.+) image$/ do |image_name|
1
+ Given /^an album "(.+)" with attached file "(.+)"$/ do |name, filename|
2
+ Album.create! :name => name, :cover_image => Rails.root.join('../../../samples', filename)
3
+ end
4
+
5
+ When /^I look at the generated image$/ do
2
6
  page.body =~ %r{src="(/media[^"]+?)"}
3
7
  url = $1
4
8
  visit(url)
5
9
  end
6
10
 
11
+ When /^I look at the original image$/ do
12
+ page.body =~ %r{src="(/system[^"]+?)"}
13
+ url = $1
14
+ visit(url)
15
+ end
16
+
7
17
  Then /^I should see a (.+) image of size (.+)$/ do |format, size|
8
18
  tempfile = Tempfile.new('wicked')
9
19
  page.body.force_encoding('UTF-8').encode! if page.body.respond_to?(:force_encoding) # For some reason need this on Ruby 1.9.2
@@ -3,6 +3,8 @@ module NavigationHelpers
3
3
  case page_name
4
4
  when "the new album page"
5
5
  '/albums/new'
6
+ when /^the page for album "(.+)"$/
7
+ album_path(Album.find_by_name($1))
6
8
  when /^the image for text "(.+)", size "(.+)"$/
7
9
  "/text/#{$1}/#{$2}"
8
10
  else
@@ -6,11 +6,11 @@ gem 'cucumber', '0.8.5'
6
6
 
7
7
  generate 'cucumber:install'
8
8
 
9
- generate 'scaffold albums cover_image_uid:string'
9
+ generate 'scaffold albums cover_image_uid:string name:string'
10
10
  rake 'db:migrate'
11
11
 
12
12
  # Copy over all files from the template dir
13
- files_dir = File.expand_path(File.dirname(__FILE__) + '/../files')
13
+ files_dir = File.expand_path(File.dirname(__FILE__) + '/files')
14
14
  run "cp -r #{files_dir}/** ."
15
15
 
16
16
  route <<ROUTES
data/irbrc.rb CHANGED
@@ -2,6 +2,7 @@ require "rubygems"
2
2
  require "bundler/setup"
3
3
  $:.unshift(File.expand_path('../lib', __FILE__))
4
4
  require 'dragonfly'
5
+ include Dragonfly::Serializer
5
6
  APP = Dragonfly[:images].configure_with(:imagemagick)
6
7
 
7
8
  # available_uids = `find #{APP.datastore.root_path} ! -type d`.split("\n").map do |file|
@@ -15,4 +16,4 @@ puts "\nAvailable sample images:\n"
15
16
  puts Dir['samples/*']
16
17
  puts "\nAvailable constants:\n"
17
18
  puts "APP"
18
- puts
19
+ puts
@@ -43,5 +43,12 @@ module Dragonfly
43
43
  App[*args]
44
44
  end
45
45
 
46
+ # Register saved configurations so we can do e.g.
47
+ # Dragonfly[:my_app].configure_with(:image_magick)
48
+ App.register_configuration(:imagemagick){ ImageMagick::Config }
49
+ App.register_configuration(:image_magick){ ImageMagick::Config }
50
+ App.register_configuration(:rails){ Config::Rails }
51
+ App.register_configuration(:heroku){ Config::Heroku }
52
+
46
53
  end
47
54
  end
@@ -1,29 +1,46 @@
1
1
  require 'forwardable'
2
+ require 'dragonfly/active_model_extensions/attachment_class_methods'
2
3
 
3
4
  module Dragonfly
4
5
  module ActiveModelExtensions
5
6
 
6
7
  class Attachment
7
8
 
9
+ # Exceptions
10
+ class BadAssignmentKey < RuntimeError; end
11
+
8
12
  extend Forwardable
9
13
  def_delegators :job,
10
14
  :data, :to_file, :file, :tempfile, :path,
11
15
  :process, :encode, :analyse,
12
16
  :meta, :meta=,
17
+ :name, :basename, :ext, :size,
13
18
  :url
14
19
 
15
- def initialize(app, parent_model, attribute_name)
16
- @app, @parent_model, @attribute_name = app, parent_model, attribute_name
17
- self.extend app.analyser.analysis_methods
18
- self.extend app.job_definitions
19
- self.uid = parent_uid
20
- self.job = app.fetch(uid) if uid
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
21
34
  end
22
35
 
23
36
  def assign(value)
37
+ self.changed = true
38
+ destroy_retained! if retained?
39
+ set_uid_and_model_uid(nil)
24
40
  if value.nil?
25
41
  self.job = nil
26
42
  reset_magic_attributes
43
+ self.class.run_callbacks(:after_unassign, model, self) if should_run_callbacks?
27
44
  else
28
45
  self.job = case value
29
46
  when Job then value.dup
@@ -31,44 +48,34 @@ module Dragonfly
31
48
  else app.new_job(value)
32
49
  end
33
50
  set_magic_attributes
51
+ update_meta
52
+ self.class.run_callbacks(:after_assign, model, self) if should_run_callbacks?
53
+ retain! if should_retain?
34
54
  end
35
- set_uid_and_parent_uid(nil)
36
55
  value
37
56
  end
38
57
 
58
+ def changed?
59
+ !!@changed
60
+ end
61
+
39
62
  def destroy!
40
63
  destroy_previous!
41
64
  destroy_content(uid) if uid
42
65
  end
43
66
 
44
67
  def save!
45
- sync_with_parent!
68
+ sync_with_model
69
+ store_job! if job && !uid
46
70
  destroy_previous!
47
- if job && !uid
48
- set_uid_and_parent_uid job.store(
49
- :meta => {
50
- :model_class => parent_model.class.name,
51
- :model_attachment => attribute_name
52
- }
53
- )
54
- self.job = job.to_fetched_job(uid)
55
- end
71
+ self.changed = false
72
+ self.retained = false
56
73
  end
57
74
 
58
75
  def to_value
59
76
  self if job
60
77
  end
61
78
 
62
- def analyse(meth, *args)
63
- has_magic_attribute_for?(meth) ? magic_attribute_for(meth) : job.send(meth)
64
- end
65
-
66
- [:size, :ext, :name].each do |meth|
67
- define_method meth do
68
- analyse(meth)
69
- end
70
- end
71
-
72
79
  def name=(name)
73
80
  job.name = name
74
81
  set_magic_attribute(:name, name) if has_magic_attribute_for?(:name)
@@ -84,6 +91,71 @@ module Dragonfly
84
91
  assign(encode(*args))
85
92
  self
86
93
  end
94
+
95
+ def remote_url(opts={})
96
+ app.remote_url_for(uid, opts) if uid
97
+ end
98
+
99
+ def apply
100
+ job.apply
101
+ self
102
+ end
103
+
104
+ attr_writer :should_run_callbacks
105
+
106
+ def should_run_callbacks?
107
+ !!@should_run_callbacks
108
+ end
109
+
110
+ # Retaining for avoiding uploading more than once
111
+
112
+ def retain!
113
+ if changed? && job
114
+ store_job!
115
+ self.retained = true
116
+ end
117
+ end
118
+
119
+ attr_writer :should_retain
120
+
121
+ def should_retain?
122
+ !!@should_retain
123
+ end
124
+
125
+ def retained?
126
+ !!@retained
127
+ end
128
+
129
+ def destroy_retained!
130
+ destroy_content(retained_attrs[:uid])
131
+ end
132
+
133
+ def retained_attrs
134
+ attribute_keys.inject({}) do |hash, key|
135
+ hash[key] = send(key)
136
+ hash
137
+ end if retained?
138
+ end
139
+
140
+ def retained_attrs=(attrs)
141
+ if changed? # if already set, ignore and destroy this retained content
142
+ destroy_content(attrs[:uid])
143
+ else
144
+ attrs.each do |key, value|
145
+ unless attribute_keys.include?(key)
146
+ raise BadAssignmentKey, "trying to call #{attribute}_#{key} = #{value.inspect} via retained_#{attribute} but this is not allowed!"
147
+ end
148
+ model.send("#{attribute}_#{key}=", value)
149
+ end
150
+ sync_with_model
151
+ update_from_uid
152
+ self.retained = true
153
+ end
154
+ end
155
+
156
+ def inspect
157
+ "<Dragonfly Attachment uid=#{uid.inspect}, app=#{app.inspect}>"
158
+ end
87
159
 
88
160
  protected
89
161
 
@@ -91,6 +163,18 @@ module Dragonfly
91
163
 
92
164
  private
93
165
 
166
+ attr_writer :changed, :retained
167
+
168
+ def attribute_keys
169
+ [:uid] + magic_attributes
170
+ end
171
+
172
+ def store_job!
173
+ opts = self.class.evaluate_storage_opts(model, self)
174
+ set_uid_and_model_uid job.store(opts)
175
+ self.job = job.to_fetched_job(uid)
176
+ end
177
+
94
178
  def destroy_content(uid)
95
179
  app.datastore.destroy(uid)
96
180
  rescue DataStorage::DataNotFound => e
@@ -104,48 +188,52 @@ module Dragonfly
104
188
  end
105
189
  end
106
190
 
107
- def sync_with_parent!
108
- # If the parent uid has been set manually
109
- if uid != parent_uid
110
- self.uid = parent_uid
191
+ def sync_with_model
192
+ # If the model uid has been set manually
193
+ if uid != model_uid
194
+ self.uid = model_uid
111
195
  end
112
196
  end
113
197
 
114
- def set_uid_and_parent_uid(uid)
198
+ def set_uid_and_model_uid(uid)
115
199
  self.uid = uid
116
- self.parent_uid = uid
200
+ self.model_uid = uid
117
201
  end
118
202
 
119
- def parent_uid=(uid)
120
- parent_model.send("#{attribute_name}_uid=", uid)
203
+ def model_uid=(uid)
204
+ model.send("#{attribute}_uid=", uid)
121
205
  end
122
206
 
123
- def parent_uid
124
- parent_model.send("#{attribute_name}_uid")
207
+ def model_uid
208
+ model.send("#{attribute}_uid")
125
209
  end
126
210
 
127
- attr_reader :app, :parent_model, :attribute_name
211
+ attr_reader :model, :uid
128
212
  attr_writer :job
129
213
  attr_accessor :previous_uid
130
- attr_reader :uid
131
214
 
132
215
  def uid=(uid)
133
216
  self.previous_uid = @uid if @uid
134
217
  @uid = uid
135
218
  end
136
219
 
137
- def allowed_magic_attributes
138
- app.analyser.analysis_method_names + [:size, :ext, :name]
220
+ def update_meta
221
+ magic_attributes.each{|property| meta[property] = model.send("#{attribute}_#{property}") }
222
+ meta[:model_class] = model.class.name
223
+ meta[:model_attachment] = attribute
224
+ end
225
+
226
+ def update_from_uid
227
+ self.job = app.fetch(uid)
228
+ update_meta
139
229
  end
140
230
 
141
231
  def magic_attributes
142
- @magic_attributes ||= parent_model.public_methods.select { |name|
143
- name.to_s =~ /^#{attribute_name}_(.+)$/ && allowed_magic_attributes.include?($1.to_sym)
144
- }.map{|name| name.to_s.sub("#{attribute_name}_", '').to_sym }
232
+ self.class.magic_attributes
145
233
  end
146
234
 
147
235
  def set_magic_attribute(property, value)
148
- parent_model.send("#{attribute_name}_#{property}=", value)
236
+ model.send("#{attribute}_#{property}=", value)
149
237
  end
150
238
 
151
239
  def set_magic_attributes
@@ -161,7 +249,7 @@ module Dragonfly
161
249
  end
162
250
 
163
251
  def magic_attribute_for(property)
164
- parent_model.send("#{attribute_name}_#{property}")
252
+ model.send("#{attribute}_#{property}")
165
253
  end
166
254
 
167
255
  end