shrine 2.19.4 → 3.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of shrine might be problematic. Click here for more details.

Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -11
  3. data/README.md +9 -3
  4. data/doc/advantages.md +1 -1
  5. data/doc/carrierwave.md +4 -4
  6. data/doc/creating_persistence_plugins.md +172 -0
  7. data/doc/creating_plugins.md +1 -1
  8. data/doc/creating_storages.md +3 -1
  9. data/doc/design.md +2 -2
  10. data/doc/direct_s3.md +0 -22
  11. data/doc/paperclip.md +3 -3
  12. data/doc/plugins/activerecord.md +211 -42
  13. data/doc/plugins/atomic_helpers.md +153 -0
  14. data/doc/plugins/column.md +90 -0
  15. data/doc/plugins/derivation_endpoint.md +54 -62
  16. data/doc/plugins/derivatives.md +752 -0
  17. data/doc/plugins/entity.md +204 -0
  18. data/doc/plugins/infer_extension.md +8 -8
  19. data/doc/plugins/instrumentation.md +33 -13
  20. data/doc/plugins/keep_files.md +5 -15
  21. data/doc/plugins/model.md +157 -0
  22. data/doc/plugins/presign_endpoint.md +2 -1
  23. data/doc/plugins/refresh_metadata.md +44 -7
  24. data/doc/plugins/sequel.md +190 -33
  25. data/doc/plugins/{default_url_options.md → url_options.md} +5 -5
  26. data/doc/processing.md +1 -1
  27. data/doc/release_notes/1.1.0.md +2 -2
  28. data/doc/release_notes/2.15.0.md +1 -1
  29. data/doc/storage/s3.md +2 -2
  30. data/doc/testing.md +1 -1
  31. data/lib/shrine.rb +72 -138
  32. data/lib/shrine/attacher.rb +272 -176
  33. data/lib/shrine/attachment.rb +2 -42
  34. data/lib/shrine/plugins/activerecord.rb +103 -26
  35. data/lib/shrine/plugins/add_metadata.rb +9 -10
  36. data/lib/shrine/plugins/atomic_helpers.rb +111 -0
  37. data/lib/shrine/plugins/attacher_options.rb +55 -0
  38. data/lib/shrine/plugins/backgrounding.rb +147 -115
  39. data/lib/shrine/plugins/cached_attachment_data.rb +6 -9
  40. data/lib/shrine/plugins/column.rb +104 -0
  41. data/lib/shrine/plugins/data_uri.rb +35 -38
  42. data/lib/shrine/plugins/default_storage.rb +18 -12
  43. data/lib/shrine/plugins/default_url.rb +11 -21
  44. data/lib/shrine/plugins/default_url_options.rb +3 -30
  45. data/lib/shrine/plugins/delete_raw.rb +9 -13
  46. data/lib/shrine/plugins/derivation_endpoint.rb +75 -114
  47. data/lib/shrine/plugins/derivatives.rb +576 -0
  48. data/lib/shrine/plugins/determine_mime_type.rb +3 -15
  49. data/lib/shrine/plugins/download_endpoint.rb +83 -131
  50. data/lib/shrine/plugins/dynamic_storage.rb +4 -8
  51. data/lib/shrine/plugins/entity.rb +128 -0
  52. data/lib/shrine/plugins/form_assign.rb +107 -0
  53. data/lib/shrine/plugins/included.rb +4 -3
  54. data/lib/shrine/plugins/infer_extension.rb +10 -17
  55. data/lib/shrine/plugins/instrumentation.rb +45 -25
  56. data/lib/shrine/plugins/keep_files.rb +2 -12
  57. data/lib/shrine/plugins/metadata_attributes.rb +15 -14
  58. data/lib/shrine/plugins/model.rb +137 -0
  59. data/lib/shrine/plugins/module_include.rb +2 -0
  60. data/lib/shrine/plugins/presign_endpoint.rb +1 -15
  61. data/lib/shrine/plugins/pretty_location.rb +5 -5
  62. data/lib/shrine/plugins/processing.rb +21 -6
  63. data/lib/shrine/plugins/rack_file.rb +1 -39
  64. data/lib/shrine/plugins/rack_response.rb +14 -7
  65. data/lib/shrine/plugins/recache.rb +5 -2
  66. data/lib/shrine/plugins/refresh_metadata.rb +12 -8
  67. data/lib/shrine/plugins/remote_url.rb +44 -53
  68. data/lib/shrine/plugins/remove_attachment.rb +7 -2
  69. data/lib/shrine/plugins/remove_invalid.rb +8 -4
  70. data/lib/shrine/plugins/restore_cached_data.rb +12 -4
  71. data/lib/shrine/plugins/sequel.rb +115 -27
  72. data/lib/shrine/plugins/signature.rb +2 -7
  73. data/lib/shrine/plugins/store_dimensions.rb +13 -27
  74. data/lib/shrine/plugins/upload_endpoint.rb +14 -15
  75. data/lib/shrine/plugins/upload_options.rb +9 -8
  76. data/lib/shrine/plugins/url_options.rb +33 -0
  77. data/lib/shrine/plugins/validation.rb +87 -0
  78. data/lib/shrine/plugins/validation_helpers.rb +33 -54
  79. data/lib/shrine/plugins/versions.rb +106 -84
  80. data/lib/shrine/storage/file_system.rb +32 -57
  81. data/lib/shrine/storage/linter.rb +9 -1
  82. data/lib/shrine/storage/memory.rb +42 -0
  83. data/lib/shrine/storage/s3.rb +38 -146
  84. data/lib/shrine/uploaded_file.rb +22 -29
  85. data/lib/shrine/version.rb +4 -4
  86. data/shrine.gemspec +2 -3
  87. metadata +27 -54
  88. data/doc/plugins/backup.md +0 -31
  89. data/doc/plugins/copy.md +0 -24
  90. data/doc/plugins/delete_promoted.md +0 -12
  91. data/doc/plugins/direct_upload.md +0 -172
  92. data/doc/plugins/hooks.md +0 -58
  93. data/doc/plugins/logging.md +0 -42
  94. data/doc/plugins/migration_helpers.md +0 -60
  95. data/doc/plugins/moving.md +0 -19
  96. data/doc/plugins/multi_delete.md +0 -20
  97. data/doc/plugins/parallelize.md +0 -16
  98. data/doc/plugins/parsed_json.md +0 -23
  99. data/lib/shrine/plugins/background_helpers.rb +0 -5
  100. data/lib/shrine/plugins/backup.rb +0 -90
  101. data/lib/shrine/plugins/copy.rb +0 -50
  102. data/lib/shrine/plugins/delete_promoted.rb +0 -20
  103. data/lib/shrine/plugins/direct_upload.rb +0 -217
  104. data/lib/shrine/plugins/hooks.rb +0 -90
  105. data/lib/shrine/plugins/logging.rb +0 -142
  106. data/lib/shrine/plugins/migration_helpers.rb +0 -70
  107. data/lib/shrine/plugins/moving.rb +0 -57
  108. data/lib/shrine/plugins/multi_delete.rb +0 -32
  109. data/lib/shrine/plugins/parallelize.rb +0 -78
  110. data/lib/shrine/plugins/parsed_json.rb +0 -29
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Shrine
4
- module Plugins
5
- # Documentation lives in [doc/plugins/hooks.md] on GitHub.
6
- #
7
- # [doc/plugins/hooks.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/hooks.md
8
- module Hooks
9
- module InstanceMethods
10
- def upload(io, context = {})
11
- result = nil
12
- before_upload(io, context)
13
- around_upload(io, context) { result = super }
14
- after_upload(io, context)
15
- result
16
- end
17
-
18
- def around_upload(*args)
19
- yield
20
- end
21
-
22
- def before_upload(*)
23
- end
24
-
25
- def after_upload(*)
26
- end
27
-
28
-
29
- def processed(io, context)
30
- result = nil
31
- before_process(io, context)
32
- around_process(io, context) { result = super }
33
- after_process(io, context)
34
- result
35
- end
36
- private :processed
37
-
38
- def around_process(*args)
39
- yield
40
- end
41
-
42
- def before_process(*)
43
- end
44
-
45
- def after_process(*)
46
- end
47
-
48
-
49
- def store(io, context = {})
50
- result = nil
51
- before_store(io, context)
52
- around_store(io, context) { result = super }
53
- after_store(io, context)
54
- result
55
- end
56
-
57
- def around_store(*args)
58
- yield
59
- end
60
-
61
- def before_store(*)
62
- end
63
-
64
- def after_store(*)
65
- end
66
-
67
-
68
- def delete(io, context = {})
69
- result = nil
70
- before_delete(io, context)
71
- around_delete(io, context) { result = super }
72
- after_delete(io, context)
73
- result
74
- end
75
-
76
- def around_delete(*args)
77
- yield
78
- end
79
-
80
- def before_delete(*)
81
- end
82
-
83
- def after_delete(*)
84
- end
85
- end
86
- end
87
-
88
- register_plugin(:hooks, Hooks)
89
- end
90
- end
@@ -1,142 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- Shrine.deprecation("The logging plugin has been deprecated in favor of instrumentation plugin. The logging plugin will be removed in Shrine 3.")
4
-
5
- require "logger"
6
- require "json"
7
- require "time"
8
-
9
- class Shrine
10
- module Plugins
11
- # Documentation lives in [doc/plugins/logging.md] on GitHub.
12
- #
13
- # [doc/plugins/logging.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/logging.md
14
- module Logging
15
- def self.load_dependencies(uploader, *)
16
- uploader.plugin :hooks
17
- end
18
-
19
- def self.configure(uploader, opts = {})
20
- uploader.opts[:logging_stream] = opts.fetch(:stream, uploader.opts.fetch(:logging_stream, $stdout))
21
- uploader.opts[:logging_logger] = opts.fetch(:logger, uploader.opts.fetch(:logging_logger, uploader.create_logger))
22
- uploader.opts[:logging_format] = opts.fetch(:format, uploader.opts.fetch(:logging_format, :human))
23
-
24
- Shrine.deprecation("The :heroku logging format has been renamed to :logfmt. Using :heroku name will stop being supported in Shrine 3.") if uploader.opts[:logging_format] == :heroku
25
- end
26
-
27
- module ClassMethods
28
- def logger=(logger)
29
- @logger = logger
30
- end
31
-
32
- def logger
33
- @logger ||= opts[:logging_logger]
34
- end
35
-
36
- def create_logger
37
- logger = Logger.new(opts[:logging_stream])
38
- logger.level = Logger::INFO
39
- logger.level = Logger::WARN if ENV["RACK_ENV"] == "test"
40
- logger.formatter = pretty_formatter
41
- logger
42
- end
43
-
44
- # It makes logging preamble simpler than the default logger. Also, it
45
- # doesn't output timestamps if on Heroku.
46
- def pretty_formatter
47
- proc do |severity, time, program_name, message|
48
- output = "#{Process.pid}: #{message}\n".dup
49
- output.prepend "#{time.utc.iso8601(3)} " unless ENV["DYNO"]
50
- output
51
- end
52
- end
53
- end
54
-
55
- module InstanceMethods
56
- def store(io, context = {})
57
- log("store", io, context) { super }
58
- end
59
-
60
- def delete(io, context = {})
61
- log("delete", io, context) { super }
62
- end
63
-
64
- private
65
-
66
- def processed(io, context = {})
67
- log("process", io, context) { super }
68
- end
69
-
70
- # Collects the data and sends it for logging.
71
- def log(action, input, context)
72
- result, duration = benchmark { yield }
73
-
74
- _log(
75
- action: action,
76
- phase: context[:action],
77
- uploader: self.class.to_s,
78
- attachment: context[:name],
79
- record_class: (context[:record].class.to_s if context[:record]),
80
- record_id: (context[:record].id if context[:record].respond_to?(:id)),
81
- files: (action == "process" ? [count(input), count(result)] : count(result)),
82
- duration: ("%.2f" % duration).to_f,
83
- ) unless result.nil?
84
-
85
- result
86
- end
87
-
88
- # Determines format of logging and calls appropriate method.
89
- def _log(data)
90
- message = send("_log_message_#{opts[:logging_format]}", data)
91
- self.class.logger.info(message)
92
- end
93
-
94
- def _log_message_human(data)
95
- components = []
96
- components << "#{data[:action].upcase}"
97
- components[-1] += "[#{data[:phase]}]" if data[:phase]
98
- components << "#{data[:uploader]}"
99
- components[-1] += "[:#{data[:attachment]}]" if data[:attachment]
100
- components << "#{data[:record_class]}" if data[:record_class]
101
- components[-1] += "[#{data[:record_id]}]" if data[:record_id]
102
- components << "#{Array(data[:files]).join("-")} #{"file#{"s" if Array(data[:files]).any?{|n| n > 1}}"}"
103
- components << "(#{data[:duration]}s)"
104
- components.join(" ")
105
- end
106
-
107
- def _log_message_json(data)
108
- data[:files] = Array(data[:files]).join("-")
109
- JSON.generate(data)
110
- end
111
-
112
- def _log_message_logfmt(data)
113
- data[:files] = Array(data[:files]).join("-")
114
- data.map { |key, value| "#{key}=#{value}" }.join(" ")
115
- end
116
- alias _log_message_heroku _log_message_logfmt # deprecated alias
117
-
118
- # We may have one file, a hash of versions, or an array of files or
119
- # hashes.
120
- def count(object)
121
- case object
122
- when Hash
123
- object.count
124
- when Array
125
- object.inject(0) { |sum, o| sum += count(o) }
126
- else
127
- 1
128
- end
129
- end
130
-
131
- def benchmark
132
- start = Time.now
133
- result = yield
134
- finish = Time.now
135
- [result, finish - start]
136
- end
137
- end
138
- end
139
-
140
- register_plugin(:logging, Logging)
141
- end
142
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- Shrine.deprecation("The migration_helpers plugin is deprecated and will be removed in Shrine 3. Attacher#cached? and Attacher#stored? have been moved to base.")
4
-
5
- class Shrine
6
- module Plugins
7
- # Documentation lives in [doc/plugins/migration_helpers.md] on GitHub.
8
- #
9
- # [doc/plugins/migration_helpers.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/migration_helpers.md
10
- module MigrationHelpers
11
- def self.configure(uploader, delegate: false)
12
- uploader.opts[:migration_helpers_delegate] = delegate
13
- end
14
-
15
- module AttachmentMethods
16
- def initialize(name)
17
- super
18
-
19
- return if shrine_class.opts[:migration_helpers_delegate] == false
20
-
21
- name = attachment_name
22
-
23
- define_method :"update_#{name}" do |&block|
24
- send(:"#{name}_attacher").update_stored(&block)
25
- end
26
-
27
- define_method :"#{name}_cache" do
28
- send(:"#{name}_attacher").cache
29
- end
30
-
31
- define_method :"#{name}_store" do
32
- send(:"#{name}_attacher").store
33
- end
34
-
35
- define_method :"#{name}_cached?" do
36
- send(:"#{name}_attacher").cached?
37
- end
38
-
39
- define_method :"#{name}_stored?" do
40
- send(:"#{name}_attacher").stored?
41
- end
42
- end
43
- end
44
-
45
- module AttacherMethods
46
- # Updates the attachment with the result of the block. It will get
47
- # called only if the attachment exists and is stored.
48
- def update_stored(&block)
49
- return if get.nil? || cache.uploaded?(get)
50
- new_attachment = block.call(get)
51
- swap(new_attachment)
52
- end
53
-
54
- # Returns true if the attachment is present and is uploaded by the
55
- # temporary storage.
56
- def cached?
57
- get && cache.uploaded?(get)
58
- end
59
-
60
- # Returns true if the attachment is present and is uploaded by the
61
- # permanent storage.
62
- def stored?
63
- get && store.uploaded?(get)
64
- end
65
- end
66
- end
67
-
68
- register_plugin(:migration_helpers, MigrationHelpers)
69
- end
70
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- Shrine.deprecation("The moving plugin has been deprecated in favor of the :move upload option for FileSystem storage. It will no longer be available in Shrine 3.")
4
-
5
- class Shrine
6
- module Plugins
7
- # Documentation lives in [doc/plugins/moving.md] on GitHub.
8
- #
9
- # [doc/plugins/moving.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/moving.md
10
- module Moving
11
- def self.configure(uploader, opts = {})
12
- uploader.opts[:moving_storages] = opts.fetch(:storages, uploader.opts[:moving_storages])
13
- end
14
-
15
- module InstanceMethods
16
- private
17
-
18
- # Moves the file if storage supports it, otherwise defaults to copying.
19
- def copy(io, context)
20
- if move?(io, context)
21
- move(io, context)
22
- else
23
- super
24
- end
25
- end
26
-
27
- # Generates upload options and calls `#move` on the storage.
28
- def move(io, context)
29
- location = context[:location]
30
- metadata = context[:metadata]
31
- upload_options = context[:upload_options] || {}
32
-
33
- storage.move(io, location, shrine_metadata: metadata, **upload_options)
34
- end
35
-
36
- # Returns true if file should be moved and is movable.
37
- def move?(io, context)
38
- return false if context[:move] == false
39
- moving_storage? && movable?(io, context)
40
- end
41
-
42
- # Returns true if storage can move this file.
43
- def movable?(io, context)
44
- storage.respond_to?(:move) && storage.movable?(io, context[:location])
45
- end
46
-
47
- # Returns true if file should be moved.
48
- def moving_storage?
49
- opts[:moving_storages].nil? ||
50
- opts[:moving_storages].include?(storage_key)
51
- end
52
- end
53
- end
54
-
55
- register_plugin(:moving, Moving)
56
- end
57
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- Shrine.deprecation("The multi_delete plugin is deprecated and will be removed in Shrine 3.")
4
-
5
- class Shrine
6
- module Plugins
7
- # Documentation lives in [doc/plugins/multi_delete.md] on GitHub.
8
- #
9
- # [doc/plugins/multi_delete.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/multi_delete.md
10
- module MultiDelete
11
- module InstanceMethods
12
- private
13
-
14
- # Adds the ability to upload multiple files, leveraging the underlying
15
- # storage's potential multi delete capability.
16
- def _delete(uploaded_file, context)
17
- if uploaded_file.is_a?(Array)
18
- if storage.respond_to?(:multi_delete)
19
- storage.multi_delete(uploaded_file.map(&:id))
20
- else
21
- uploaded_file.each { |file| _delete(file, context) }
22
- end
23
- else
24
- super
25
- end
26
- end
27
- end
28
- end
29
-
30
- register_plugin(:multi_delete, MultiDelete)
31
- end
32
- end
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "thread"
4
-
5
- class Shrine
6
- module Plugins
7
- # Documentation lives in [doc/plugins/parallelize.md] on GitHub.
8
- #
9
- # [doc/plugins/parallelize.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/parallelize.md
10
- module Parallelize
11
- def self.configure(uploader, opts = {})
12
- uploader.opts[:parallelize_threads] = opts.fetch(:threads, uploader.opts.fetch(:parallelize_threads, 3))
13
- end
14
-
15
- def self.load_dependencies(uploader, opts = {})
16
- uploader.plugin :hooks
17
- end
18
-
19
- module InstanceMethods
20
- def around_store(io, context)
21
- with_pool { |pool| super(io, context.update(thread_pool: pool)) }
22
- end
23
-
24
- def around_delete(uploaded_file, context)
25
- with_pool { |pool| super(uploaded_file, context.update(thread_pool: pool)) }
26
- end
27
-
28
- private
29
-
30
- def put(io, context)
31
- context[:thread_pool].enqueue { super }
32
- end
33
-
34
- def remove(uploaded_file, context)
35
- context[:thread_pool].enqueue { super }
36
- end
37
-
38
- # We initialize a thread pool with configured number of threads.
39
- def with_pool(&block)
40
- pool = ThreadPool.new(opts[:parallelize_threads])
41
- result = yield pool
42
- pool.perform
43
- result
44
- end
45
- end
46
-
47
- class ThreadPool
48
- def initialize(size)
49
- @size = size
50
- @tasks = Queue.new
51
- end
52
-
53
- def enqueue(&task)
54
- @tasks.enq(task)
55
- end
56
-
57
- def perform
58
- @tasks.close
59
- threads = @size.times.map { spawn_thread }
60
- threads.each(&:join)
61
- end
62
-
63
- private
64
-
65
- def spawn_thread
66
- Thread.new do
67
- loop do
68
- task = @tasks.deq or break
69
- task.call
70
- end
71
- end
72
- end
73
- end
74
- end
75
-
76
- register_plugin(:parallelize, Parallelize)
77
- end
78
- end