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
@@ -0,0 +1,8 @@
1
+ Then "debug" do
2
+ debugger
3
+ true
4
+ end
5
+
6
+ Then 'show "(.*)"' do |code|
7
+ eval "puts #{code}"
8
+ end
@@ -0,0 +1,66 @@
1
+ require 'tempfile'
2
+
3
+ Given /^we are using the app for (\w+)$/ do |app_name|
4
+ $app = Dragonfly[app_name.to_sym]
5
+ end
6
+
7
+ Given /^a stored file "(.+?)"$/ do |name|
8
+ file = File.new(File.dirname(__FILE__) + "/../../samples/#{name}")
9
+ uid = $app.store(file)
10
+ TEMP_FILES[name] = uid
11
+ end
12
+
13
+ Given /^a stored image "(.+?)" with dimensions (\d+)x(\d+)$/ do |name, width, height|
14
+ tempfile = Tempfile.new(name)
15
+ `convert -resize #{width}x#{height}! #{SAMPLE_IMAGE_PATH} #{tempfile.path}`
16
+ uid = $app.store(tempfile)
17
+ TEMP_FILES[name] = uid
18
+ end
19
+
20
+ When /^I go to the url for "(.+?)"$/ do |name|
21
+ uid = TEMP_FILES[name]
22
+ make_request $app.fetch(uid)
23
+ end
24
+
25
+ When /^I go to the url for "(.+?)", with format '([^']+?)'$/ do |name, format|
26
+ uid = TEMP_FILES[name]
27
+ make_request $app.fetch(uid).encode(format)
28
+ end
29
+
30
+ When /^I go to the url for "(.+?)", with format '(.+?)' and resize geometry '(.+?)'$/ do |name, format, geometry|
31
+ uid = TEMP_FILES[name]
32
+ make_request $app.fetch(uid).process(:resize, geometry).encode(format)
33
+ end
34
+
35
+ When /^I go to the url for "(.+?)", with shortcut '([^']+?)'$/ do |name, geometry|
36
+ uid = TEMP_FILES[name]
37
+ make_request $app.fetch(uid).thumb(geometry)
38
+ end
39
+
40
+ Then "the response should be OK" do
41
+ @response.status.should == 200
42
+ end
43
+
44
+ Then /the response should have mime-type '(.+?)'/ do |mime_type|
45
+ @response.headers['Content-Type'].should == mime_type
46
+ end
47
+
48
+ Then /^the image should have width '(.+?)'$/ do |width|
49
+ @response.body.should have_width(width.to_i)
50
+ end
51
+
52
+ Then /^the image should have height '(.+?)'$/ do |height|
53
+ @response.body.should have_height(height.to_i)
54
+ end
55
+
56
+ Then /^the image should have format '(.+?)'$/ do |format|
57
+ @response.body.should have_format(format)
58
+ end
59
+
60
+ Then /^the response should have the same content as the file "([^\"]*)"$/ do |name|
61
+ if RUBY_VERSION =~ /^1\.8/
62
+ @response.body.should == $app.fetch(TEMP_FILES[name]).data
63
+ else
64
+ @response.body.force_encoding('BINARY').should == $app.fetch(TEMP_FILES[name]).data.force_encoding('BINARY')
65
+ end
66
+ end
@@ -0,0 +1,39 @@
1
+ RAILS_APP_NAME = 'tmp_app'
2
+ FIXTURES_PATH = ROOT_PATH + "/fixtures"
3
+ GEMFILES = {
4
+ '2.3.5' => ROOT_PATH + '/Gemfile.rails.2.3.5',
5
+ '3.0.3' => ROOT_PATH + '/Gemfile',
6
+ }
7
+
8
+ def fixture_path(version)
9
+ "#{FIXTURES_PATH}/rails_#{version}"
10
+ end
11
+
12
+ def app_path(version)
13
+ "#{fixture_path(version)}/#{RAILS_APP_NAME}"
14
+ end
15
+
16
+ ##############################################################################
17
+
18
+ {
19
+ '2.3.5' => "BUNDLE_GEMFILE=#{GEMFILES['2.3.5']} rails #{RAILS_APP_NAME} -m template.rb",
20
+ '3.0.3' => "BUNDLE_GEMFILE=#{GEMFILES['3.0.3']} bundle exec rails new #{RAILS_APP_NAME} -m template.rb"
21
+ }.each do |version, rails_command|
22
+
23
+ Given /^a Rails #{version} application set up for using dragonfly$/ do
24
+ raise "Problem setting up Rails app" unless `
25
+ cd #{fixture_path(version)} &&
26
+ rm -rf #{RAILS_APP_NAME} &&
27
+ #{rails_command}`
28
+ end
29
+
30
+ end
31
+
32
+ Then /^the (.+) cucumber features in my Rails (.+) app should pass$/ do |filename, version|
33
+ puts "\n*** RUNNING FEATURES IN THE RAILS APP... ***\n"
34
+ path = File.join(fixture_path(version), RAILS_APP_NAME)
35
+ `cd #{path} && BUNDLE_GEMFILE=#{GEMFILES[version]} RAILS_ENV=cucumber rake db:migrate`
36
+ features_passed = system "cd #{path} && BUNDLE_GEMFILE=#{GEMFILES[version]} cucumber features/#{filename}.feature"
37
+ puts "\n*** FINISHED RUNNING FEATURES IN THE RAILS APP ***\n"
38
+ raise "Features failed" unless features_passed
39
+ end
@@ -0,0 +1,40 @@
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)
15
+ end
16
+
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
39
+
40
+ World(MyHelpers)
@@ -0,0 +1,3 @@
1
+ class Album < ActiveRecord::Base
2
+ image_accessor :cover_image
3
+ end
@@ -0,0 +1,4 @@
1
+ <% form_for @album, :html => {:multipart => true} do |f| %>
2
+ <%= f.file_field :cover_image %>
3
+ <%= f.submit :value => 'Create' %>
4
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <%= flash[:notice] %>
2
+ Look at this cover image!
3
+ <%= image_tag @album.cover_image.thumb('200x100!').url %>
4
+ <%= link_to 'Index', albums_path %>
@@ -0,0 +1,4 @@
1
+ # This is a hack to get the generated rails apps to use the version of dragonfly being worked on.
2
+ $:.unshift(File.expand_path(File.dirname(__FILE__)+'/../../../../../lib'))
3
+ require 'dragonfly/rails/images'
4
+
@@ -0,0 +1,12 @@
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 beach image
12
+ And I should see a PNG image of size 200x100
@@ -0,0 +1,15 @@
1
+ When /^I look at the generated (.+) image$/ do |image_name|
2
+ page.body =~ %r{src="(/media[^"]+?)"}
3
+ url = $1
4
+ visit(url)
5
+ end
6
+
7
+ Then /^I should see a (.+) image of size (.+)$/ do |format, size|
8
+ tempfile = Tempfile.new('wicked')
9
+ page.body.force_encoding('UTF-8').encode! if page.body.respond_to?(:force_encoding) # For some reason need this on Ruby 1.9.2
10
+ tempfile.write page.body
11
+ tempfile.close
12
+ output = `identify #{tempfile.path}`.split(' ')
13
+ output[1].should == format
14
+ output[2].should == size
15
+ end
@@ -0,0 +1,15 @@
1
+ module NavigationHelpers
2
+ def path_to(page_name)
3
+ case page_name
4
+ when "the new album page"
5
+ '/albums/new'
6
+ when /^the image for text "(.+)", size "(.+)"$/
7
+ "/text/#{$1}/#{$2}"
8
+ else
9
+ raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
10
+ "Now, go and add a mapping in #{__FILE__}"
11
+ end
12
+ end
13
+ end
14
+
15
+ World(NavigationHelpers)
@@ -0,0 +1,7 @@
1
+ Feature: champion adds text images to his app
2
+ In order to be a champion
3
+ A user adds text images to his app
4
+
5
+ Scenario: View text image
6
+ When I go to the image for text "Hello", size "300x150!"
7
+ Then I should see a PNG image of size 300x150
@@ -0,0 +1,10 @@
1
+ gem 'rack-cache', :lib => 'rack/cache'
2
+ gem 'cucumber'
3
+ generate 'cucumber'
4
+
5
+ generate 'scaffold albums cover_image_uid:string'
6
+ rake 'db:migrate'
7
+
8
+ # Copy over all files from the template dir
9
+ files_dir = File.expand_path(File.dirname(__FILE__) + '/../../files')
10
+ run "cp -r #{files_dir}/** ."
@@ -0,0 +1,20 @@
1
+ gem 'rack-cache', :require => 'rack/cache'
2
+
3
+ gem 'capybara'
4
+ gem 'cucumber-rails'
5
+ gem 'cucumber', '0.8.5'
6
+
7
+ generate 'cucumber:install'
8
+
9
+ generate 'scaffold albums cover_image_uid:string'
10
+ rake 'db:migrate'
11
+
12
+ # Copy over all files from the template dir
13
+ files_dir = File.expand_path(File.dirname(__FILE__) + '/../files')
14
+ run "cp -r #{files_dir}/** ."
15
+
16
+ route <<ROUTES
17
+ match '/text/:text/:size' => Dragonfly[:images].endpoint{|params, app|
18
+ app.generate(:text, params[:text]).thumb(params[:size])
19
+ }
20
+ ROUTES
@@ -0,0 +1,17 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require File.dirname(__FILE__) + '/lib/dragonfly'
4
+ APP = Dragonfly[:images].configure_with(:imagemagick)
5
+
6
+ # available_uids = `find #{APP.datastore.root_path} ! -type d`.split("\n").map do |file|
7
+ # file.sub("#{APP.datastore.root_path}/", '')
8
+ # end
9
+
10
+ puts "Loaded stuff from dragonfly irbrc"
11
+ # puts "\nAvailable uids:\n"
12
+ # puts available_uids
13
+ puts "\nAvailable sample images:\n"
14
+ puts Dir['samples/*']
15
+ puts "\nAvailable constants:\n"
16
+ puts "APP"
17
+ puts
@@ -0,0 +1,45 @@
1
+ # AUTOLOAD EVERYTHING IN THE DRAGONFLY DIRECTORY TREE
2
+
3
+ # The convention is that dirs are modules
4
+ # so declare them here and autoload any modules/classes inside them
5
+ # All paths here are absolute
6
+ def camelize(path)
7
+ # e.g. 'test/this_one' => Test::ThisOne
8
+ "#{path}".
9
+ chomp('/').
10
+ gsub('/','::').
11
+ gsub(/([^a-z])(\w)/){ "#{$1}#{$2.upcase}" }.
12
+ gsub('_','').
13
+ sub(/^(\w)/){ $1.upcase }
14
+ end
15
+ def autoload_files_in_dir(path, namespace)
16
+ # Define the module
17
+ eval("module #{namespace}; end")
18
+ # Autoload modules/classes in that module
19
+ Dir.glob("#{path}/*.rb").each do |file|
20
+ file = File.expand_path(file)
21
+ sub_const_name = camelize( File.basename(file, '.rb') )
22
+ eval("#{namespace}.autoload('#{sub_const_name}', '#{file}')")
23
+ end
24
+ # Recurse on subdirectories
25
+ Dir.glob("#{path}/*/").each do |dir|
26
+ sub_namespace = camelize( File.basename(dir) )
27
+ autoload_files_in_dir(dir, "#{namespace}::#{sub_namespace}")
28
+ end
29
+ end
30
+
31
+ autoload_files_in_dir("#{File.dirname(__FILE__)}/dragonfly", 'Dragonfly')
32
+
33
+ require File.dirname(__FILE__) + '/dragonfly/core_ext/object'
34
+ require File.dirname(__FILE__) + '/dragonfly/core_ext/string'
35
+ require File.dirname(__FILE__) + '/dragonfly/core_ext/symbol'
36
+
37
+ module Dragonfly
38
+ class << self
39
+
40
+ def [](*args)
41
+ App[*args]
42
+ end
43
+
44
+ end
45
+ end
@@ -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,169 @@
1
+ require 'forwardable'
2
+
3
+ module Dragonfly
4
+ module ActiveModelExtensions
5
+
6
+ class Attachment
7
+
8
+ extend Forwardable
9
+ def_delegators :job,
10
+ :data, :to_file, :file, :tempfile, :path,
11
+ :process, :encode, :analyse,
12
+ :meta, :meta=,
13
+ :url
14
+
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
21
+ end
22
+
23
+ def assign(value)
24
+ if value.nil?
25
+ self.job = nil
26
+ reset_magic_attributes
27
+ else
28
+ self.job = case value
29
+ when Job then value.dup
30
+ when self.class then value.job.dup
31
+ else app.new_job(value)
32
+ end
33
+ set_magic_attributes
34
+ end
35
+ set_uid_and_parent_uid(nil)
36
+ value
37
+ end
38
+
39
+ def destroy!
40
+ destroy_previous!
41
+ destroy_content(uid) if uid
42
+ end
43
+
44
+ def save!
45
+ sync_with_parent!
46
+ 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
56
+ end
57
+
58
+ def to_value
59
+ self if job
60
+ end
61
+
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
+ def name=(name)
73
+ job.name = name
74
+ set_magic_attribute(:name, name) if has_magic_attribute_for?(:name)
75
+ name
76
+ end
77
+
78
+ def process!(*args)
79
+ assign(process(*args))
80
+ self
81
+ end
82
+
83
+ def encode!(*args)
84
+ assign(encode(*args))
85
+ self
86
+ end
87
+
88
+ protected
89
+
90
+ attr_reader :job
91
+
92
+ private
93
+
94
+ def destroy_content(uid)
95
+ app.datastore.destroy(uid)
96
+ rescue DataStorage::DataNotFound => e
97
+ app.log.warn("*** WARNING ***: tried to destroy data with uid #{uid}, but got error: #{e}")
98
+ end
99
+
100
+ def destroy_previous!
101
+ if previous_uid
102
+ destroy_content(previous_uid)
103
+ self.previous_uid = nil
104
+ end
105
+ end
106
+
107
+ def sync_with_parent!
108
+ # If the parent uid has been set manually
109
+ if uid != parent_uid
110
+ self.uid = parent_uid
111
+ end
112
+ end
113
+
114
+ def set_uid_and_parent_uid(uid)
115
+ self.uid = uid
116
+ self.parent_uid = uid
117
+ end
118
+
119
+ def parent_uid=(uid)
120
+ parent_model.send("#{attribute_name}_uid=", uid)
121
+ end
122
+
123
+ def parent_uid
124
+ parent_model.send("#{attribute_name}_uid")
125
+ end
126
+
127
+ attr_reader :app, :parent_model, :attribute_name
128
+ attr_writer :job
129
+ attr_accessor :previous_uid
130
+ attr_reader :uid
131
+
132
+ def uid=(uid)
133
+ self.previous_uid = @uid if @uid
134
+ @uid = uid
135
+ end
136
+
137
+ def allowed_magic_attributes
138
+ app.analyser.analysis_method_names + [:size, :ext, :name]
139
+ end
140
+
141
+ 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 }
145
+ end
146
+
147
+ def set_magic_attribute(property, value)
148
+ parent_model.send("#{attribute_name}_#{property}=", value)
149
+ end
150
+
151
+ def set_magic_attributes
152
+ magic_attributes.each{|property| set_magic_attribute(property, job.send(property)) }
153
+ end
154
+
155
+ def reset_magic_attributes
156
+ magic_attributes.each{|property| set_magic_attribute(property, nil) }
157
+ end
158
+
159
+ def has_magic_attribute_for?(property)
160
+ magic_attributes.include?(property.to_sym)
161
+ end
162
+
163
+ def magic_attribute_for(property)
164
+ parent_model.send("#{attribute_name}_#{property}")
165
+ end
166
+
167
+ end
168
+ end
169
+ end