shrine 2.19.3 → 3.6.0

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 (211) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +523 -41
  3. data/LICENSE.txt +1 -1
  4. data/README.md +83 -979
  5. data/doc/advantages.md +231 -204
  6. data/doc/attacher.md +304 -153
  7. data/doc/carrierwave.md +297 -226
  8. data/doc/changing_derivatives.md +308 -0
  9. data/doc/changing_location.md +103 -21
  10. data/doc/changing_storage.md +110 -0
  11. data/doc/creating_persistence_plugins.md +132 -0
  12. data/doc/creating_plugins.md +43 -23
  13. data/doc/creating_storages.md +19 -5
  14. data/doc/design.md +147 -97
  15. data/doc/direct_s3.md +38 -28
  16. data/doc/external/articles.md +63 -0
  17. data/doc/external/extensions.md +53 -0
  18. data/doc/external/misc.md +32 -0
  19. data/doc/getting_started.md +1156 -0
  20. data/doc/metadata.md +190 -109
  21. data/doc/multiple_files.md +93 -30
  22. data/doc/paperclip.md +384 -262
  23. data/doc/plugins/activerecord.md +177 -46
  24. data/doc/plugins/add_metadata.md +139 -38
  25. data/doc/plugins/atomic_helpers.md +217 -0
  26. data/doc/plugins/backgrounding.md +156 -98
  27. data/doc/plugins/cached_attachment_data.md +7 -5
  28. data/doc/plugins/column.md +121 -0
  29. data/doc/plugins/data_uri.md +23 -22
  30. data/doc/plugins/default_storage.md +36 -10
  31. data/doc/plugins/default_url.md +30 -13
  32. data/doc/plugins/delete_raw.md +4 -2
  33. data/doc/plugins/derivation_endpoint.md +186 -101
  34. data/doc/plugins/derivatives.md +839 -0
  35. data/doc/plugins/determine_mime_type.md +4 -2
  36. data/doc/plugins/download_endpoint.md +64 -8
  37. data/doc/plugins/dynamic_storage.md +5 -3
  38. data/doc/plugins/entity.md +263 -0
  39. data/doc/plugins/form_assign.md +55 -0
  40. data/doc/plugins/included.md +31 -8
  41. data/doc/plugins/infer_extension.md +21 -10
  42. data/doc/plugins/instrumentation.md +38 -16
  43. data/doc/plugins/keep_files.md +16 -17
  44. data/doc/plugins/metadata_attributes.md +42 -13
  45. data/doc/plugins/mirroring.md +118 -0
  46. data/doc/plugins/model.md +210 -0
  47. data/doc/plugins/module_include.md +4 -2
  48. data/doc/plugins/multi_cache.md +24 -0
  49. data/doc/plugins/persistence.md +101 -0
  50. data/doc/plugins/presign_endpoint.md +9 -4
  51. data/doc/plugins/pretty_location.md +16 -3
  52. data/doc/plugins/processing.md +4 -2
  53. data/doc/plugins/rack_file.md +8 -2
  54. data/doc/plugins/rack_response.md +6 -2
  55. data/doc/plugins/recache.md +4 -2
  56. data/doc/plugins/refresh_metadata.md +49 -9
  57. data/doc/plugins/remote_url.md +84 -47
  58. data/doc/plugins/remove_attachment.md +27 -6
  59. data/doc/plugins/remove_invalid.md +21 -6
  60. data/doc/plugins/restore_cached_data.md +11 -3
  61. data/doc/plugins/sequel.md +159 -35
  62. data/doc/plugins/signature.md +16 -5
  63. data/doc/plugins/store_dimensions.md +14 -2
  64. data/doc/plugins/tempfile.md +4 -2
  65. data/doc/plugins/type_predicates.md +96 -0
  66. data/doc/plugins/upload_endpoint.md +13 -13
  67. data/doc/plugins/upload_options.md +6 -4
  68. data/doc/plugins/{default_url_options.md → url_options.md} +9 -7
  69. data/doc/plugins/validation.md +97 -0
  70. data/doc/plugins/validation_helpers.md +16 -13
  71. data/doc/plugins/versions.md +15 -19
  72. data/doc/processing.md +438 -221
  73. data/doc/refile.md +188 -170
  74. data/doc/release_notes/1.0.0.md +4 -0
  75. data/doc/release_notes/1.1.0.md +6 -2
  76. data/doc/release_notes/1.2.0.md +4 -0
  77. data/doc/release_notes/1.3.0.md +4 -0
  78. data/doc/release_notes/1.4.0.md +4 -0
  79. data/doc/release_notes/1.4.1.md +4 -0
  80. data/doc/release_notes/1.4.2.md +4 -0
  81. data/doc/release_notes/2.0.0.md +4 -0
  82. data/doc/release_notes/2.0.1.md +4 -0
  83. data/doc/release_notes/2.1.0.md +5 -1
  84. data/doc/release_notes/2.1.1.md +4 -0
  85. data/doc/release_notes/2.10.0.md +4 -0
  86. data/doc/release_notes/2.10.1.md +4 -0
  87. data/doc/release_notes/2.11.0.md +4 -0
  88. data/doc/release_notes/2.12.0.md +4 -0
  89. data/doc/release_notes/2.13.0.md +4 -0
  90. data/doc/release_notes/2.14.0.md +5 -1
  91. data/doc/release_notes/2.15.0.md +11 -7
  92. data/doc/release_notes/2.16.0.md +4 -0
  93. data/doc/release_notes/2.17.0.md +4 -0
  94. data/doc/release_notes/2.18.0.md +4 -0
  95. data/doc/release_notes/2.19.0.md +6 -3
  96. data/doc/release_notes/2.2.0.md +4 -0
  97. data/doc/release_notes/2.3.0.md +4 -0
  98. data/doc/release_notes/2.3.1.md +4 -0
  99. data/doc/release_notes/2.4.0.md +4 -0
  100. data/doc/release_notes/2.4.1.md +4 -0
  101. data/doc/release_notes/2.5.0.md +4 -0
  102. data/doc/release_notes/2.6.0.md +4 -0
  103. data/doc/release_notes/2.6.1.md +4 -0
  104. data/doc/release_notes/2.7.0.md +4 -0
  105. data/doc/release_notes/2.8.0.md +4 -0
  106. data/doc/release_notes/2.9.0.md +4 -0
  107. data/doc/release_notes/3.0.0.md +981 -0
  108. data/doc/release_notes/3.0.1.md +22 -0
  109. data/doc/release_notes/3.1.0.md +73 -0
  110. data/doc/release_notes/3.2.0.md +96 -0
  111. data/doc/release_notes/3.2.1.md +31 -0
  112. data/doc/release_notes/3.2.2.md +14 -0
  113. data/doc/release_notes/3.3.0.md +105 -0
  114. data/doc/release_notes/3.4.0.md +35 -0
  115. data/doc/release_notes/3.5.0.md +63 -0
  116. data/doc/release_notes/3.6.0.md +23 -0
  117. data/doc/retrieving_uploads.md +5 -2
  118. data/doc/securing_uploads.md +60 -37
  119. data/doc/storage/file_system.md +20 -3
  120. data/doc/storage/memory.md +19 -0
  121. data/doc/storage/s3.md +122 -78
  122. data/doc/testing.md +141 -133
  123. data/doc/upgrading_to_3.md +708 -0
  124. data/doc/validation.md +54 -90
  125. data/lib/shrine/attacher.rb +292 -169
  126. data/lib/shrine/attachment.rb +13 -46
  127. data/lib/shrine/plugins/_persistence.rb +93 -0
  128. data/lib/shrine/plugins/activerecord.rb +77 -34
  129. data/lib/shrine/plugins/add_metadata.rb +25 -17
  130. data/lib/shrine/plugins/atomic_helpers.rb +119 -0
  131. data/lib/shrine/plugins/backgrounding.rb +77 -113
  132. data/lib/shrine/plugins/cached_attachment_data.rb +6 -15
  133. data/lib/shrine/plugins/column.rb +102 -0
  134. data/lib/shrine/plugins/data_uri.rb +38 -36
  135. data/lib/shrine/plugins/default_storage.rb +45 -15
  136. data/lib/shrine/plugins/default_url.rb +12 -24
  137. data/lib/shrine/plugins/default_url_options.rb +3 -30
  138. data/lib/shrine/plugins/delete_raw.rb +10 -16
  139. data/lib/shrine/plugins/derivation_endpoint.rb +130 -171
  140. data/lib/shrine/plugins/derivatives.rb +645 -0
  141. data/lib/shrine/plugins/determine_mime_type.rb +9 -21
  142. data/lib/shrine/plugins/download_endpoint.rb +118 -133
  143. data/lib/shrine/plugins/dynamic_storage.rb +5 -11
  144. data/lib/shrine/plugins/entity.rb +158 -0
  145. data/lib/shrine/plugins/form_assign.rb +108 -0
  146. data/lib/shrine/plugins/included.rb +6 -6
  147. data/lib/shrine/plugins/infer_extension.rb +17 -20
  148. data/lib/shrine/plugins/instrumentation.rb +59 -43
  149. data/lib/shrine/plugins/keep_files.rb +3 -15
  150. data/lib/shrine/plugins/metadata_attributes.rb +28 -19
  151. data/lib/shrine/plugins/mirroring.rb +142 -0
  152. data/lib/shrine/plugins/model.rb +160 -0
  153. data/lib/shrine/plugins/module_include.rb +3 -3
  154. data/lib/shrine/plugins/multi_cache.rb +27 -0
  155. data/lib/shrine/plugins/presign_endpoint.rb +27 -28
  156. data/lib/shrine/plugins/pretty_location.rb +15 -9
  157. data/lib/shrine/plugins/processing.rb +22 -9
  158. data/lib/shrine/plugins/rack_file.rb +2 -42
  159. data/lib/shrine/plugins/rack_response.rb +21 -10
  160. data/lib/shrine/plugins/recache.rb +6 -5
  161. data/lib/shrine/plugins/refresh_metadata.rb +13 -11
  162. data/lib/shrine/plugins/remote_url.rb +49 -49
  163. data/lib/shrine/plugins/remove_attachment.rb +12 -6
  164. data/lib/shrine/plugins/remove_invalid.rb +19 -8
  165. data/lib/shrine/plugins/restore_cached_data.rb +13 -7
  166. data/lib/shrine/plugins/sequel.rb +86 -36
  167. data/lib/shrine/plugins/signature.rb +10 -16
  168. data/lib/shrine/plugins/store_dimensions.rb +35 -40
  169. data/lib/shrine/plugins/tempfile.rb +1 -3
  170. data/lib/shrine/plugins/type_predicates.rb +113 -0
  171. data/lib/shrine/plugins/upload_endpoint.rb +28 -24
  172. data/lib/shrine/plugins/upload_options.rb +14 -15
  173. data/lib/shrine/plugins/url_options.rb +31 -0
  174. data/lib/shrine/plugins/validation.rb +80 -0
  175. data/lib/shrine/plugins/validation_helpers.rb +35 -58
  176. data/lib/shrine/plugins/versions.rb +107 -87
  177. data/lib/shrine/plugins.rb +22 -0
  178. data/lib/shrine/storage/file_system.rb +46 -64
  179. data/lib/shrine/storage/linter.rb +42 -7
  180. data/lib/shrine/storage/memory.rb +49 -0
  181. data/lib/shrine/storage/s3.rb +173 -160
  182. data/lib/shrine/uploaded_file.rb +32 -32
  183. data/lib/shrine/version.rb +3 -3
  184. data/lib/shrine.rb +87 -150
  185. data/shrine.gemspec +11 -12
  186. metadata +92 -82
  187. data/doc/migrating_storage.md +0 -76
  188. data/doc/plugins/backup.md +0 -31
  189. data/doc/plugins/copy.md +0 -24
  190. data/doc/plugins/delete_promoted.md +0 -12
  191. data/doc/plugins/direct_upload.md +0 -172
  192. data/doc/plugins/hooks.md +0 -58
  193. data/doc/plugins/logging.md +0 -42
  194. data/doc/plugins/migration_helpers.md +0 -60
  195. data/doc/plugins/moving.md +0 -19
  196. data/doc/plugins/multi_delete.md +0 -20
  197. data/doc/plugins/parallelize.md +0 -16
  198. data/doc/plugins/parsed_json.md +0 -23
  199. data/doc/regenerating_versions.md +0 -143
  200. data/lib/shrine/plugins/background_helpers.rb +0 -5
  201. data/lib/shrine/plugins/backup.rb +0 -90
  202. data/lib/shrine/plugins/copy.rb +0 -50
  203. data/lib/shrine/plugins/delete_promoted.rb +0 -20
  204. data/lib/shrine/plugins/direct_upload.rb +0 -217
  205. data/lib/shrine/plugins/hooks.rb +0 -90
  206. data/lib/shrine/plugins/logging.rb +0 -142
  207. data/lib/shrine/plugins/migration_helpers.rb +0 -70
  208. data/lib/shrine/plugins/moving.rb +0 -57
  209. data/lib/shrine/plugins/multi_delete.rb +0 -32
  210. data/lib/shrine/plugins/parallelize.rb +0 -78
  211. data/lib/shrine/plugins/parsed_json.rb +0 -29
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Shrine
4
+ module Plugins
5
+ # Documentation can be found on https://shrinerb.com/docs/plugins/model
6
+ module Model
7
+ def self.load_dependencies(uploader, **)
8
+ uploader.plugin :entity
9
+ end
10
+
11
+ def self.configure(uploader, **opts)
12
+ uploader.opts[:model] ||= { cache: true }
13
+ uploader.opts[:model].merge!(opts)
14
+ end
15
+
16
+ module AttachmentMethods
17
+ # Allows disabling model behaviour:
18
+ #
19
+ # Shrine::Attachment(:image) # model (default)
20
+ # Shrine::Attachment(:image, model: false) # entity
21
+ def initialize(name, model: true, **options)
22
+ super(name, **options)
23
+ @model = model
24
+ end
25
+
26
+ # We define model methods only on inclusion. This gives other plugins
27
+ # the ability to disable model behaviour for entity classes. In this
28
+ # case we want to skip defining model methods as well.
29
+ def included(klass)
30
+ super
31
+ define_model_methods(@name) if model?
32
+ end
33
+
34
+ private
35
+
36
+ # Defines attachment setter and enhances the copy constructor.
37
+ def define_model_methods(name)
38
+ super if defined?(super)
39
+
40
+ define_method :"#{name}=" do |value|
41
+ send(:"#{name}_attacher").model_assign(value)
42
+ end
43
+
44
+ define_method :"#{name}_changed?" do
45
+ send(:"#{name}_attacher").changed?
46
+ end
47
+
48
+ # The copy constructor that's called on #dup and #clone.
49
+ define_method :initialize_copy do |other|
50
+ super(other)
51
+ attacher_copy = instance_variable_get(:"@#{name}_attacher")&.dup
52
+ attacher_copy.set_entity(self, name) if attacher_copy
53
+ instance_variable_set(:"@#{name}_attacher", attacher_copy)
54
+ self
55
+ end
56
+ private :initialize_copy
57
+ end
58
+
59
+ # Memoizes the attacher instance into an instance variable.
60
+ def attacher(record, **options)
61
+ return super unless model?
62
+
63
+ if !record.instance_variable_get(:"@#{@name}_attacher") || options.any?
64
+ attacher = class_attacher(**options)
65
+ attacher.load_model(record, @name)
66
+
67
+ record.instance_variable_set(:"@#{@name}_attacher", attacher)
68
+ else
69
+ record.instance_variable_get(:"@#{@name}_attacher")
70
+ end
71
+ end
72
+
73
+ def model?
74
+ @model
75
+ end
76
+ end
77
+
78
+ module AttacherClassMethods
79
+ # Initializes itself from a model instance and attachment name.
80
+ #
81
+ # photo.image_data #=> "{...}" # a file is attached
82
+ #
83
+ # attacher = Attacher.from_model(photo, :image)
84
+ # attacher.file #=> #<Shrine::UploadedFile>
85
+ def from_model(record, name, **options)
86
+ attacher = new(**options)
87
+ attacher.load_model(record, name)
88
+ attacher
89
+ end
90
+ end
91
+
92
+ module AttacherMethods
93
+ def initialize(model_cache: shrine_class.opts[:model][:cache], **options)
94
+ super(**options)
95
+ @model_cache = model_cache
96
+ @model = nil
97
+ end
98
+
99
+ # Saves record and name and initializes attachment from the model
100
+ # attribute. Called from `Attacher.from_model`.
101
+ def load_model(record, name)
102
+ set_model(record, name)
103
+ read
104
+ end
105
+
106
+ # Saves record and name without loading attachment from the model
107
+ # attribute.
108
+ def set_model(record, name)
109
+ set_entity(record, name)
110
+ @model = true
111
+ end
112
+
113
+ # Called by the attachment attribute setter on the model.
114
+ def model_assign(value, **options)
115
+ if model_cache?
116
+ assign(value, **options)
117
+ else
118
+ attach(value, **options)
119
+ end
120
+ end
121
+
122
+ # Writes uploaded file data into the model.
123
+ def set(*)
124
+ result = super
125
+ write if model?
126
+ result
127
+ end
128
+
129
+ # Writes the attachment data into the model attribute.
130
+ def write
131
+ column_values.each do |name, value|
132
+ write_attribute(name, value)
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ # Writes given value into the model attribute.
139
+ def write_attribute(name = attribute, value)
140
+ record.public_send(:"#{name}=", value)
141
+ end
142
+
143
+ # Returns whether assigned files should be uploaded to/loaded from
144
+ # temporary storage.
145
+ def model_cache?
146
+ @model_cache
147
+ end
148
+
149
+ # Returns whether the attacher is being backed by a model instance.
150
+ # This allows users to still use the attacher with an entity instance
151
+ # or without any record instance.
152
+ def model?
153
+ @model
154
+ end
155
+ end
156
+ end
157
+
158
+ register_plugin(:model, Model)
159
+ end
160
+ end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ Shrine.deprecation("The module_include plugin is deprecated and will be removed in Shrine 4. Override core classes directly instead.")
4
+
3
5
  class Shrine
4
6
  module Plugins
5
- # Documentation lives in [doc/plugins/module_include.md] on GitHub.
6
- #
7
- # [doc/plugins/module_include.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/module_include.md
7
+ # Documentation can be found on https://shrinerb.com/docs/plugins/module_include
8
8
  module ModuleInclude
9
9
  module ClassMethods
10
10
  def attachment_module(mod = nil, &block)
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Shrine
4
+ module Plugins
5
+ # Documentation can be found on https://shrinerb.com/docs/plugins/multi_cache
6
+ module MultiCache
7
+ def self.configure(uploader, **opts)
8
+ uploader.opts[:multi_cache] ||= {}
9
+ uploader.opts[:multi_cache].merge!(opts)
10
+ end
11
+
12
+ module AttacherMethods
13
+ def cached?(file = self.file)
14
+ super || additional_cache.any? { |key| uploaded?(file, key) }
15
+ end
16
+
17
+ private
18
+
19
+ def additional_cache
20
+ Array(shrine_class.opts[:multi_cache][:additional_cache])
21
+ end
22
+ end
23
+ end
24
+
25
+ register_plugin(:multi_cache, MultiCache)
26
+ end
27
+ end
@@ -6,11 +6,9 @@ require "json"
6
6
 
7
7
  class Shrine
8
8
  module Plugins
9
- # Documentation lives in [doc/plugins/presign_endpoint.md] on GitHub.
10
- #
11
- # [doc/plugins/presign_endpoint.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/presign_endpoint.md
9
+ # Documentation can be found on https://shrinerb.com/docs/plugins/presign_endpoint
12
10
  module PresignEndpoint
13
- def self.configure(uploader, opts = {})
11
+ def self.configure(uploader, **opts)
14
12
  uploader.opts[:presign_endpoint] ||= {}
15
13
  uploader.opts[:presign_endpoint].merge!(opts)
16
14
  end
@@ -83,12 +81,19 @@ class Shrine
83
81
 
84
82
  status, headers, body = catch(:halt) do
85
83
  error!(404, "Not Found") unless ["", "/"].include?(request.path_info)
86
- error!(405, "Method Not Allowed") unless request.get?
87
84
 
88
- handle_request(request)
85
+ if request.get?
86
+ handle_request(request)
87
+ elsif request.options?
88
+ handle_options_request(request)
89
+ else
90
+ error!(405, "Method Not Allowed")
91
+ end
89
92
  end
90
93
 
91
- headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s
94
+ headers = Rack::Headers[headers] if Rack.release >= "3"
95
+ headers["Content-Length"] ||= body.respond_to?(:bytesize) ? body.bytesize.to_s :
96
+ body.map(&:bytesize).inject(0, :+).to_s
92
97
 
93
98
  [status, headers, body]
94
99
  end
@@ -111,6 +116,13 @@ class Shrine
111
116
  make_response(presign, request)
112
117
  end
113
118
 
119
+ # Uppy client sends an OPTIONS request to fetch information about the
120
+ # Uppy Companion. Since our Rack app is only acting as Uppy Companion, we
121
+ # just return a successful response.
122
+ def handle_options_request(request)
123
+ [200, {}, []]
124
+ end
125
+
114
126
  # Generates the location using `Shrine#generate_uid`, and extracts the
115
127
  # extension from the `filename` query parameter. If `:presign_location`
116
128
  # option is given, calls that instead.
@@ -137,20 +149,10 @@ class Shrine
137
149
  if @presign
138
150
  data = @presign.call(location, options, request)
139
151
  else
140
- data = storage.presign(location, options)
152
+ data = storage.presign(location, **options)
141
153
  end
142
154
 
143
- if data.respond_to?(:to_h)
144
- { fields: {}, headers: {} }.merge(data.to_h)
145
- else
146
- Shrine.deprecation("Returning a custom object in Storage#presign is deprecated, presign_endpoint will not support it in Shrine 3. Storage#presign should return a Hash instead.")
147
-
148
- url = data.url
149
- fields = data.fields
150
- headers = data.headers if data.respond_to?(:headers)
151
-
152
- { url: url, fields: fields.to_h, headers: headers.to_h }
153
- end
155
+ { fields: {}, headers: {} }.merge(data.to_h)
154
156
  end
155
157
 
156
158
  # Transforms the presign hash into a JSON response. It returns a Rack
@@ -158,16 +160,17 @@ class Shrine
158
160
  # headers, and a body enumerable. If `:rack_response` option is given,
159
161
  # calls that instead.
160
162
  def make_response(object, request)
161
- if @rack_response
162
- response = @rack_response.call(object, request)
163
+ status, headers, body = if @rack_response
164
+ @rack_response.call(object, request)
163
165
  else
164
- response = [200, { "Content-Type" => CONTENT_TYPE_JSON }, [object.to_json]]
166
+ [200, { "Content-Type" => CONTENT_TYPE_JSON }, [object.to_json]]
165
167
  end
166
168
 
169
+ headers = Rack::Headers[headers] if Rack.release >= "3"
167
170
  # prevent browsers from caching the response
168
- response[1]["Cache-Control"] = "no-store" unless response[1].key?("Cache-Control")
171
+ headers["Cache-Control"] = "no-store" unless headers.key?("Cache-Control")
169
172
 
170
- response
173
+ [status, headers, body]
171
174
  end
172
175
 
173
176
  # Used for early returning an error response.
@@ -185,8 +188,4 @@ class Shrine
185
188
  @shrine_class.find_storage(@storage_key)
186
189
  end
187
190
  end
188
-
189
- # backwards compatibility
190
- Plugins::PresignEndpoint.const_set(:App, PresignEndpoint)
191
- Plugins::PresignEndpoint.deprecate_constant(:App)
192
191
  end
@@ -2,28 +2,26 @@
2
2
 
3
3
  class Shrine
4
4
  module Plugins
5
- # Documentation lives in [doc/plugins/pretty_location.md] on GitHub.
6
- #
7
- # [doc/plugins/pretty_location.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/pretty_location.md
5
+ # Documentation can be found on https://shrinerb.com/docs/plugins/pretty_location
8
6
  module PrettyLocation
9
- def self.configure(uploader, opts = {})
7
+ def self.configure(uploader, **opts)
10
8
  uploader.opts[:pretty_location] ||= { identifier: :id }
11
9
  uploader.opts[:pretty_location].merge!(opts)
12
10
  end
13
11
 
14
12
  module InstanceMethods
15
- def generate_location(io, context)
16
- pretty_location(io, context)
13
+ def generate_location(io, **options)
14
+ pretty_location(io, **options)
17
15
  end
18
16
 
19
- def pretty_location(io, name: nil, record: nil, version: nil, identifier: nil, metadata: {}, **)
17
+ def pretty_location(io, name: nil, record: nil, version: nil, derivative: nil, identifier: nil, metadata: {}, **)
20
18
  if record
21
19
  namespace = record_namespace(record)
22
20
  identifier ||= record_identifier(record)
23
21
  end
24
22
 
25
23
  basename = basic_location(io, metadata: metadata)
26
- basename = "#{version}-#{basename}" if version
24
+ basename = [*(version || derivative), basename].join("-")
27
25
 
28
26
  [*namespace, *identifier, *name, basename].join("/")
29
27
  end
@@ -34,9 +32,17 @@ class Shrine
34
32
  record.public_send(opts[:pretty_location][:identifier])
35
33
  end
36
34
 
35
+ def transform_class_name(class_name)
36
+ if opts[:pretty_location][:class_underscore]
37
+ class_name.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z])([A-Z])/, '\1_\2').downcase
38
+ else
39
+ class_name.downcase
40
+ end
41
+ end
42
+
37
43
  def record_namespace(record)
38
44
  class_name = record.class.name or return
39
- parts = class_name.downcase.split("::")
45
+ parts = transform_class_name(class_name).split("::")
40
46
 
41
47
  if separator = opts[:pretty_location][:namespace]
42
48
  parts.join(separator)
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ Shrine.deprecation("The processing plugin is deprecated and will be removed in Shrine 4. If you were using it with versions plugin, use the new derivatives plugin instead.")
4
+
3
5
  class Shrine
4
6
  module Plugins
5
- # Documentation lives in [doc/plugins/processing.md] on GitHub.
6
- #
7
- # [doc/plugins/processing.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/processing.md
7
+ # Documentation can be found on https://shrinerb.com/docs/plugins/processing
8
8
  module Processing
9
9
  def self.configure(uploader)
10
10
  uploader.opts[:processing] ||= {}
@@ -13,19 +13,32 @@ class Shrine
13
13
  module ClassMethods
14
14
  def process(action, &block)
15
15
  opts[:processing][action] ||= []
16
- opts[:processing][action] += [block]
16
+ opts[:processing][action] << block
17
17
  end
18
18
  end
19
19
 
20
20
  module InstanceMethods
21
- def process(io, context = {})
22
- pipeline = opts[:processing][context[:action]] || []
21
+ def upload(io, process: true, **options)
22
+ if process
23
+ input = process(io, **options)
24
+ else
25
+ input = io
26
+ end
27
+
28
+ super(input, **options)
29
+ end
30
+
31
+ private
23
32
 
24
- result = pipeline.inject(io) do |input, processing|
25
- instance_exec(input, context, &processing) || input
33
+ def process(io, **options)
34
+ pipeline = processing_pipeline(options[:action])
35
+ pipeline.inject(io) do |input, processor|
36
+ instance_exec(input, **options, &processor) || input
26
37
  end
38
+ end
27
39
 
28
- result unless result == io
40
+ def processing_pipeline(key)
41
+ opts[:processing][key] || []
29
42
  end
30
43
  end
31
44
  end
@@ -4,9 +4,7 @@ require "forwardable"
4
4
 
5
5
  class Shrine
6
6
  module Plugins
7
- # Documentation lives in [doc/plugins/rack_file.md] on GitHub.
8
- #
9
- # [doc/plugins/rack_file.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/rack_file.md
7
+ # Documentation can be found on https://shrinerb.com/docs/plugins/rack_file
10
8
  module RackFile
11
9
  module ClassMethods
12
10
  # Accepts a Rack uploaded file hash and wraps it in an IO object.
@@ -23,46 +21,12 @@ class Shrine
23
21
  end
24
22
  end
25
23
 
26
- module InstanceMethods
27
- # If `io` is a Rack uploaded file hash, converts it to an IO-like
28
- # object and calls `super`.
29
- def upload(io, context = {})
30
- super(convert_rack_file(io), context)
31
- end
32
-
33
- # If `io` is a Rack uploaded file hash, converts it to an IO-like
34
- # object and calls `super`.
35
- def store(io, context = {})
36
- super(convert_rack_file(io), context)
37
- end
38
-
39
- private
40
-
41
- # If given a Rack uploaded file hash, returns a
42
- # `Shrine::Plugins::RackFile::UploadedFile` IO-like object wrapping that
43
- # hash, otherwise returns the value unchanged.
44
- def convert_rack_file(value)
45
- if rack_file?(value)
46
- Shrine.deprecation("Passing a Rack uploaded file hash to Shrine#upload is deprecated, use Shrine.rack_file to convert the Rack file hash into an IO object.")
47
- self.class.rack_file(value)
48
- else
49
- value
50
- end
51
- end
52
-
53
- # Returns whether a given value is a Rack uploaded file hash, by
54
- # checking whether it's a hash with `:tempfile` and `:name` keys.
55
- def rack_file?(value)
56
- value.is_a?(Hash) && value.key?(:tempfile) && value.key?(:name)
57
- end
58
- end
59
-
60
24
  module AttacherMethods
61
25
  # Checks whether a file is a Rack file hash, and in that case wraps the
62
26
  # hash in an IO-like object.
63
27
  def assign(value, **options)
64
28
  if rack_file?(value)
65
- assign(shrine_class.rack_file(value), **options)
29
+ assign shrine_class.rack_file(value), **options
66
30
  else
67
31
  super
68
32
  end
@@ -101,8 +65,4 @@ class Shrine
101
65
  extend Forwardable
102
66
  delegate [:read, :size, :rewind, :eof?, :close] => :@tempfile
103
67
  end
104
-
105
- # backwards compatibility
106
- Plugins::RackFile.const_set(:UploadedFile, RackFile)
107
- Plugins::RackFile.deprecate_constant(:UploadedFile)
108
68
  end
@@ -6,9 +6,7 @@ require "digest"
6
6
 
7
7
  class Shrine
8
8
  module Plugins
9
- # Documentation lives in [doc/plugins/rack_response.md] on GitHub.
10
- #
11
- # [doc/plugins/rack_response.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/rack_response.md
9
+ # Documentation can be found on https://shrinerb.com/docs/plugins/rack_response
12
10
  module RackResponse
13
11
  module FileMethods
14
12
  # Returns a Rack response triple for the uploaded file.
@@ -34,6 +32,8 @@ class Shrine
34
32
  headers = rack_headers(**options)
35
33
  body = rack_body(**options)
36
34
 
35
+ headers = Rack::Headers[headers] if Rack.release >= "3"
36
+
37
37
  [status, headers, body]
38
38
  end
39
39
 
@@ -101,16 +101,23 @@ class Shrine
101
101
  FileBody.new(file, range: range)
102
102
  end
103
103
 
104
- # Parses the value of a "Range" HTTP header.
104
+ # Retrieves a range value parsed from HTTP "Range" header.
105
105
  def parse_content_range(range_header)
106
- if Rack.release >= "2.0"
107
- ranges = Rack::Utils.get_byte_ranges(range_header, file.size)
108
- else
109
- ranges = Rack::Utils.byte_ranges({ "HTTP_RANGE" => range_header }, file.size)
110
- end
111
-
106
+ ranges = get_byte_ranges(range_header)
112
107
  ranges.first if ranges && ranges.one?
113
108
  end
109
+
110
+ if Rack.release >= "2.0"
111
+ def get_byte_ranges(range_header)
112
+ Rack::Utils.get_byte_ranges(range_header, file.size)
113
+ end
114
+ else
115
+ # :nocov:
116
+ def get_byte_ranges(range_header)
117
+ Rack::Utils.byte_ranges({ "HTTP_RANGE" => range_header }, file.size)
118
+ end
119
+ # :nocov:
120
+ end
114
121
  end
115
122
 
116
123
  # Implements the interface of a Rack response body object.
@@ -136,6 +143,10 @@ class Shrine
136
143
  file.close
137
144
  end
138
145
 
146
+ def bytesize
147
+ each.inject(0) { |sum, chunk| sum += chunk.length }
148
+ end
149
+
139
150
  # Rack::Sendfile is activated when response body responds to #to_path.
140
151
  def respond_to_missing?(name, include_private = false)
141
152
  name == :to_path && path
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ Shrine.deprecation("The recache plugin is deprecated and will be removed in Shrine 4. If you were using it with versions plugin, use the new derivatives plugin instead.")
4
+
3
5
  class Shrine
4
6
  module Plugins
5
- # Documentation lives in [doc/plugins/recache.md] on GitHub.
6
- #
7
- # [doc/plugins/recache.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/recache.md
7
+ # Documentation can be found on https://shrinerb.com/docs/plugins/recache
8
8
  module Recache
9
9
  module AttacherMethods
10
10
  def save
@@ -14,8 +14,9 @@ class Shrine
14
14
 
15
15
  def recache
16
16
  if cached?
17
- recached = cache!(get, action: :recache)
18
- _set(recached)
17
+ result = upload(file, cache_key, action: :recache)
18
+
19
+ set(result)
19
20
  end
20
21
  end
21
22
  end
@@ -2,20 +2,22 @@
2
2
 
3
3
  class Shrine
4
4
  module Plugins
5
- # Documentation lives in [doc/plugins/refresh_metadata.md] on GitHub.
6
- #
7
- # [doc/plugins/refresh_metadata.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/refresh_metadata.md
5
+ # Documentation can be found on https://shrinerb.com/docs/plugins/refresh_metadata
8
6
  module RefreshMetadata
7
+ module AttacherMethods
8
+ def refresh_metadata!(**options)
9
+ file!.refresh_metadata!(**context, **options)
10
+ set(file) # trigger model write
11
+ end
12
+ end
13
+
9
14
  module FileMethods
10
- def refresh_metadata!(**context)
11
- refreshed_metadata =
12
- if opened?
13
- uploader.send(:get_metadata, self, metadata: true, **context)
14
- else
15
- open { uploader.send(:get_metadata, self, metadata: true, **context) }
16
- end
15
+ def refresh_metadata!(**options)
16
+ return open { refresh_metadata!(**options) } unless opened?
17
+
18
+ refreshed_metadata = uploader.send(:get_metadata, self, metadata: true, **options)
17
19
 
18
- @data = @data.merge("metadata" => metadata.merge(refreshed_metadata))
20
+ @metadata = @metadata.merge(refreshed_metadata)
19
21
  end
20
22
  end
21
23
  end