kithe 1.1.2 → 2.0.0.pre.alpha1

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.
@@ -27,8 +27,12 @@ describe "Kithe::Asset derivative definitions", queue_adapter: :test do
27
27
  ).tap do |a|
28
28
  # We want to promote without create_derivatives being automatically called
29
29
  # as usual, so we can test create_derivatives manually.
30
- a.file_attacher.set_promotion_directives(skip_callbacks: true)
30
+ a.file_attacher.set_promotion_directives(create_derivatives: false)
31
31
  a.promote
32
+
33
+ # Precondition assumptions for our test setup to be valid
34
+ expect(a.file_attacher.stored?).to be(true)
35
+ expect(a.derivatives).to be_empty
32
36
  end
33
37
  end
34
38
 
@@ -101,7 +105,7 @@ describe "Kithe::Asset derivative definitions", queue_adapter: :test do
101
105
  asset.create_derivatives
102
106
 
103
107
  jpg_deriv = asset.derivatives.find {|d| d.key == "a_jpg"}
104
- expect(jpg_deriv.file.storage_key).to eq("kithe_derivatives")
108
+ expect(jpg_deriv.file.storage_key).to eq(:kithe_derivatives)
105
109
  end
106
110
 
107
111
 
@@ -147,7 +151,7 @@ describe "Kithe::Asset derivative definitions", queue_adapter: :test do
147
151
  deriv = asset.derivatives.first
148
152
 
149
153
  expect(deriv).to be_present
150
- expect(deriv.file.storage_key).to eq("store")
154
+ expect(deriv.file.storage_key).to eq(:store)
151
155
  end
152
156
  end
153
157
 
@@ -15,6 +15,28 @@ describe "Kithe::Asset promotion hooks", queue_adapter: :inline do
15
15
  )
16
16
  }
17
17
 
18
+ describe "before_promotion" do
19
+ temporary_class("TestAsset") do
20
+ Class.new(Kithe::Asset) do
21
+ before_promotion do
22
+ $metadata_in_before_promotion = self.file.metadata
23
+ end
24
+ end
25
+ end
26
+ before do
27
+ $metadata_in_before_promotion = nil
28
+ end
29
+
30
+ # we have a built-in before_promotion for metadata extraction,
31
+ # make sure it happens before any additional before_promotions,
32
+ # so they can eg use it to cancel
33
+ it "has access to automatic metadata extraction" do
34
+ unsaved_asset.save!
35
+ expect($metadata_in_before_promotion).to be_present
36
+ expect($metadata_in_before_promotion['sha512']).to be_present
37
+ end
38
+ end
39
+
18
40
  describe "before_promotion cancellation" do
19
41
  temporary_class("TestAsset") do
20
42
  Class.new(Kithe::Asset) do
@@ -28,23 +50,138 @@ describe "Kithe::Asset promotion hooks", queue_adapter: :inline do
28
50
  end
29
51
  end
30
52
 
31
- it "works" do
32
- expect_any_instance_of(Kithe::AssetUploader::Attacher).not_to receive(:store!)
53
+ describe "with inline promotion", queue_adapter: :test do
54
+ before do
55
+ unsaved_asset.file_attacher.set_promotion_directives(promote: :inline)
56
+ end
33
57
 
34
- unsaved_asset.save!
35
- unsaved_asset.reload
36
- expect(unsaved_asset.stored?).to be(false)
58
+ it "cancels" do
59
+ expect_any_instance_of(Kithe::AssetUploader::Attacher).not_to receive(:promote)
60
+
61
+ unsaved_asset.save!
62
+ unsaved_asset.reload
63
+ expect(unsaved_asset.reload.stored?).to be(false)
64
+ end
65
+
66
+ describe "with promotion_directives[:skip_callbacks]" do
67
+ it "doesn't cancel" do
68
+ expect_any_instance_of(Kithe::AssetUploader::Attacher).to receive(:promote).and_call_original
69
+
70
+ unsaved_asset.file_attacher.set_promotion_directives(skip_callbacks: true)
71
+ unsaved_asset.save!
72
+ unsaved_asset.reload
73
+
74
+ expect(unsaved_asset.stored?).to be(true)
75
+ end
76
+ end
37
77
  end
38
78
 
39
- describe "with promotion_directives[:skip_callbacks]" do
40
- it "doesn't cancel" do
41
- expect_any_instance_of(Kithe::AssetUploader::Attacher).to receive(:store!).and_call_original
79
+ describe "with backgrounding promotion", queue_adapter: :inline do
80
+ it "cancels" do
81
+ expect_any_instance_of(Kithe::AssetUploader::Attacher).not_to receive(:promote)
42
82
 
43
- unsaved_asset.file_attacher.set_promotion_directives(skip_callbacks: true)
44
83
  unsaved_asset.save!
45
84
  unsaved_asset.reload
85
+ expect(unsaved_asset.stored?).to be(false)
86
+ end
87
+
88
+ describe "with promotion_directives[:skip_callbacks]" do
89
+ it "doesn't cancel" do
90
+ expect_any_instance_of(Kithe::AssetUploader::Attacher).to receive(:promote).and_call_original
91
+
92
+ unsaved_asset.file_attacher.set_promotion_directives(skip_callbacks: true)
93
+ unsaved_asset.save!
94
+ unsaved_asset.reload
95
+
96
+ expect(unsaved_asset.stored?).to be(true)
97
+ end
98
+ end
99
+ end
100
+
101
+
102
+ describe "assigning directly to store" do
103
+ temporary_class("TestAsset") do
104
+ Class.new(Kithe::Asset) do
105
+ before_promotion do
106
+ raise "Should not call before_promotion"
107
+ end
108
+
109
+ after_promotion do
110
+ raise "Should not call after_promotion"
111
+ end
112
+ end
113
+ end
114
+
115
+ let(:asset) {
116
+ TestAsset.create(title: "test")
117
+ }
118
+
119
+ let(:filepath) { Kithe::Engine.root.join("spec/test_support/images/1x1_pixel.jpg") }
120
+
121
+ describe "with inline promoting" do
122
+ before do
123
+ asset.file_attacher.set_promotion_directives(promote: :inline)
124
+ end
125
+
126
+ it "should not call callbacks" do
127
+ expect_any_instance_of(Kithe::AssetUploader::Attacher).not_to receive(:promote)
128
+
129
+ asset.file_attacher.attach(File.open(filepath))
130
+ asset.save!
131
+
132
+ expect(asset.changed?).to be(false)
133
+ asset.reload
134
+ expect(asset.file).to be_present
135
+ expect(asset.stored?).to be(true)
136
+ end
137
+ end
138
+
139
+ describe "with background promoting", queue_adapter: :inline do
140
+ before do
141
+ asset.file_attacher.set_promotion_directives(promote: :background)
142
+ end
143
+
144
+ it "should not call callbacks" do
145
+ expect_any_instance_of(Kithe::AssetUploader::Attacher).not_to receive(:promote)
146
+
147
+ asset.file_attacher.attach(File.open(filepath))
148
+ asset.save!
149
+
150
+ expect(asset.changed?).to be(false)
151
+ asset.reload
152
+ expect(asset.file).to be_present
153
+ expect(asset.stored?).to be(true)
154
+ end
155
+ end
156
+ end
157
+
158
+
159
+ describe "calling Asset#promote directly", queue_adapter: :inline do
160
+ before do
161
+ unsaved_asset.file_attacher.set_promotion_directives(promote: false)
162
+ unsaved_asset.save!
163
+ # precondition
164
+ expect(unsaved_asset.reload.file_attacher.cached?).to be(true)
165
+ end
166
+
167
+ it "cancels" do
168
+ expect_any_instance_of(Kithe::AssetUploader::Attacher).not_to receive(:promote)
169
+
170
+ unsaved_asset.promote
171
+ unsaved_asset.reload
172
+ expect(unsaved_asset.stored?).to be(false)
173
+ end
174
+
175
+ describe "with promotion_directives[:skip_callbacks]" do
176
+ it "doesn't cancel" do
177
+ expect_any_instance_of(Kithe::AssetUploader::Attacher).to receive(:promote).and_call_original
46
178
 
47
- expect(unsaved_asset.stored?).to be(true)
179
+ unsaved_asset.file_attacher.set_promotion_directives(skip_callbacks: true)
180
+ unsaved_asset.promote
181
+ unsaved_asset.reload
182
+
183
+ expect(unsaved_asset.stored?).to be(true)
184
+ end
48
185
  end
49
186
  end
50
187
  end
@@ -56,7 +193,7 @@ describe "Kithe::Asset promotion hooks", queue_adapter: :inline do
56
193
  receiver = after_promotion_receiver
57
194
  Class.new(Kithe::Asset) do
58
195
  after_promotion do
59
- receiver.call
196
+ receiver.call(self)
60
197
  end
61
198
  end
62
199
  end
@@ -66,6 +203,29 @@ describe "Kithe::Asset promotion hooks", queue_adapter: :inline do
66
203
  unsaved_asset.save!
67
204
  end
68
205
 
206
+ describe "with inline promotion" do
207
+ before do
208
+ unsaved_asset.file_attacher.set_promotion_directives(promote: :inline)
209
+ end
210
+
211
+ # this is actually what's checking for following example...
212
+ let(:after_promotion_receiver) do
213
+ proc do |asset|
214
+ expect(asset.changed?).to be(false)
215
+
216
+ asset.reload
217
+
218
+ expect(asset.stored?).to be(true)
219
+ expect(asset.sha512).to be_present
220
+ end
221
+ end
222
+
223
+ it "asset has metadata and is finalized" do
224
+ expect(after_promotion_receiver).to receive(:call).and_call_original
225
+ unsaved_asset.save!
226
+ end
227
+ end
228
+
69
229
  describe "with promotion_directives[:skip_callbacks]" do
70
230
  it "doesn't call" do
71
231
  expect(after_promotion_receiver).not_to receive(:call)
@@ -178,7 +338,7 @@ describe "Kithe::Asset promotion hooks", queue_adapter: :inline do
178
338
  let!(:existing_file) { saved_asset.file }
179
339
 
180
340
  it "can cancel deletion" do
181
- expect(Kithe::AssetUploader::Attacher).not_to receive(:delete)
341
+ expect_any_instance_of(Kithe::AssetUploader::Attacher).not_to receive(:destroy)
182
342
 
183
343
 
184
344
  saved_asset.set_promotion_directives(delete: false)
@@ -224,5 +384,12 @@ describe "Kithe::Asset promotion hooks", queue_adapter: :inline do
224
384
  asset.set_promotion_directives(promote: :inline)
225
385
  expect(asset.file_attacher.promotion_directives).to eq("promote" => "inline")
226
386
  end
387
+
388
+ it "setting from instance writer is aggregative" do
389
+ Kithe::Asset.promotion_directives = { promote: :inline }
390
+ asset = Kithe::Asset.new
391
+ asset.set_promotion_directives(create_derivatives: false)
392
+ expect(asset.file_attacher.promotion_directives).to eq("promote" => "inline", "create_derivatives" => "false")
393
+ end
227
394
  end
228
395
  end
@@ -107,7 +107,7 @@ RSpec.describe Kithe::Asset, type: :model do
107
107
  asset.save!
108
108
  asset.reload
109
109
 
110
- expect(asset.file.storage_key).to eq(asset.file_attacher.store.storage_key.to_s)
110
+ expect(asset.file.storage_key).to eq(asset.file_attacher.store.storage_key.to_sym)
111
111
  expect(asset.stored?).to be true
112
112
  expect(asset.file.read).to include("Example Response")
113
113
  expect(asset.file.id).to end_with(".html") # no query params
@@ -120,6 +120,7 @@ RSpec.describe Kithe::Asset, type: :model do
120
120
  asset.file = {"id" => "http://www.example.com/bar.html?foo=bar",
121
121
  "storage" => "remote_url",
122
122
  "headers" => {"Authorization" => "Bearer TestToken"}}
123
+
123
124
  asset.save!
124
125
 
125
126
  expect(
@@ -172,7 +173,7 @@ RSpec.describe Kithe::Asset, type: :model do
172
173
  file: File.open(Kithe::Engine.root.join("spec/test_support/images/1x1_pixel.jpg"))
173
174
  ).tap do |a|
174
175
  a.file_attacher.set_promotion_directives(skip_callbacks: true)
175
- a.promote
176
+ #a.promote
176
177
  a.reload
177
178
  a.update_derivative(:existing, StringIO.new("content"))
178
179
  end
@@ -52,7 +52,7 @@ RSpec.describe Kithe::Derivative, type: :model, queue_adapter: :test do
52
52
  # file is stored
53
53
  expect(derivative.key).to eq(key)
54
54
  expect(derivative.file).to be_present
55
- expect(derivative.file.storage_key).to eq("kithe_derivatives")
55
+ expect(derivative.file.storage_key).to eq(:kithe_derivatives)
56
56
  expect(derivative.file.read).to eq(dummy_content)
57
57
 
58
58
  # some metadata we got
@@ -63,7 +63,7 @@ RSpec.describe Kithe::Derivative, type: :model, queue_adapter: :test do
63
63
  expect(derivative.file.metadata["filename"]).to eq("#{asset.friendlier_id}_some_derivative.jpeg")
64
64
 
65
65
  # path on storage is nice and pretty
66
- expect(derivative.file.id).to match %r{\A#{asset.id}/#{key}/[a-f0-9]+\.jpg\Z}
66
+ expect(derivative.file.id).to match %r{\A#{asset.id}/#{key}/[a-f0-9]+\.jpeg\Z}
67
67
  end
68
68
 
69
69
  it "can add a derivative with custom storage location" do
@@ -72,11 +72,11 @@ RSpec.describe Kithe::Derivative, type: :model, queue_adapter: :test do
72
72
  expect(derivative).to be_present
73
73
  derivative.reload
74
74
  expect(derivative.file).to be_present
75
- expect(derivative.file.storage_key).to eq("store")
75
+ expect(derivative.file.storage_key).to eq(:store)
76
76
  end
77
77
 
78
78
  it "can add a derivative with custom metadata" do
79
- derivative = asset.update_derivative(key, dummy_io, metadata: { foo: "bar" })
79
+ derivative = asset.update_derivative(key, dummy_io, metadata: { "foo" => "bar" })
80
80
  expect(derivative).to be_present
81
81
  expect(derivative.file.metadata["size"]).to eq(dummy_content.length)
82
82
  expect(derivative.file.metadata["foo"]).to eq("bar")
@@ -22,7 +22,7 @@ describe Shrine::Plugins::KitheMultiCache do
22
22
  it "can promote from additional cache" do
23
23
  extra_storage.upload(fakeio("test_content"), "test_id")
24
24
  attacher.assign({"id" => "test_id", "storage" => "additional_one"}.to_json)
25
- attacher.promote(attacher.get)
25
+ attacher.promote
26
26
 
27
27
  uploaded_file = attacher.get
28
28
  expect(uploaded_file).not_to be_nil
data/spec/spec_helper.rb CHANGED
@@ -130,7 +130,7 @@ end
130
130
  # https://github.com/shrinerb/shrine/pull/443
131
131
  #
132
132
  require 'sane_patch'
133
- SanePatch.patch("shrine", "< 3") do
133
+ SanePatch.patch("shrine", "< 3.2.2") do
134
134
  require 'shrine/storage/memory'
135
135
  class Shrine::Storage::Memory
136
136
  def open(id, *)
@@ -3,6 +3,7 @@ module ShrineSpecSupport
3
3
  # https://github.com/shrinerb/shrine/blob/203c2c9a0c83815c9ded1b09d5d006b2a523579c/test/support/generic_helper.rb#L6
4
4
  def test_uploader(storage_key = :store, &block)
5
5
  uploader_class = Class.new(Shrine)
6
+ uploader_class.plugin :model
6
7
  uploader_class.storages[:cache] = Shrine::Storage::Test.new
7
8
  uploader_class.storages[:store] = Shrine::Storage::Test.new
8
9
  uploader_class.class_eval(&block) if block
@@ -13,7 +14,7 @@ module ShrineSpecSupport
13
14
  uploader = test_uploader(*args, &block)
14
15
  Object.send(:remove_const, "TestUser") if defined?(TestUser) # for warnings
15
16
  user_class = Object.const_set("TestUser", Struct.new(:avatar_data, :id))
16
- user_class.include uploader.class::Attachment.new(:avatar, **attachment_options)
17
+ user_class.include uploader.class::Attachment(:avatar, **attachment_options)
17
18
  user_class.new.avatar_attacher
18
19
  end
19
20
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kithe
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 2.0.0.pre.alpha1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Rochkind
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-28 00:00:00.000000000 Z
11
+ date: 2020-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -70,14 +70,14 @@ dependencies:
70
70
  requirements:
71
71
  - - "~>"
72
72
  - !ruby/object:Gem::Version
73
- version: '2.14'
73
+ version: '3.2'
74
74
  type: :runtime
75
75
  prerelease: false
76
76
  version_requirements: !ruby/object:Gem::Requirement
77
77
  requirements:
78
78
  - - "~>"
79
79
  - !ruby/object:Gem::Version
80
- version: '2.14'
80
+ version: '3.2'
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: shrine-url
83
83
  requirement: !ruby/object:Gem::Requirement
@@ -376,7 +376,8 @@ files:
376
376
  - lib/kithe/version.rb
377
377
  - lib/shrine/plugins/kithe_accept_remote_url.rb
378
378
  - lib/shrine/plugins/kithe_multi_cache.rb
379
- - lib/shrine/plugins/kithe_promotion_hooks.rb
379
+ - lib/shrine/plugins/kithe_promotion_callbacks.rb
380
+ - lib/shrine/plugins/kithe_promotion_directives.rb
380
381
  - lib/shrine/plugins/kithe_storage_location.rb
381
382
  - lib/tasks/kithe_tasks.rake
382
383
  - lib/vendor/icc/sRGB2014.icc
@@ -483,12 +484,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
483
484
  requirements:
484
485
  - - ">="
485
486
  - !ruby/object:Gem::Version
486
- version: '0'
487
+ version: '2.5'
487
488
  required_rubygems_version: !ruby/object:Gem::Requirement
488
489
  requirements:
489
- - - ">="
490
+ - - ">"
490
491
  - !ruby/object:Gem::Version
491
- version: '0'
492
+ version: 1.3.1
492
493
  requirements: []
493
494
  rubygems_version: 3.0.3
494
495
  signing_key:
@@ -1,138 +0,0 @@
1
- class Shrine
2
- module Plugins
3
- # This adds some features around shrine promotion that we found useful for dealing
4
- # with backgrounding promotion.
5
- #
6
- # * It will run shrine uploader metadata extraction routines on _any promotion_,
7
- # also adding a `promoting: true` key to the shrine context for that metadata
8
- # extraction. (Using shrine refresh_metadata plugin)
9
- #
10
- # * We separately give our Kithe::Asset model class an activemodel callback
11
- # "promotion" hook. This plugin will call those callbacks around promotion (whether background
12
- # or foreground) -- before, after, or around.
13
- #
14
- # If a callback does a `throw :abort` before promotion, it can cancel promotion. This could
15
- # be used to cancel promotion for a validation failure of some kind -- you'd want to somehow
16
- # store or notify what happened, otherwise to the app and it's users it will just look like
17
- # the thing was never promoted for unknown reasons.
18
- #
19
- # After promotion hooks can be used to hook into things you want to do only after a promotion;
20
- # since promotion is backgrounded it would be otherwise inconvenient to execute something
21
- # only after promotion completes.
22
- #
23
- # The default Kithe::Asset hooks into after_promotion to run derivatives creation
24
- # routines.
25
- #
26
- # * A special :promotion_directives key in the shrine context, which will be serialized
27
- # and restored to be preserved even accross background promotion. It is intended to hold
28
- # a hash of arbitrary key/values. The special key :skip_callbacks, when set to a truthy
29
- # value, will prevent the promotion callbacks discussed above from happening. So if you want
30
- # to save a Kithe::Asset and have promotion happen as usual, but _not_ trigger any callbacks
31
- # (including derivative creation):
32
- #
33
- # some_asset.file = some_assignable_file
34
- # some_asset.file_attacher.set_promotion_directives(skip_callbacks: true)
35
- # some_asset.save!
36
- #
37
- # You can add other arbitrary keys which your own code in an uploader or promotion
38
- # callback may consult, with `set_promotion_directives` as above. To consult, check
39
- # attacher.promotion_directives[:some_key]
40
- #
41
- # You can also set promotion directives globally for Kithe::Asset or a sub-class, in
42
- # a class method. Especially useful for batch processing.
43
- #
44
- # Kithe::Asset.promotion_directives = { promote: :inline, create_derivatives: :inline }
45
- #
46
- class KithePromotionHooks
47
- # whitelist of allowed promotion_directive keys, so we can raise on typos but still
48
- # be extensible. Also serves as some documentation of what directives available.
49
- class_attribute :allowed_promotion_directives,
50
- instance_writer: false,
51
- default: [:promote, :skip_callbacks, :create_derivatives, :delete]
52
-
53
- def self.load_dependencies(uploader, *)
54
- uploader.plugin :refresh_metadata
55
- uploader.plugin :backgrounding
56
- end
57
-
58
- module AttacherClassMethods
59
- # Overridden to restore any serialized promotion_directives to context[:promotion_directives],
60
- # in backgrounding promotion.
61
- def load(data)
62
- super.tap do |attacher|
63
- if data["promotion_directives"]
64
- attacher.context[:promotion_directives] = data["promotion_directives"]
65
- end
66
- end
67
- end
68
- end
69
-
70
- module AttacherMethods
71
-
72
- # Set one or more promotion directives, in context[:promotion_directives], that
73
- # will be serialized and restored to context for bg promotion. The values are intended
74
- # to be simple strings or other json-serializable primitives.
75
- #
76
- # set_promotion_directives will merge it's results into existing promotion directives,
77
- # existing keys will remain. So you can set multiple directives with multiple
78
- # calls to set_promotion_directives, or pass multiple keys to one calls.
79
- #
80
- # @example
81
- # some_model.file_attacher.set_promotion_directives(skip_callbacks: true)
82
- # some_model.save!
83
- def set_promotion_directives(hash)
84
- # ActiveJob sometimes has trouble if there are symbols in there, somewhat
85
- # unpredictably.
86
- hash = hash.collect { |k, v| [k.to_s, v === Symbol ? v.to_s : v.to_s]}.to_h
87
-
88
- unrecognized = hash.keys.collect(&:to_sym) - KithePromotionHooks.allowed_promotion_directives
89
- unless unrecognized.length == 0
90
- raise ArgumentError.new("Unrecognized promotion directive key: #{unrecognized.join('')}")
91
- end
92
-
93
- promotion_directives.merge!(hash)
94
- end
95
-
96
- # context[:promotion_directives], lazily initializing to hash for convenience.
97
- def promotion_directives
98
- context[:promotion_directives] ||= {}
99
- end
100
-
101
- # Overridden so our context[:promotion_directives] is serialized for
102
- # backgrounding.
103
- def dump
104
- super.tap do |hash|
105
- if context[:promotion_directives]
106
- hash["promotion_directives"] = context[:promotion_directives]
107
- end
108
- end
109
- end
110
-
111
- # Overridden to:
112
- # a) refresh metadata as part of promotion (adds `promoting: true` to context for such)
113
- # b) call promotion callbacks on Asset model, unless `promotion_directives["skip_callbacks"]`
114
- # has been set.
115
- def promote(uploaded_file = get, **options)
116
- # insist on a metadata extraction, add a new key `promoting: true` in case
117
- # anyone is interested.
118
-
119
- uploaded_file.refresh_metadata!(**context.merge(options).merge(promoting: true))
120
-
121
- # Now run ordinary promotion with activemodel callbacks from
122
- # the Asset, which will automatically allow them to cancel promotion using
123
- # ordinary activemodel callbacck technique of `throw :abort`.
124
- if ( !promotion_directives["skip_callbacks"] &&
125
- context[:record] &&
126
- context[:record].class.respond_to?(:_promotion_callbacks) )
127
- context[:record].run_callbacks(:promotion) do
128
- super(uploaded_file, **options)
129
- end
130
- else
131
- super(uploaded_file, **options)
132
- end
133
- end
134
- end
135
- end
136
- register_plugin(:kithe_promotion_hooks, KithePromotionHooks)
137
- end
138
- end