dragonfly 0.6.2 → 0.7.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 (157) hide show
  1. data/.gitignore +2 -0
  2. data/.specopts +2 -0
  3. data/.yardopts +11 -5
  4. data/Gemfile +22 -0
  5. data/Gemfile.rails.2.3.5 +13 -0
  6. data/History.md +49 -0
  7. data/README.md +18 -28
  8. data/Rakefile +24 -36
  9. data/VERSION +1 -1
  10. data/config.ru +4 -1
  11. data/dragonfly.gemspec +85 -99
  12. data/extra_docs/Analysers.md +66 -30
  13. data/extra_docs/Caching.md +22 -0
  14. data/extra_docs/Configuration.md +116 -0
  15. data/extra_docs/DataStorage.md +114 -14
  16. data/extra_docs/Encoding.md +62 -37
  17. data/extra_docs/GeneralUsage.md +118 -0
  18. data/extra_docs/Generators.md +92 -0
  19. data/extra_docs/Heroku.md +51 -0
  20. data/extra_docs/Index.md +8 -9
  21. data/extra_docs/MimeTypes.md +18 -17
  22. data/extra_docs/Models.md +251 -0
  23. data/extra_docs/Processing.md +94 -70
  24. data/extra_docs/Rack.md +53 -0
  25. data/extra_docs/Rails2.md +44 -0
  26. data/extra_docs/Rails3.md +51 -0
  27. data/extra_docs/Sinatra.md +21 -0
  28. data/extra_docs/URLs.md +114 -0
  29. data/features/images.feature +6 -7
  30. data/features/no_processing.feature +0 -6
  31. data/features/rails_2.3.5.feature +1 -1
  32. data/features/rails_3.0.0.rc.feature +8 -0
  33. data/features/steps/dragonfly_steps.rb +14 -12
  34. data/features/steps/rails_steps.rb +20 -9
  35. data/features/support/env.rb +10 -11
  36. data/fixtures/files/app/views/albums/new.html.erb +4 -4
  37. data/fixtures/files/app/views/albums/show.html.erb +1 -1
  38. data/fixtures/files/features/manage_album_images.feature +1 -1
  39. data/fixtures/files/features/step_definitions/{album_steps.rb → image_steps.rb} +4 -3
  40. data/fixtures/files/features/support/paths.rb +2 -0
  41. data/fixtures/files/features/text_images.feature +7 -0
  42. data/fixtures/rails_3.0.0.rc/template.rb +21 -0
  43. data/irbrc.rb +2 -1
  44. data/lib/dragonfly.rb +4 -16
  45. data/lib/dragonfly/{active_record_extensions.rb → active_model_extensions.rb} +1 -1
  46. data/lib/dragonfly/active_model_extensions/attachment.rb +146 -0
  47. data/lib/dragonfly/{active_record_extensions → active_model_extensions}/class_methods.rb +5 -6
  48. data/lib/dragonfly/{active_record_extensions → active_model_extensions}/instance_methods.rb +1 -1
  49. data/lib/dragonfly/{active_record_extensions → active_model_extensions}/validations.rb +5 -9
  50. data/lib/dragonfly/analyser.rb +59 -0
  51. data/lib/dragonfly/analysis/file_command_analyser.rb +1 -1
  52. data/lib/dragonfly/analysis/r_magick_analyser.rb +46 -31
  53. data/lib/dragonfly/app.rb +138 -173
  54. data/lib/dragonfly/config/heroku.rb +19 -0
  55. data/lib/dragonfly/config/r_magick.rb +37 -0
  56. data/lib/dragonfly/config/{rails_defaults.rb → rails.rb} +6 -7
  57. data/lib/dragonfly/configurable.rb +30 -27
  58. data/lib/dragonfly/core_ext/object.rb +1 -1
  59. data/lib/dragonfly/data_storage/file_data_store.rb +59 -26
  60. data/lib/dragonfly/data_storage/mongo_data_store.rb +65 -0
  61. data/lib/dragonfly/data_storage/s3data_store.rb +31 -12
  62. data/lib/dragonfly/encoder.rb +13 -0
  63. data/lib/dragonfly/encoding/r_magick_encoder.rb +10 -19
  64. data/lib/dragonfly/endpoint.rb +43 -0
  65. data/lib/dragonfly/function_manager.rb +65 -0
  66. data/lib/dragonfly/{processing/r_magick_text_processor.rb → generation/r_magick_generator.rb} +25 -11
  67. data/lib/dragonfly/generator.rb +9 -0
  68. data/lib/dragonfly/job.rb +290 -0
  69. data/lib/dragonfly/job_builder.rb +39 -0
  70. data/lib/dragonfly/job_definitions.rb +26 -0
  71. data/lib/dragonfly/job_endpoint.rb +17 -0
  72. data/lib/dragonfly/loggable.rb +28 -0
  73. data/lib/dragonfly/middleware.rb +21 -14
  74. data/lib/dragonfly/processing/r_magick_processor.rb +71 -48
  75. data/lib/dragonfly/processor.rb +9 -0
  76. data/lib/dragonfly/r_magick_utils.rb +24 -0
  77. data/lib/dragonfly/rails/images.rb +10 -7
  78. data/lib/dragonfly/routed_endpoint.rb +42 -0
  79. data/lib/dragonfly/serializer.rb +32 -0
  80. data/lib/dragonfly/simple_cache.rb +23 -0
  81. data/lib/dragonfly/simple_endpoint.rb +64 -0
  82. data/lib/dragonfly/temp_object.rb +77 -45
  83. data/spec/argument_matchers.rb +7 -17
  84. data/spec/dragonfly/active_model_extensions/active_model_setup.rb +97 -0
  85. data/spec/dragonfly/active_model_extensions/active_record_setup.rb +85 -0
  86. data/spec/dragonfly/{active_record_extensions → active_model_extensions}/model_spec.rb +282 -244
  87. data/spec/dragonfly/active_model_extensions/spec_helper.rb +11 -0
  88. data/spec/dragonfly/analyser_spec.rb +123 -0
  89. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +2 -2
  90. data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +10 -1
  91. data/spec/dragonfly/app_spec.rb +175 -69
  92. data/spec/dragonfly/configurable_spec.rb +14 -0
  93. data/spec/dragonfly/data_storage/data_store_spec.rb +36 -9
  94. data/spec/dragonfly/data_storage/file_data_store_spec.rb +61 -38
  95. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +18 -0
  96. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +34 -39
  97. data/spec/dragonfly/deprecation_spec.rb +20 -0
  98. data/spec/dragonfly/function_manager_spec.rb +154 -0
  99. data/spec/dragonfly/generation/r_magick_generator_spec.rb +119 -0
  100. data/spec/dragonfly/job_builder_spec.rb +37 -0
  101. data/spec/dragonfly/job_definitions_spec.rb +35 -0
  102. data/spec/dragonfly/job_endpoint_spec.rb +66 -0
  103. data/spec/dragonfly/job_spec.rb +605 -0
  104. data/spec/dragonfly/loggable_spec.rb +80 -0
  105. data/spec/dragonfly/middleware_spec.rb +37 -17
  106. data/spec/dragonfly/processing/r_magick_processor_spec.rb +182 -166
  107. data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
  108. data/spec/dragonfly/serializer_spec.rb +61 -0
  109. data/spec/dragonfly/simple_cache_spec.rb +27 -0
  110. data/spec/dragonfly/simple_endpoint_spec.rb +78 -0
  111. data/spec/dragonfly/temp_object_spec.rb +154 -119
  112. data/spec/simple_matchers.rb +22 -0
  113. data/spec/spec_helper.rb +28 -4
  114. data/yard/templates/default/layout/html/layout.erb +18 -11
  115. metadata +89 -190
  116. data/config.rb +0 -5
  117. data/extra_docs/ActiveRecord.md +0 -196
  118. data/extra_docs/ExampleUseCases.md +0 -189
  119. data/extra_docs/GettingStarted.md +0 -114
  120. data/extra_docs/Shortcuts.md +0 -118
  121. data/extra_docs/UsingWithRails.md +0 -81
  122. data/features/rails_3.0.0.beta3.feature +0 -7
  123. data/fixtures/rails_3.0.0.beta3/template.rb +0 -16
  124. data/lib/dragonfly/active_record_extensions/attachment.rb +0 -170
  125. data/lib/dragonfly/analyser_list.rb +0 -9
  126. data/lib/dragonfly/analysis/base.rb +0 -10
  127. data/lib/dragonfly/belongs_to_app.rb +0 -24
  128. data/lib/dragonfly/config/heroku_rails_images.rb +0 -23
  129. data/lib/dragonfly/config/r_magick_images.rb +0 -69
  130. data/lib/dragonfly/config/r_magick_text.rb +0 -25
  131. data/lib/dragonfly/config/rails_images.rb +0 -13
  132. data/lib/dragonfly/data_storage/base.rb +0 -21
  133. data/lib/dragonfly/data_storage/base64_data_store.rb +0 -23
  134. data/lib/dragonfly/data_storage/transparent_data_store.rb +0 -21
  135. data/lib/dragonfly/delegatable.rb +0 -14
  136. data/lib/dragonfly/delegator.rb +0 -62
  137. data/lib/dragonfly/encoder_list.rb +0 -9
  138. data/lib/dragonfly/encoding/base.rb +0 -14
  139. data/lib/dragonfly/encoding/transparent_encoder.rb +0 -14
  140. data/lib/dragonfly/extended_temp_object.rb +0 -120
  141. data/lib/dragonfly/parameters.rb +0 -163
  142. data/lib/dragonfly/processing/base.rb +0 -10
  143. data/lib/dragonfly/processor_list.rb +0 -9
  144. data/lib/dragonfly/url_handler.rb +0 -147
  145. data/spec/dragonfly/active_record_extensions/attachment_spec.rb +0 -8
  146. data/spec/dragonfly/active_record_extensions/migration.rb +0 -42
  147. data/spec/dragonfly/active_record_extensions/models.rb +0 -6
  148. data/spec/dragonfly/active_record_extensions/spec_helper.rb +0 -24
  149. data/spec/dragonfly/belongs_to_app_spec.rb +0 -55
  150. data/spec/dragonfly/delegatable_spec.rb +0 -32
  151. data/spec/dragonfly/delegator_spec.rb +0 -145
  152. data/spec/dragonfly/extended_temp_object_spec.rb +0 -71
  153. data/spec/dragonfly/parameters_spec.rb +0 -298
  154. data/spec/dragonfly/processing/r_magick_text_processor_spec.rb +0 -84
  155. data/spec/dragonfly/url_handler_spec.rb +0 -247
  156. data/spec/dragonfly_spec.rb +0 -16
  157. data/spec/ginger_scenarios.rb +0 -13
@@ -0,0 +1,19 @@
1
+ module Dragonfly
2
+ module Config
3
+
4
+ module Heroku
5
+
6
+ def self.apply_configuration(app, bucket_name)
7
+ app.configure do |c|
8
+ c.datastore = Dragonfly::DataStorage::S3DataStore.new
9
+ c.datastore.configure do |d|
10
+ d.bucket_name = bucket_name
11
+ d.access_key_id = ENV['S3_KEY'] || raise("ENV variable 'S3_KEY' needs to be set - use\n\theroku config:add S3_KEY=XXXXXXXXX")
12
+ d.secret_access_key = ENV['S3_SECRET'] || raise("ENV variable 'S3_SECRET' needs to be set - use\n\theroku config:add S3_SECRET=XXXXXXXXX")
13
+ end
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,37 @@
1
+ module Dragonfly
2
+ module Config
3
+
4
+ # RMagick is a saved configuration for Dragonfly apps, which does the following:
5
+ # - registers an rmagick analyser
6
+ # - registers an rmagick processor
7
+ # - registers an rmagick encoder
8
+ # - adds thumb shortcuts like '280x140!', etc.
9
+ # Look at the source code for apply_configuration to see exactly how it configures the app.
10
+ module RMagick
11
+
12
+ def self.apply_configuration(app)
13
+ app.configure do |c|
14
+ c.analyser.register(Analysis::RMagickAnalyser)
15
+ c.processor.register(Processing::RMagickProcessor)
16
+ c.encoder.register(Encoding::RMagickEncoder)
17
+ c.generator.register(Generation::RMagickGenerator)
18
+ c.job :thumb do |geometry, format|
19
+ process :thumb, geometry
20
+ encode format if format
21
+ end
22
+ c.job :gif do
23
+ encode :gif
24
+ end
25
+ c.job :jpg do
26
+ encode :jpg
27
+ end
28
+ c.job :png do
29
+ encode :png
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -1,15 +1,14 @@
1
1
  module Dragonfly
2
2
  module Config
3
-
4
- module RailsDefaults
5
-
3
+
4
+ module Rails
5
+
6
6
  def self.apply_configuration(app)
7
7
  app.configure do |c|
8
8
  c.log = ::Rails.logger
9
- c.datastore.root_path = "#{::Rails.root}/public/system/dragonfly/#{::Rails.env}"
10
- c.url_handler.configure do |u|
11
- u.path_prefix = '/media'
12
- end
9
+ c.datastore.root_path = "#{::Rails.root}/public/system/dragonfly/#{::Rails.env}" if c.datastore.is_a?(DataStorage::FileDataStore)
10
+ c.url_path_prefix = '/media'
11
+ c.analyser.register(Analysis::FileCommandAnalyser)
13
12
  end
14
13
  end
15
14
 
@@ -1,14 +1,14 @@
1
1
  module Dragonfly
2
2
  module Configurable
3
-
3
+
4
4
  # Exceptions
5
5
  class BadConfigAttribute < StandardError; end
6
-
6
+
7
7
  def self.included(klass)
8
8
  klass.class_eval do
9
9
  include Configurable::InstanceMethods
10
10
  extend Configurable::ClassMethods
11
-
11
+
12
12
  # These aren't included in InstanceMethods because we need access to 'klass'
13
13
  # We can't just put them into InstanceMethods and use 'self.class' because
14
14
  # this won't always point to the class in which we've included Configurable,
@@ -17,25 +17,28 @@ module Dragonfly
17
17
  @configuration_hash ||= klass.default_configuration.dup
18
18
  end
19
19
  private :configuration_hash
20
-
20
+
21
21
  define_method :configuration_methods do
22
22
  klass.configuration_methods
23
23
  end
24
-
24
+
25
25
  end
26
26
  end
27
-
27
+
28
28
  module InstanceMethods
29
-
29
+
30
30
  def configure(&block)
31
31
  yield ConfigurationProxy.new(self)
32
+ self
32
33
  end
33
-
34
+
34
35
  def configure_with(configurer, *args, &block)
36
+ configurer = configurer_for(configurer) if configurer.is_a?(Symbol)
35
37
  configurer.apply_configuration(self, *args)
36
38
  configure(&block) if block
39
+ self
37
40
  end
38
-
41
+
39
42
  def configuration
40
43
  configuration_hash.dup
41
44
  end
@@ -45,22 +48,22 @@ module Dragonfly
45
48
  end
46
49
 
47
50
  end
48
-
51
+
49
52
  module ClassMethods
50
-
53
+
51
54
  def default_configuration
52
55
  @default_configuration ||= {}
53
56
  end
54
-
57
+
55
58
  def configuration_methods
56
59
  @configuration_methods ||= []
57
60
  end
58
-
61
+
59
62
  private
60
-
63
+
61
64
  def configurable_attr attribute, default=nil, &blk
62
65
  default_configuration[attribute] = blk || default
63
-
66
+
64
67
  # Define the reader
65
68
  define_method(attribute) do
66
69
  if configuration_hash[attribute].respond_to? :call
@@ -68,28 +71,28 @@ module Dragonfly
68
71
  end
69
72
  configuration_hash[attribute]
70
73
  end
71
-
74
+
72
75
  # Define the writer
73
76
  define_method("#{attribute}=") do |value|
74
77
  configuration_hash[attribute] = value
75
78
  end
76
-
79
+
77
80
  configuration_method attribute
78
81
  configuration_method "#{attribute}="
79
82
  end
80
-
83
+
81
84
  def configuration_method(*method_names)
82
85
  configuration_methods.push(*method_names.map{|n| n.to_sym })
83
86
  end
84
-
87
+
85
88
  end
86
-
89
+
87
90
  class ConfigurationProxy
88
-
91
+
89
92
  def initialize(owner)
90
93
  @owner = owner
91
94
  end
92
-
95
+
93
96
  def method_missing(method_name, *args, &block)
94
97
  if owner.has_configuration_method?(method_name)
95
98
  owner.send(method_name, *args, &block)
@@ -99,16 +102,16 @@ module Dragonfly
99
102
  raise BadConfigAttribute, "You tried to configure using '#{method_name.inspect}', but the valid config attributes are #{owner.configuration_methods.map{|a| %('#{a.inspect}') }.sort.join(', ')}"
100
103
  end
101
104
  end
102
-
105
+
103
106
  private
104
-
107
+
105
108
  attr_reader :owner
106
-
109
+
107
110
  def nested_configurable?(method, *args)
108
111
  owner.respond_to?(method) && owner.send(method, *args).is_a?(Configurable)
109
112
  end
110
-
113
+
111
114
  end
112
-
115
+
113
116
  end
114
117
  end
@@ -5,4 +5,4 @@ class Object
5
5
  respond_to?(:empty?) ? empty? : !self
6
6
  end
7
7
 
8
- end
8
+ end
@@ -3,23 +3,24 @@ require 'pathname'
3
3
  module Dragonfly
4
4
  module DataStorage
5
5
 
6
- class FileDataStore < Base
6
+ class FileDataStore
7
7
 
8
8
  include Configurable
9
-
10
- configurable_attr :root_path, '/var/tmp/dragonfly'
11
9
 
12
- def store(temp_object)
10
+ configurable_attr :root_path, '/var/tmp/dragonfly'
13
11
 
14
- relative_path = relative_storage_path(temp_object.basename || 'file')
12
+ def store(temp_object, opts={})
13
+ filename = temp_object.name || 'file'
15
14
 
15
+ relative_path = relative_path_for(filename)
16
16
  begin
17
- while File.exist?(storage_path = absolute_storage_path(relative_path))
18
- relative_path = increment_path(relative_path)
17
+ while File.exist?(path = absolute_path(relative_path))
18
+ filename = disambiguate(filename)
19
+ relative_path = relative_path_for(filename)
19
20
  end
20
- storage_dir = File.dirname(storage_path)
21
- FileUtils.mkdir_p(storage_dir) unless File.exist?(storage_dir)
22
- FileUtils.cp temp_object.path, storage_path
21
+ prepare_path(path)
22
+ temp_object.to_file(path).close
23
+ store_extra_data(path, temp_object)
23
24
  rescue Errno::EACCES => e
24
25
  raise UnableToStore, e.message
25
26
  end
@@ -28,33 +29,33 @@ module Dragonfly
28
29
  end
29
30
 
30
31
  def retrieve(relative_path)
31
- File.new(absolute_storage_path(relative_path))
32
+ path = absolute_path(relative_path)
33
+ [
34
+ File.new(path),
35
+ retrieve_extra_data(path)
36
+ ]
32
37
  rescue Errno::ENOENT => e
33
38
  raise DataNotFound, e.message
34
39
  end
35
40
 
36
41
  def destroy(relative_path)
37
- FileUtils.rm absolute_storage_path(relative_path)
38
- containing_directory = Pathname.new(relative_path).dirname
39
- containing_directory.ascend do |relative_dir|
40
- dir = absolute_storage_path(relative_dir)
41
- FileUtils.rmdir dir if directory_empty?(dir)
42
- end
42
+ path = absolute_path(relative_path)
43
+ FileUtils.rm path
44
+ FileUtils.rm extra_data_path(path)
45
+ purge_empty_directories(relative_path)
43
46
  rescue Errno::ENOENT => e
44
47
  raise DataNotFound, e.message
45
48
  end
46
49
 
47
- private
48
-
49
- def increment_path(path)
50
- path.sub(/(_(\d+))?$/){ $1 ? "_#{$2.to_i+1}" : '_2' }
50
+ def disambiguate(filename)
51
+ basename = File.basename(filename, '.*')
52
+ extname = File.extname(filename)
53
+ "#{basename}_#{Time.now.usec.to_s(32)}#{extname}"
51
54
  end
52
55
 
53
- def relative_storage_path(suffix)
54
- "#{Time.now.strftime '%Y/%m/%d/%H%M%S'}_#{suffix}"
55
- end
56
-
57
- def absolute_storage_path(relative_path)
56
+ private
57
+
58
+ def absolute_path(relative_path)
58
59
  File.join(root_path, relative_path)
59
60
  end
60
61
 
@@ -62,6 +63,38 @@ module Dragonfly
62
63
  Dir.entries(path) == ['.','..']
63
64
  end
64
65
 
66
+ def extra_data_path(data_path)
67
+ "#{data_path}.extra"
68
+ end
69
+
70
+ def relative_path_for(filename)
71
+ "#{Time.now.strftime '%Y/%m/%d'}/#{filename.gsub(/[^\w.]+/,'_')}"
72
+ end
73
+
74
+ def store_extra_data(data_path, temp_object)
75
+ File.open(extra_data_path(data_path), 'w') do |f|
76
+ f.write Marshal.dump(temp_object.attributes)
77
+ end
78
+ end
79
+
80
+ def retrieve_extra_data(data_path)
81
+ path = extra_data_path(data_path)
82
+ File.exist?(path) ? Marshal.load(File.read(path)) : {}
83
+ end
84
+
85
+ def prepare_path(path)
86
+ dir = File.dirname(path)
87
+ FileUtils.mkdir_p(dir) unless File.exist?(dir)
88
+ end
89
+
90
+ def purge_empty_directories(path)
91
+ containing_directory = Pathname.new(path).dirname
92
+ containing_directory.ascend do |relative_dir|
93
+ dir = absolute_path(relative_dir)
94
+ FileUtils.rmdir dir if directory_empty?(dir)
95
+ end
96
+ end
97
+
65
98
  end
66
99
 
67
100
  end
@@ -0,0 +1,65 @@
1
+ require 'mongo'
2
+
3
+ module Dragonfly
4
+ module DataStorage
5
+ class MongoDataStore
6
+
7
+ include Configurable
8
+ include Serializer
9
+
10
+ configurable_attr :host
11
+ configurable_attr :port
12
+ configurable_attr :database, 'dragonfly'
13
+
14
+ def initialize(opts={})
15
+ self.host = opts[:host]
16
+ self.port = opts[:port]
17
+ self.database = opts[:database] if opts[:database]
18
+ end
19
+
20
+ def store(temp_object, opts={})
21
+ temp_object.file do |f|
22
+ mongo_id = grid.put(f, :metadata => marshal_encode(temp_object.attributes))
23
+ mongo_id.to_s
24
+ end
25
+ end
26
+
27
+ def retrieve(uid)
28
+ grid_io = grid.get(bson_id(uid))
29
+ extra = marshal_decode(grid_io.metadata)
30
+ extra[:meta].merge!(:stored_at => grid_io.upload_date)
31
+ [
32
+ grid_io.read,
33
+ extra
34
+ ]
35
+ rescue Mongo::GridFileNotFound, BSON::InvalidObjectID => e
36
+ raise DataNotFound, "#{e} - #{uid}"
37
+ end
38
+
39
+ def destroy(uid)
40
+ grid.delete(bson_id(uid))
41
+ rescue Mongo::GridFileNotFound, BSON::InvalidObjectID => e
42
+ raise DataNotFound, "#{e} - #{uid}"
43
+ end
44
+
45
+ private
46
+
47
+ def connection
48
+ @connection ||= Mongo::Connection.new(host, port)
49
+ end
50
+
51
+ def db
52
+ @db ||= connection.db(database)
53
+ end
54
+
55
+ def grid
56
+ @grid ||= Mongo::Grid.new(db)
57
+ end
58
+
59
+ def bson_id(uid)
60
+ BSON::ObjectID.from_string(uid)
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -3,16 +3,23 @@ require 'aws/s3'
3
3
  module Dragonfly
4
4
  module DataStorage
5
5
 
6
- class S3DataStore < Base
7
-
6
+ class S3DataStore
7
+
8
8
  include Configurable
9
9
  include AWS::S3
10
-
10
+ include Serializer
11
+
11
12
  configurable_attr :bucket_name
12
13
  configurable_attr :access_key_id
13
14
  configurable_attr :secret_access_key
14
15
  configurable_attr :use_filesystem, true
15
16
 
17
+ def initialize(opts={})
18
+ self.bucket_name = opts[:bucket_name]
19
+ self.access_key_id = opts[:access_key_id]
20
+ self.secret_access_key = opts[:secret_access_key]
21
+ end
22
+
16
23
  def connect!
17
24
  AWS::S3::Base.establish_connection!(
18
25
  :access_key_id => access_key_id,
@@ -24,22 +31,27 @@ module Dragonfly
24
31
  Bucket.create(bucket_name) unless bucket_names.include?(bucket_name)
25
32
  end
26
33
 
27
- def store(temp_object)
28
- uid = generate_uid(temp_object.basename || 'file')
34
+ def store(temp_object, opts={})
35
+ uid = generate_uid(temp_object.name || 'file')
29
36
  ensure_initialized
30
37
  object = use_filesystem ? temp_object.file : temp_object.data
31
- S3Object.store(uid, object, bucket_name)
38
+ extra_data = temp_object.attributes
39
+ S3Object.store(uid, object, bucket_name, s3_metadata_for(extra_data))
32
40
  object.close if use_filesystem
33
41
  uid
34
42
  end
35
43
 
36
44
  def retrieve(uid)
37
45
  ensure_initialized
38
- S3Object.value(uid, bucket_name)
46
+ s3_object = S3Object.find(uid, bucket_name)
47
+ [
48
+ s3_object.value,
49
+ parse_s3_metadata(s3_object.metadata)
50
+ ]
39
51
  rescue AWS::S3::NoSuchKey => e
40
52
  raise DataNotFound, "#{e} - #{uid}"
41
53
  end
42
-
54
+
43
55
  def destroy(uid)
44
56
  ensure_initialized
45
57
  S3Object.delete(uid, bucket_name)
@@ -61,12 +73,19 @@ module Dragonfly
61
73
  end
62
74
  end
63
75
 
64
- def generate_uid(suffix)
65
- time = Time.now
66
- "#{time.strftime '%Y/%m/%d/%H/%M/%S'}/#{rand(1000)}/#{suffix}"
76
+ def generate_uid(name)
77
+ "#{Time.now.strftime '%Y/%m/%d/%H/%M/%S'}/#{rand(1000)}/#{name.gsub(/[^\w.]+/, '_')}"
78
+ end
79
+
80
+ def s3_metadata_for(extra_data)
81
+ {'x-amz-meta-extra' => marshal_encode(extra_data)}
82
+ end
83
+
84
+ def parse_s3_metadata(metadata)
85
+ marshal_decode(metadata['x-amz-meta-extra'])
67
86
  end
68
87
 
69
88
  end
70
-
89
+
71
90
  end
72
91
  end