oahu-dragonfly 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. data/.rspec +1 -0
  2. data/.yardopts +24 -0
  3. data/Gemfile +30 -0
  4. data/History.md +323 -0
  5. data/LICENSE +20 -0
  6. data/README.md +88 -0
  7. data/Rakefile +50 -0
  8. data/VERSION +1 -0
  9. data/config.ru +14 -0
  10. data/docs.watchr +1 -0
  11. data/dragonfly.gemspec +297 -0
  12. data/extra_docs/Analysers.md +66 -0
  13. data/extra_docs/Caching.md +23 -0
  14. data/extra_docs/Configuration.md +124 -0
  15. data/extra_docs/Couch.md +49 -0
  16. data/extra_docs/DataStorage.md +153 -0
  17. data/extra_docs/Encoding.md +67 -0
  18. data/extra_docs/GeneralUsage.md +121 -0
  19. data/extra_docs/Generators.md +60 -0
  20. data/extra_docs/Heroku.md +50 -0
  21. data/extra_docs/ImageMagick.md +125 -0
  22. data/extra_docs/Index.md +33 -0
  23. data/extra_docs/MimeTypes.md +40 -0
  24. data/extra_docs/Models.md +272 -0
  25. data/extra_docs/Mongo.md +45 -0
  26. data/extra_docs/Processing.md +77 -0
  27. data/extra_docs/Rack.md +52 -0
  28. data/extra_docs/Rails2.md +57 -0
  29. data/extra_docs/Rails3.md +62 -0
  30. data/extra_docs/Sinatra.md +25 -0
  31. data/extra_docs/URLs.md +169 -0
  32. data/features/images.feature +47 -0
  33. data/features/no_processing.feature +14 -0
  34. data/features/rails_3.0.5.feature +8 -0
  35. data/features/steps/common_steps.rb +8 -0
  36. data/features/steps/dragonfly_steps.rb +66 -0
  37. data/features/steps/rails_steps.rb +28 -0
  38. data/features/support/env.rb +13 -0
  39. data/features/support/setup.rb +32 -0
  40. data/fixtures/rails_3.0.5/files/app/models/album.rb +7 -0
  41. data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
  42. data/fixtures/rails_3.0.5/files/app/views/albums/show.html.erb +6 -0
  43. data/fixtures/rails_3.0.5/files/config/initializers/dragonfly.rb +4 -0
  44. data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
  45. data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
  46. data/fixtures/rails_3.0.5/files/features/step_definitions/image_steps.rb +25 -0
  47. data/fixtures/rails_3.0.5/files/features/support/paths.rb +17 -0
  48. data/fixtures/rails_3.0.5/files/features/text_images.feature +7 -0
  49. data/fixtures/rails_3.0.5/template.rb +20 -0
  50. data/irbrc.rb +18 -0
  51. data/lib/dragonfly.rb +55 -0
  52. data/lib/dragonfly/active_model_extensions.rb +13 -0
  53. data/lib/dragonfly/active_model_extensions/attachment.rb +250 -0
  54. data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +148 -0
  55. data/lib/dragonfly/active_model_extensions/class_methods.rb +95 -0
  56. data/lib/dragonfly/active_model_extensions/instance_methods.rb +28 -0
  57. data/lib/dragonfly/active_model_extensions/validations.rb +41 -0
  58. data/lib/dragonfly/analyser.rb +58 -0
  59. data/lib/dragonfly/analysis/file_command_analyser.rb +32 -0
  60. data/lib/dragonfly/analysis/image_magick_analyser.rb +6 -0
  61. data/lib/dragonfly/app.rb +172 -0
  62. data/lib/dragonfly/config/heroku.rb +19 -0
  63. data/lib/dragonfly/config/image_magick.rb +6 -0
  64. data/lib/dragonfly/config/rails.rb +20 -0
  65. data/lib/dragonfly/configurable.rb +207 -0
  66. data/lib/dragonfly/core_ext/array.rb +7 -0
  67. data/lib/dragonfly/core_ext/hash.rb +7 -0
  68. data/lib/dragonfly/core_ext/object.rb +12 -0
  69. data/lib/dragonfly/core_ext/string.rb +9 -0
  70. data/lib/dragonfly/core_ext/symbol.rb +9 -0
  71. data/lib/dragonfly/data_storage.rb +9 -0
  72. data/lib/dragonfly/data_storage/couch_data_store.rb +64 -0
  73. data/lib/dragonfly/data_storage/file_data_store.rb +141 -0
  74. data/lib/dragonfly/data_storage/mongo_data_store.rb +86 -0
  75. data/lib/dragonfly/data_storage/s3data_store.rb +145 -0
  76. data/lib/dragonfly/encoder.rb +13 -0
  77. data/lib/dragonfly/encoding/image_magick_encoder.rb +6 -0
  78. data/lib/dragonfly/function_manager.rb +71 -0
  79. data/lib/dragonfly/generation/image_magick_generator.rb +6 -0
  80. data/lib/dragonfly/generator.rb +9 -0
  81. data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
  82. data/lib/dragonfly/image_magick/analyser.rb +51 -0
  83. data/lib/dragonfly/image_magick/config.rb +41 -0
  84. data/lib/dragonfly/image_magick/encoder.rb +57 -0
  85. data/lib/dragonfly/image_magick/generator.rb +145 -0
  86. data/lib/dragonfly/image_magick/processor.rb +99 -0
  87. data/lib/dragonfly/image_magick/utils.rb +72 -0
  88. data/lib/dragonfly/image_magick_utils.rb +4 -0
  89. data/lib/dragonfly/job.rb +451 -0
  90. data/lib/dragonfly/job_builder.rb +39 -0
  91. data/lib/dragonfly/job_definitions.rb +26 -0
  92. data/lib/dragonfly/job_endpoint.rb +15 -0
  93. data/lib/dragonfly/loggable.rb +28 -0
  94. data/lib/dragonfly/middleware.rb +20 -0
  95. data/lib/dragonfly/processing/image_magick_processor.rb +6 -0
  96. data/lib/dragonfly/processor.rb +9 -0
  97. data/lib/dragonfly/rails/images.rb +27 -0
  98. data/lib/dragonfly/response.rb +97 -0
  99. data/lib/dragonfly/routed_endpoint.rb +40 -0
  100. data/lib/dragonfly/serializer.rb +32 -0
  101. data/lib/dragonfly/server.rb +113 -0
  102. data/lib/dragonfly/simple_cache.rb +23 -0
  103. data/lib/dragonfly/temp_object.rb +175 -0
  104. data/lib/dragonfly/url_mapper.rb +78 -0
  105. data/samples/beach.png +0 -0
  106. data/samples/egg.png +0 -0
  107. data/samples/round.gif +0 -0
  108. data/samples/sample.docx +0 -0
  109. data/samples/taj.jpg +0 -0
  110. data/spec/dragonfly/active_model_extensions/model_spec.rb +1426 -0
  111. data/spec/dragonfly/active_model_extensions/spec_helper.rb +91 -0
  112. data/spec/dragonfly/analyser_spec.rb +123 -0
  113. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +48 -0
  114. data/spec/dragonfly/app_spec.rb +135 -0
  115. data/spec/dragonfly/configurable_spec.rb +461 -0
  116. data/spec/dragonfly/core_ext/array_spec.rb +19 -0
  117. data/spec/dragonfly/core_ext/hash_spec.rb +19 -0
  118. data/spec/dragonfly/core_ext/string_spec.rb +17 -0
  119. data/spec/dragonfly/core_ext/symbol_spec.rb +17 -0
  120. data/spec/dragonfly/data_storage/couch_data_store_spec.rb +76 -0
  121. data/spec/dragonfly/data_storage/file_data_store_spec.rb +296 -0
  122. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +57 -0
  123. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +258 -0
  124. data/spec/dragonfly/data_storage/shared_data_store_examples.rb +77 -0
  125. data/spec/dragonfly/function_manager_spec.rb +154 -0
  126. data/spec/dragonfly/hash_with_css_style_keys_spec.rb +24 -0
  127. data/spec/dragonfly/image_magick/analyser_spec.rb +64 -0
  128. data/spec/dragonfly/image_magick/encoder_spec.rb +41 -0
  129. data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
  130. data/spec/dragonfly/image_magick/processor_spec.rb +233 -0
  131. data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
  132. data/spec/dragonfly/job_builder_spec.rb +37 -0
  133. data/spec/dragonfly/job_definitions_spec.rb +35 -0
  134. data/spec/dragonfly/job_endpoint_spec.rb +173 -0
  135. data/spec/dragonfly/job_spec.rb +1046 -0
  136. data/spec/dragonfly/loggable_spec.rb +80 -0
  137. data/spec/dragonfly/middleware_spec.rb +47 -0
  138. data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
  139. data/spec/dragonfly/serializer_spec.rb +61 -0
  140. data/spec/dragonfly/server_spec.rb +278 -0
  141. data/spec/dragonfly/simple_cache_spec.rb +27 -0
  142. data/spec/dragonfly/temp_object_spec.rb +306 -0
  143. data/spec/dragonfly/url_mapper_spec.rb +126 -0
  144. data/spec/functional/deprecations_spec.rb +51 -0
  145. data/spec/functional/image_magick_app_spec.rb +27 -0
  146. data/spec/functional/model_urls_spec.rb +85 -0
  147. data/spec/functional/remote_on_the_fly_spec.rb +51 -0
  148. data/spec/functional/to_response_spec.rb +31 -0
  149. data/spec/spec_helper.rb +51 -0
  150. data/spec/support/argument_matchers.rb +19 -0
  151. data/spec/support/image_matchers.rb +47 -0
  152. data/spec/support/simple_matchers.rb +53 -0
  153. data/yard/handlers/configurable_attr_handler.rb +38 -0
  154. data/yard/setup.rb +15 -0
  155. data/yard/templates/default/fulldoc/html/css/common.css +107 -0
  156. data/yard/templates/default/layout/html/layout.erb +89 -0
  157. data/yard/templates/default/module/html/configuration_summary.erb +31 -0
  158. data/yard/templates/default/module/setup.rb +17 -0
  159. metadata +544 -0
@@ -0,0 +1,7 @@
1
+ class Array
2
+
3
+ def to_dragonfly_unique_s
4
+ map{|item| item.to_dragonfly_unique_s }.join
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class Hash
2
+
3
+ def to_dragonfly_unique_s
4
+ sort_by{|k, v| k.to_dragonfly_unique_s }.to_dragonfly_unique_s
5
+ end
6
+
7
+ end
@@ -0,0 +1,12 @@
1
+ class Object
2
+
3
+ # Don't want to depend on activesupport for this
4
+ def blank?
5
+ respond_to?(:empty?) ? empty? : !self
6
+ end
7
+
8
+ def to_dragonfly_unique_s
9
+ to_s
10
+ end
11
+
12
+ end
@@ -0,0 +1,9 @@
1
+ class String
2
+
3
+ # Ruby 1.8 reports methods as strings,
4
+ # whereas 1.9 reports them as symbols
5
+ def to_method_name
6
+ RUBY_VERSION =~ /^1.8/ ? self : to_sym
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ class Symbol
2
+
3
+ # Ruby 1.8 reports methods as strings,
4
+ # whereas 1.9 reports them as symbols
5
+ def to_method_name
6
+ RUBY_VERSION =~ /^1.8/ ? to_s : self
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ module Dragonfly
2
+ module DataStorage
3
+
4
+ # Exceptions
5
+ class DataNotFound < StandardError; end
6
+ class UnableToStore < StandardError; end
7
+
8
+ end
9
+ end
@@ -0,0 +1,64 @@
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]
18
+ self.port = 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
+ response['id']
34
+ end
35
+ rescue RuntimeError => e
36
+ raise UnableToStore, "#{e} - #{temp_object.inspect}"
37
+ end
38
+
39
+ def retrieve(uid)
40
+ doc = db.get(uid)
41
+ name = doc['_attachments'].keys.first
42
+ [doc.fetch_attachment(name), marshal_decode(doc['meta'])]
43
+ rescue RestClient::ResourceNotFound => e
44
+ raise DataNotFound, "#{e} - #{uid}"
45
+ end
46
+
47
+ def destroy(uid)
48
+ doc = db.get(uid)
49
+ db.delete_doc(doc)
50
+ rescue RestClient::ResourceNotFound => e
51
+ raise DataNotFound, "#{e} - #{uid}"
52
+ end
53
+
54
+ def db
55
+ @db ||= begin
56
+ auth = username.blank? ? nil : "#{username}:#{password}@"
57
+ url = "http://#{auth}#{host}:#{port}"
58
+ CouchRest.new(url).database!(database)
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,141 @@
1
+ require 'pathname'
2
+
3
+ module Dragonfly
4
+ module DataStorage
5
+
6
+ class FileDataStore
7
+
8
+ # Exceptions
9
+ class UnableToFormUrl < RuntimeError; end
10
+
11
+ include Configurable
12
+
13
+ configurable_attr :root_path, '/var/tmp/dragonfly'
14
+ configurable_attr :server_root
15
+ configurable_attr :store_meta, true
16
+
17
+ def store(temp_object, opts={})
18
+ meta = opts[:meta] || {}
19
+ relative_path = if opts[:path]
20
+ opts[:path]
21
+ else
22
+ filename = meta[:name] || temp_object.original_filename || 'file'
23
+ relative_path = relative_path_for(filename)
24
+ end
25
+
26
+ begin
27
+ path = absolute(relative_path)
28
+ until !File.exist?(path)
29
+ path = disambiguate(path)
30
+ end
31
+ prepare_path(path)
32
+ temp_object.to_file(path).close
33
+ store_meta_data(path, meta) if store_meta
34
+ rescue Errno::EACCES => e
35
+ raise UnableToStore, e.message
36
+ end
37
+
38
+ relative(path)
39
+ end
40
+
41
+ def retrieve(relative_path)
42
+ path = absolute(relative_path)
43
+ pathname = Pathname.new(path)
44
+ raise DataNotFound, "couldn't find file #{path}" unless pathname.exist?
45
+ [
46
+ pathname,
47
+ (store_meta ? retrieve_meta_data(path) : {})
48
+ ]
49
+ end
50
+
51
+ def destroy(relative_path)
52
+ path = absolute(relative_path)
53
+ FileUtils.rm path
54
+ FileUtils.rm_f meta_data_path(path)
55
+ FileUtils.rm_f deprecated_meta_data_path(path)
56
+ purge_empty_directories(relative_path)
57
+ rescue Errno::ENOENT => e
58
+ raise DataNotFound, e.message
59
+ end
60
+
61
+ def url_for(relative_path, opts={})
62
+ if server_root.nil?
63
+ raise NotConfigured, "you need to configure server_root for #{self.class.name} in order to form urls"
64
+ else
65
+ _, __, path = absolute(relative_path).partition(server_root)
66
+ if path.empty?
67
+ raise UnableToFormUrl, "couldn't form url for uid #{relative_path.inspect} with root_path #{root_path.inspect} and server_root #{server_root.inspect}"
68
+ else
69
+ path
70
+ end
71
+ end
72
+ end
73
+
74
+ def disambiguate(path)
75
+ dirname = File.dirname(path)
76
+ basename = File.basename(path, '.*')
77
+ extname = File.extname(path)
78
+ "#{dirname}/#{basename}_#{(Time.now.usec*10 + rand(100)).to_s(32)}#{extname}"
79
+ end
80
+
81
+ private
82
+
83
+ def absolute(relative_path)
84
+ File.join(root_path, relative_path)
85
+ end
86
+
87
+ def relative(absolute_path)
88
+ absolute_path[/^#{Regexp.escape root_path}\/?(.*)$/, 1]
89
+ end
90
+
91
+ def directory_empty?(path)
92
+ Dir.entries(path) == ['.','..']
93
+ end
94
+
95
+ def meta_data_path(data_path)
96
+ "#{data_path}.meta"
97
+ end
98
+
99
+ def deprecated_meta_data_path(data_path)
100
+ "#{data_path}.extra"
101
+ end
102
+
103
+ def relative_path_for(filename)
104
+ time = Time.now
105
+ msec = time.usec / 1000
106
+ "#{time.strftime '%Y/%m/%d/%H_%M_%S'}_#{msec}_#{filename.gsub(/[^\w.]+/,'_')}"
107
+ end
108
+
109
+ def store_meta_data(data_path, meta)
110
+ File.open(meta_data_path(data_path), 'wb') do |f|
111
+ f.write Marshal.dump(meta)
112
+ end
113
+ end
114
+
115
+ def retrieve_meta_data(data_path)
116
+ path = meta_data_path(data_path)
117
+ if File.exist?(path)
118
+ File.open(path,'rb'){|f| Marshal.load(f.read) }
119
+ else
120
+ deprecated_path = deprecated_meta_data_path(data_path)
121
+ File.exist?(deprecated_path) ? File.open(deprecated_path,'rb'){|f| Marshal.load(f.read) } : {}
122
+ end
123
+ end
124
+
125
+ def prepare_path(path)
126
+ dir = File.dirname(path)
127
+ FileUtils.mkdir_p(dir) unless File.exist?(dir)
128
+ end
129
+
130
+ def purge_empty_directories(path)
131
+ containing_directory = Pathname.new(path).dirname
132
+ containing_directory.ascend do |relative_dir|
133
+ dir = absolute(relative_dir)
134
+ FileUtils.rmdir dir if directory_empty?(dir)
135
+ end
136
+ end
137
+
138
+ end
139
+
140
+ end
141
+ end
@@ -0,0 +1,86 @@
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
+ configurable_attr :username
14
+ configurable_attr :password
15
+ configurable_attr :connection
16
+ configurable_attr :db
17
+
18
+ # Mongo gem deprecated ObjectID in favour of ObjectId
19
+ OBJECT_ID = defined?(BSON::ObjectId) ? BSON::ObjectId : BSON::ObjectID
20
+ INVALID_OBJECT_ID = defined?(BSON::InvalidObjectId) ? BSON::InvalidObjectId : BSON::InvalidObjectID
21
+
22
+ def initialize(opts={})
23
+ self.host = opts[:host]
24
+ self.port = opts[:port]
25
+ self.database = opts[:database] if opts[:database]
26
+ self.username = opts[:username]
27
+ self.password = opts[:password]
28
+ self.connection = opts[:connection]
29
+ self.db = opts[:db]
30
+ end
31
+
32
+ def store(temp_object, opts={})
33
+ ensure_authenticated!
34
+ temp_object.file do |f|
35
+ mongo_id = grid.put(f, :metadata => marshal_encode(opts[:meta] || {}))
36
+ mongo_id.to_s
37
+ end
38
+ end
39
+
40
+ def retrieve(uid)
41
+ ensure_authenticated!
42
+ grid_io = grid.get(bson_id(uid))
43
+ meta = marshal_decode(grid_io.metadata)
44
+ meta.merge!(:stored_at => grid_io.upload_date)
45
+ [
46
+ grid_io.read,
47
+ meta
48
+ ]
49
+ rescue Mongo::GridFileNotFound, INVALID_OBJECT_ID => e
50
+ raise DataNotFound, "#{e} - #{uid}"
51
+ end
52
+
53
+ def destroy(uid)
54
+ ensure_authenticated!
55
+ grid.delete(bson_id(uid))
56
+ rescue Mongo::GridFileNotFound, INVALID_OBJECT_ID => e
57
+ raise DataNotFound, "#{e} - #{uid}"
58
+ end
59
+
60
+ def connection
61
+ @connection ||= Mongo::Connection.new(host, port)
62
+ end
63
+
64
+ def db
65
+ @db ||= connection.db(database)
66
+ end
67
+
68
+ def grid
69
+ @grid ||= Mongo::Grid.new(db)
70
+ end
71
+
72
+ private
73
+
74
+ def ensure_authenticated!
75
+ if username
76
+ @authenticated ||= db.authenticate(username, password)
77
+ end
78
+ end
79
+
80
+ def bson_id(uid)
81
+ OBJECT_ID.from_string(uid)
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,145 @@
1
+ require 'fog'
2
+
3
+ module Dragonfly
4
+ module DataStorage
5
+
6
+ class S3DataStore
7
+
8
+ include Configurable
9
+ include Serializer
10
+
11
+ configurable_attr :bucket_name
12
+ configurable_attr :access_key_id
13
+ configurable_attr :secret_access_key
14
+ configurable_attr :use_filesystem, true
15
+ configurable_attr :region
16
+ configurable_attr :storage_headers, {'x-amz-acl' => 'public-read'}
17
+ configurable_attr :specific_uid
18
+
19
+ REGIONS = {
20
+ 'us-east-1' => 's3.amazonaws.com', #default
21
+ 'eu-west-1' => 's3-eu-west-1.amazonaws.com',
22
+ 'ap-southeast-1' => 's3-ap-southeast-1.amazonaws.com',
23
+ 'us-west-1' => 's3-us-west-1.amazonaws.com'
24
+ }
25
+
26
+ def initialize(opts={})
27
+ self.bucket_name = opts[:bucket_name]
28
+ self.access_key_id = opts[:access_key_id]
29
+ self.secret_access_key = opts[:secret_access_key]
30
+ self.region = opts[:region]
31
+ end
32
+
33
+ def store(temp_object, opts={})
34
+ ensure_configured
35
+ ensure_bucket_initialized
36
+
37
+ meta = opts[:meta] || {}
38
+ headers = opts[:headers] || {}
39
+ uid = opts[:path] || generate_uid(meta[:name] || temp_object.original_filename || 'file')
40
+
41
+ if use_filesystem
42
+ temp_object.file do |f|
43
+ storage.put_object(bucket_name, uid, f, full_storage_headers(headers, meta))
44
+ end
45
+ else
46
+ storage.put_object(bucket_name, uid, temp_object.data, full_storage_headers(headers, meta))
47
+ end
48
+
49
+ uid
50
+ end
51
+
52
+ def retrieve(uid)
53
+ ensure_configured
54
+ response = storage.get_object(bucket_name, uid)
55
+ [
56
+ response.body,
57
+ parse_s3_metadata(response.headers)
58
+ ]
59
+ rescue Excon::Errors::NotFound => e
60
+ raise DataNotFound, "#{e} - #{uid}"
61
+ end
62
+
63
+ def destroy(uid)
64
+ storage.delete_object(bucket_name, uid)
65
+ rescue Excon::Errors::NotFound => e
66
+ raise DataNotFound, "#{e} - #{uid}"
67
+ end
68
+
69
+ def url_for(uid, opts={})
70
+ if opts && opts[:expires]
71
+ storage.get_object_url(bucket_name, uid, opts[:expires])
72
+ else
73
+ "http://#{bucket_name}.s3.amazonaws.com/#{uid}"
74
+ end
75
+ end
76
+
77
+ def domain
78
+ REGIONS[get_region]
79
+ end
80
+
81
+ def storage
82
+ @storage ||= Fog::Storage.new(
83
+ :provider => 'AWS',
84
+ :aws_access_key_id => access_key_id,
85
+ :aws_secret_access_key => secret_access_key,
86
+ :region => region
87
+ )
88
+ end
89
+
90
+ def bucket_exists?
91
+ storage.get_bucket_location(bucket_name)
92
+ true
93
+ rescue Excon::Errors::NotFound => e
94
+ false
95
+ end
96
+
97
+ private
98
+
99
+ def ensure_configured
100
+ unless @configured
101
+ [:bucket_name, :access_key_id, :secret_access_key].each do |attr|
102
+ raise NotConfigured, "You need to configure #{self.class.name} with #{attr}" if send(attr).nil?
103
+ end
104
+ @configured = true
105
+ end
106
+ end
107
+
108
+ def ensure_bucket_initialized
109
+ unless @bucket_initialized
110
+ storage.put_bucket(bucket_name, 'LocationConstraint' => region) unless bucket_exists?
111
+ @bucket_initialized = true
112
+ end
113
+ end
114
+
115
+ def get_region
116
+ reg = region || 'us-east-1'
117
+ raise "Invalid region #{reg} - should be one of #{valid_regions.join(', ')}" unless valid_regions.include?(reg)
118
+ reg
119
+ end
120
+
121
+ def generate_uid(name)
122
+ if self.specific_uid.is_a?(Proc)
123
+ self.specific_uid.call(name)
124
+ else
125
+ "#{Time.now.strftime '%Y/%m/%d/%H/%M/%S'}/#{rand(1000)}/#{name.gsub(/[^\w.]+/, '_')}"
126
+ end
127
+ end
128
+
129
+ def full_storage_headers(headers, meta)
130
+ {'x-amz-meta-extra' => marshal_encode(meta)}.merge(storage_headers).merge(headers)
131
+ end
132
+
133
+ def parse_s3_metadata(headers)
134
+ encoded_meta = headers['x-amz-meta-extra']
135
+ (marshal_decode(encoded_meta) if encoded_meta) || {}
136
+ end
137
+
138
+ def valid_regions
139
+ REGIONS.keys
140
+ end
141
+
142
+ end
143
+
144
+ end
145
+ end