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
@@ -5,7 +5,7 @@ module Dragonfly
5
5
 
6
6
  def self.apply_configuration(app, bucket_name)
7
7
  app.configure do |c|
8
- c.datastore = Dragonfly::DataStorage::S3DataStore.new
8
+ c.datastore = DataStorage::S3DataStore.new
9
9
  c.datastore.configure do |d|
10
10
  d.bucket_name = bucket_name
11
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")
@@ -1,41 +1,6 @@
1
1
  module Dragonfly
2
2
  module Config
3
-
4
- # ImageMagick is a saved configuration for Dragonfly apps, which does the following:
5
- # - registers an imagemagick analyser
6
- # - registers an imagemagick processor
7
- # - registers an imagemagick 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 ImageMagick
11
-
12
- def self.apply_configuration(app, opts={})
13
- app.configure do |c|
14
- c.analyser.register(Analysis::ImageMagickAnalyser)
15
- c.processor.register(Processing::ImageMagickProcessor)
16
- c.encoder.register(Encoding::ImageMagickEncoder)
17
- c.generator.register(Generation::ImageMagickGenerator)
18
-
19
- c.job :thumb do |geometry, format|
20
- process :thumb, geometry
21
- encode format if format
22
- end
23
- c.job :gif do
24
- encode :gif
25
- end
26
- c.job :jpg do
27
- encode :jpg
28
- end
29
- c.job :png do
30
- encode :png
31
- end
32
- c.job :convert do |args, format|
33
- process :convert, args, format
34
- end
35
- end
36
-
37
- end
38
-
39
- end
3
+ puts "WARNING: Dragonfly::Config::ImageMagick is DEPRECATED and will soon be removed. Please use Dragonfly::ImageMagick::Config instead."
4
+ ImageMagick = Dragonfly::ImageMagick::Config
40
5
  end
41
6
  end
@@ -6,8 +6,11 @@ module Dragonfly
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}" if c.datastore.is_a?(DataStorage::FileDataStore)
10
- c.url_path_prefix = '/media'
9
+ if c.datastore.is_a?(DataStorage::FileDataStore)
10
+ c.datastore.root_path = ::Rails.root.join('public/system/dragonfly', ::Rails.env).to_s
11
+ c.datastore.server_root = ::Rails.root.join('public').to_s
12
+ end
13
+ c.url_format = '/media/:job/:basename.:format'
11
14
  c.analyser.register(Analysis::FileCommandAnalyser)
12
15
  end
13
16
  end
@@ -2,26 +2,20 @@ module Dragonfly
2
2
  module Configurable
3
3
 
4
4
  # Exceptions
5
- class BadConfigAttribute < StandardError; end
5
+ class NotConfigured < RuntimeError; end
6
+ class BadConfigAttribute < RuntimeError; end
6
7
 
7
8
  def self.included(klass)
8
9
  klass.class_eval do
9
10
  include Configurable::InstanceMethods
10
11
  extend Configurable::ClassMethods
11
12
 
12
- # These aren't included in InstanceMethods because we need access to 'klass'
13
- # We can't just put them into InstanceMethods and use 'self.class' because
14
- # this won't always point to the class in which we've included Configurable,
15
- # e.g. if we've included it in an eigenclasse
16
- define_method :configuration_hash do
17
- @configuration_hash ||= klass.default_configuration.dup
13
+ # We should use configured_class rather than self.class
14
+ # because sometimes this will be the eigenclass of an object
15
+ # e.g. if we configure a module, etc.
16
+ define_method :configured_class do
17
+ klass
18
18
  end
19
- private :configuration_hash
20
-
21
- define_method :configuration_methods do
22
- klass.configuration_methods
23
- end
24
-
25
19
  end
26
20
  end
27
21
 
@@ -42,19 +36,86 @@ module Dragonfly
42
36
  self
43
37
  end
44
38
 
45
- def configure_with(configurer, *args, &block)
46
- configurer = configurer_for(configurer) if configurer.is_a?(Symbol)
47
- configurer.apply_configuration(self, *args)
39
+ def configure_with(config, *args, &block)
40
+ config = saved_config_for(config) if config.is_a?(Symbol)
41
+ config.apply_configuration(self, *args)
48
42
  configure(&block) if block
49
43
  self
50
44
  end
51
45
 
46
+ def has_config_method?(method_name)
47
+ config_methods.include?(method_name.to_sym)
48
+ end
49
+
50
+ def config_methods
51
+ @config_methods ||= configured_class.config_methods.dup
52
+ end
53
+
52
54
  def configuration
53
- configuration_hash.dup
55
+ @configuration ||= {}
56
+ end
57
+
58
+ def default_configuration
59
+ # Merge the default configuration of all ancestor classes/modules which are configurable
60
+ @default_configuration ||= [self.class, configured_class, *configured_class.ancestors].reverse.inject({}) do |default_config, klass|
61
+ default_config.merge!(klass.default_configuration) if klass.respond_to? :default_configuration
62
+ default_config
63
+ end
64
+ end
65
+
66
+ def set_config_value(key, value)
67
+ configuration[key] = value
68
+ child_configurables.each{|c| c.set_if_unset(key, value) }
69
+ value
70
+ end
71
+
72
+ def use_as_fallback_config(other_configurable)
73
+ other_configurable.add_child_configurable(self)
74
+ self.fallback_configurable = other_configurable
75
+ end
76
+
77
+ protected
78
+
79
+ def add_child_configurable(obj)
80
+ child_configurables << obj
81
+ config_methods.push(*obj.config_methods)
82
+ fallback_configurable.config_methods.push(*obj.config_methods) if fallback_configurable
83
+ end
84
+
85
+ def set_if_unset(key, value)
86
+ set_config_value(key, value) unless set_locally?(key)
87
+ end
88
+
89
+ private
90
+
91
+ attr_accessor :fallback_configurable
92
+
93
+ def child_configurables
94
+ @child_configurables ||= []
95
+ end
96
+
97
+ def set_locally?(key)
98
+ instance_variable_defined?("@#{key}")
99
+ end
100
+
101
+ def default_value(key)
102
+ if default_configuration[key].is_a?(DeferredBlock)
103
+ default_configuration[key] = default_configuration[key].call
104
+ end
105
+ default_configuration[key]
54
106
  end
55
107
 
56
- def has_configuration_method?(method_name)
57
- configuration_methods.include?(method_name.to_sym)
108
+ def saved_configs
109
+ configured_class.saved_configs
110
+ end
111
+
112
+ def saved_config_for(symbol)
113
+ config = saved_configs[symbol]
114
+ if config.nil?
115
+ raise ArgumentError, "#{symbol.inspect} is not a known configuration - try one of #{saved_configs.keys.join(', ')}"
116
+ end
117
+ config = config.call if config.respond_to?(:call)
118
+ config
58
119
  end
59
120
 
60
121
  end
@@ -65,8 +126,20 @@ module Dragonfly
65
126
  @default_configuration ||= {}
66
127
  end
67
128
 
68
- def configuration_methods
69
- @configuration_methods ||= []
129
+ def config_methods
130
+ @config_methods ||= []
131
+ end
132
+
133
+ def nested_configurables
134
+ @nested_configurables ||= []
135
+ end
136
+
137
+ def register_configuration(name, config=nil, &config_in_block)
138
+ saved_configs[name] = config_in_block || config
139
+ end
140
+
141
+ def saved_configs
142
+ @saved_configs ||= {}
70
143
  end
71
144
 
72
145
  private
@@ -76,15 +149,13 @@ module Dragonfly
76
149
 
77
150
  # Define the reader
78
151
  define_method(attribute) do
79
- if configuration_hash[attribute].is_a?(DeferredBlock)
80
- configuration_hash[attribute] = configuration_hash[attribute].call
81
- end
82
- configuration_hash[attribute]
152
+ configuration.has_key?(attribute) ? configuration[attribute] : default_value(attribute)
83
153
  end
84
154
 
85
155
  # Define the writer
86
156
  define_method("#{attribute}=") do |value|
87
- configuration_hash[attribute] = value
157
+ instance_variable_set("@#{attribute}", value)
158
+ set_config_value(attribute, value)
88
159
  end
89
160
 
90
161
  configuration_method attribute
@@ -92,7 +163,11 @@ module Dragonfly
92
163
  end
93
164
 
94
165
  def configuration_method(*method_names)
95
- configuration_methods.push(*method_names.map{|n| n.to_sym })
166
+ config_methods.push(*method_names.map{|n| n.to_sym }).uniq!
167
+ end
168
+
169
+ def nested_configurable(*method_names)
170
+ nested_configurables.push(*method_names)
96
171
  end
97
172
 
98
173
  end
@@ -104,12 +179,17 @@ module Dragonfly
104
179
  end
105
180
 
106
181
  def method_missing(method_name, *args, &block)
107
- if owner.has_configuration_method?(method_name)
108
- owner.send(method_name, *args, &block)
109
- elsif nested_configurable?(method_name, *args)
110
- owner.send(method_name, *args)
182
+ if owner.has_config_method?(method_name)
183
+ attribute = method_name.to_s.tr('=','').to_sym
184
+ if method_name.to_s =~ /=$/ && owner.has_config_method?(attribute) # a bit hacky - if it has both getter and setter, assume it's a configurable_attr
185
+ owner.set_config_value(attribute, args.first)
186
+ else
187
+ owner.send(method_name, *args, &block)
188
+ end
189
+ elsif nested_configurable?(method_name)
190
+ owner.send(method_name)
111
191
  else
112
- 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(', ')}"
192
+ raise BadConfigAttribute, "You tried to configure using '#{method_name.inspect}', but the valid config attributes are #{owner.config_methods.map{|a| %('#{a.inspect}') }.sort.join(', ')}"
113
193
  end
114
194
  end
115
195
 
@@ -117,11 +197,11 @@ module Dragonfly
117
197
 
118
198
  attr_reader :owner
119
199
 
120
- def nested_configurable?(method, *args)
121
- owner.respond_to?(method) && owner.send(method, *args).is_a?(Configurable)
200
+ def nested_configurable?(method)
201
+ owner.configured_class.nested_configurables.include?(method.to_sym)
122
202
  end
123
203
 
124
204
  end
125
205
 
126
206
  end
127
- end
207
+ end
@@ -1,6 +1,6 @@
1
1
  class Object
2
2
 
3
- # Will eventually get this by cherry-picking from activesupport
3
+ # Don't want to depend on activesupport for this
4
4
  def blank?
5
5
  respond_to?(:empty?) ? empty? : !self
6
6
  end
@@ -5,5 +5,5 @@ class String
5
5
  def to_method_name
6
6
  RUBY_VERSION =~ /^1.8/ ? self : to_sym
7
7
  end
8
-
8
+
9
9
  end
@@ -0,0 +1,84 @@
1
+ require 'couchrest'
2
+
3
+ module Dragonfly
4
+ module DataStorage
5
+ class CouchDataStore
6
+
7
+ include Configurable
8
+ include Serializer
9
+
10
+ configurable_attr :host, 'localhost'
11
+ configurable_attr :port, '5984'
12
+ configurable_attr :database, 'dragonfly'
13
+ configurable_attr :username
14
+ configurable_attr :password
15
+
16
+ def initialize(opts={})
17
+ self.host = opts[:host] if opts[:host]
18
+ self.port = opts[:port] if opts[:port]
19
+ self.database = opts[:database] if opts[:database]
20
+ self.username = opts[:username]
21
+ self.password = opts[:password]
22
+ end
23
+
24
+ def store(temp_object, opts={})
25
+ meta = opts[:meta] || {}
26
+ name = meta[:name] || temp_object.original_filename || 'file'
27
+ content_type = opts[:content_type] || opts[:mime_type] || 'application/octet-stream'
28
+
29
+ temp_object.file do |f|
30
+ doc = CouchRest::Document.new(:meta => marshal_encode(meta))
31
+ response = db.save_doc(doc)
32
+ doc.put_attachment(name, f, {:content_type => content_type})
33
+ form_uid(response['id'], name)
34
+ end
35
+ rescue RuntimeError => e
36
+ raise UnableToStore, "#{e} - #{temp_object.inspect}"
37
+ end
38
+
39
+ def retrieve(uid)
40
+ doc_id, attachment = parse_uid(uid)
41
+ doc = db.get(doc_id)
42
+ [doc.fetch_attachment(attachment), marshal_decode(doc['meta'])]
43
+ rescue RestClient::ResourceNotFound => e
44
+ raise DataNotFound, "#{e} - #{uid}"
45
+ end
46
+
47
+ def destroy(uid)
48
+ doc_id, attachment = parse_uid(uid)
49
+ doc = db.get(doc_id)
50
+ db.delete_doc(doc)
51
+ rescue RestClient::ResourceNotFound => e
52
+ raise DataNotFound, "#{e} - #{uid}"
53
+ end
54
+
55
+ def db
56
+ @db ||= begin
57
+ url = "http://#{auth}#{host}:#{port}"
58
+ CouchRest.new(url).database!(database)
59
+ end
60
+ end
61
+
62
+ def url_for(uid, opts={})
63
+ doc_id, attachment = parse_uid(uid)
64
+ "http://#{host}:#{port}/#{database}/#{doc_id}/#{attachment}"
65
+ end
66
+
67
+ private
68
+
69
+ def auth
70
+ username.blank? ? nil : "#{username}:#{password}@"
71
+ end
72
+
73
+ def form_uid(doc_id, attachment)
74
+ "#{doc_id}/#{attachment}"
75
+ end
76
+
77
+ def parse_uid(uid)
78
+ doc_id, attachment = uid.split('/')
79
+ [doc_id, (attachment || 'file')]
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -7,16 +7,20 @@ module Dragonfly
7
7
 
8
8
  # Exceptions
9
9
  class BadUID < RuntimeError; end
10
+ class UnableToFormUrl < RuntimeError; end
10
11
 
11
12
  include Configurable
12
13
 
13
14
  configurable_attr :root_path, '/var/tmp/dragonfly'
15
+ configurable_attr :server_root
16
+ configurable_attr :store_meta, true
14
17
 
15
18
  def store(temp_object, opts={})
19
+ meta = opts[:meta] || {}
16
20
  relative_path = if opts[:path]
17
21
  opts[:path]
18
22
  else
19
- filename = temp_object.name || 'file'
23
+ filename = meta[:name] || temp_object.original_filename || 'file'
20
24
  relative_path = relative_path_for(filename)
21
25
  end
22
26
 
@@ -27,7 +31,7 @@ module Dragonfly
27
31
  end
28
32
  prepare_path(path)
29
33
  temp_object.to_file(path).close
30
- store_extra_data(path, temp_object)
34
+ store_meta_data(path, meta) if store_meta
31
35
  rescue Errno::EACCES => e
32
36
  raise UnableToStore, e.message
33
37
  end
@@ -38,26 +42,38 @@ module Dragonfly
38
42
  def retrieve(relative_path)
39
43
  validate_uid!(relative_path)
40
44
  path = absolute(relative_path)
41
- file = File.new(path)
42
- file.close
45
+ pathname = Pathname.new(path)
46
+ raise DataNotFound, "couldn't find file #{path}" unless pathname.exist?
43
47
  [
44
- file,
45
- retrieve_extra_data(path)
48
+ pathname,
49
+ (store_meta ? retrieve_meta_data(path) : {})
46
50
  ]
47
- rescue Errno::ENOENT => e
48
- raise DataNotFound, e.message
49
51
  end
50
52
 
51
53
  def destroy(relative_path)
52
54
  validate_uid!(relative_path)
53
55
  path = absolute(relative_path)
54
56
  FileUtils.rm path
55
- FileUtils.rm extra_data_path(path)
57
+ FileUtils.rm_f meta_data_path(path)
58
+ FileUtils.rm_f deprecated_meta_data_path(path)
56
59
  purge_empty_directories(relative_path)
57
60
  rescue Errno::ENOENT => e
58
61
  raise DataNotFound, e.message
59
62
  end
60
63
 
64
+ def url_for(relative_path, opts={})
65
+ if server_root.nil?
66
+ raise NotConfigured, "you need to configure server_root for #{self.class.name} in order to form urls"
67
+ else
68
+ _, __, path = absolute(relative_path).partition(server_root)
69
+ if path.empty?
70
+ raise UnableToFormUrl, "couldn't form url for uid #{relative_path.inspect} with root_path #{root_path.inspect} and server_root #{server_root.inspect}"
71
+ else
72
+ path
73
+ end
74
+ end
75
+ end
76
+
61
77
  def disambiguate(path)
62
78
  dirname = File.dirname(path)
63
79
  basename = File.basename(path, '.*')
@@ -72,14 +88,18 @@ module Dragonfly
72
88
  end
73
89
 
74
90
  def relative(absolute_path)
75
- absolute_path[/^#{root_path}\/?(.*)$/, 1]
91
+ absolute_path[/^#{Regexp.escape root_path}\/?(.*)$/, 1]
76
92
  end
77
93
 
78
94
  def directory_empty?(path)
79
95
  Dir.entries(path) == ['.','..']
80
96
  end
81
97
 
82
- def extra_data_path(data_path)
98
+ def meta_data_path(data_path)
99
+ "#{data_path}.meta"
100
+ end
101
+
102
+ def deprecated_meta_data_path(data_path)
83
103
  "#{data_path}.extra"
84
104
  end
85
105
 
@@ -89,15 +109,20 @@ module Dragonfly
89
109
  "#{time.strftime '%Y/%m/%d/%H_%M_%S'}_#{msec}_#{filename.gsub(/[^\w.]+/,'_')}"
90
110
  end
91
111
 
92
- def store_extra_data(data_path, temp_object)
93
- File.open(extra_data_path(data_path), 'wb') do |f|
94
- f.write Marshal.dump(temp_object.attributes)
112
+ def store_meta_data(data_path, meta)
113
+ File.open(meta_data_path(data_path), 'wb') do |f|
114
+ f.write Marshal.dump(meta)
95
115
  end
96
116
  end
97
117
 
98
- def retrieve_extra_data(data_path)
99
- path = extra_data_path(data_path)
100
- File.exist?(path) ? File.open(path,'rb'){|f| Marshal.load(f.read) } : {}
118
+ def retrieve_meta_data(data_path)
119
+ path = meta_data_path(data_path)
120
+ if File.exist?(path)
121
+ File.open(path,'rb'){|f| Marshal.load(f.read) }
122
+ else
123
+ deprecated_path = deprecated_meta_data_path(data_path)
124
+ File.exist?(deprecated_path) ? File.open(deprecated_path,'rb'){|f| Marshal.load(f.read) } : {}
125
+ end
101
126
  end
102
127
 
103
128
  def prepare_path(path)
@@ -114,7 +139,7 @@ module Dragonfly
114
139
  end
115
140
 
116
141
  def validate_uid!(uid)
117
- raise BadUID, "tried to fetch uid #{uid.inspect} - perhaps due to a malicious user" if uid['../']
142
+ raise BadUID, "tried to fetch uid #{uid.inspect} - perhaps due to a malicious user" if uid['..']
118
143
  end
119
144
 
120
145
  end