activestorage_saas 5.2.5.2 → 7.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -3
  3. data/Gemfile.lock +190 -66
  4. data/Guardfile +42 -0
  5. data/Rakefile +5 -9
  6. data/activestorage_saas.gemspec +2 -5
  7. data/config/initializers/active_storage_saas.rb +6 -0
  8. data/lib/active_storage/service/saas_service.rb +15 -10
  9. data/lib/active_storage_saas/blob_model_mixin.rb +48 -0
  10. data/lib/active_storage_saas/direct_uploads_controller_mixin.rb +31 -0
  11. data/lib/active_storage_saas/engine.rb +26 -5
  12. data/lib/active_storage_saas/routes.rb +3 -3
  13. data/lib/active_storage_saas/storage_service_configuration_model_mixin.rb +47 -0
  14. data/lib/active_storage_saas.rb +4 -2
  15. data/lib/generators/active_storage_saas/install/USAGE +5 -0
  16. data/lib/generators/active_storage_saas/install/install_generator.rb +19 -0
  17. data/lib/generators/active_storage_saas/install/templates/config/initializers/active_storage_saas.rb +6 -0
  18. metadata +20 -16
  19. data/app/controller/active_storage_saas/direct_uploads_controller.rb +0 -28
  20. data/app/javascript/active_storage_saas/direct_upload_controller/blob_record.js +0 -73
  21. data/app/javascript/active_storage_saas/direct_upload_controller/blob_upload.js +0 -45
  22. data/app/javascript/active_storage_saas/direct_upload_controller/direct_upload.js +0 -48
  23. data/app/javascript/active_storage_saas/direct_upload_controller/file_checksum.js +0 -53
  24. data/app/javascript/active_storage_saas/direct_upload_controller/helpers.js +0 -51
  25. data/app/javascript/active_storage_saas/direct_upload_controller.js +0 -78
  26. data/app/models/tenant_storage_service.rb +0 -5
  27. data/db/migrate/001_activestorage_saas_tables.rb +0 -17
  28. data/lib/active_storage_saas/blob_patch.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14d926492586f0ab3058a8ff4a48a7cec2824747366c677f9c91f5c46a658535
4
- data.tar.gz: 1c57c47f19302d6edc4953e6dcf94cd7871f0e22dda6670955c99639f2e3fffb
3
+ metadata.gz: d2582d10000f7aae9780b0d273c172c4a76cb08c97bf092126979ce4110d00d8
4
+ data.tar.gz: 7f88a5f75a7a18bf1587d114e2b87d7d9888afd633d88488868910b495b2c72a
5
5
  SHA512:
6
- metadata.gz: b9101363a32b4d5fbe122d0c7e4953fd10c0a7b2dce02f3d83c4ba42e80745a88d3a50111a4ad100417b6b4e2f3a6244cf5c16f8f07539afbaedea2ea8adde67
7
- data.tar.gz: b5cb1539cbc4129abcee5c5f7b1df6a3b2773a9a87cd4426c223be3bd6ca41ab82632d93deff93b9a59fd4d90a97394c3ae320afc397f4ef0ea16bfd75aa0738
6
+ metadata.gz: f7d76f176c4a9c6ed75544f7008d874363c0cd9b80a372af94d93c8d24800835644fc18ed62e96546e4ea924491b789e8ec07e676e3aaa9bbc1e7b19bff4aad0
7
+ data.tar.gz: 5d9ed9018b5a423dc66ed88bec3b93e34abd12d060c11ca245767a9eb91aa22bf2115bbef8e73e1c5fdedf17a698c139303a641dd1da81ddbc617a30ce7fb998
data/Gemfile CHANGED
@@ -5,8 +5,11 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in activestorage_saas.gemspec
6
6
  gemspec
7
7
 
8
- gem "rake", "~> 13.0"
8
+ gem "rails", "~> 7.0"
9
+ gem "sqlite3"
10
+ gem "propshaft"
11
+ gem "rspec-rails", "~> 5.1"
9
12
 
10
- gem "rspec", "~> 3.0"
13
+ gem "guard-rspec", "~> 4.7"
11
14
 
12
- gem "rubocop", "~> 1.21"
15
+ gem "aws-sdk-s3", "~> 1.114"
data/Gemfile.lock CHANGED
@@ -1,112 +1,236 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activestorage_saas (5.2.5.1)
5
- activestorage (= 5.2.5)
4
+ activestorage_saas (7.0.4)
5
+ activestorage (>= 7.0.3.1, <= 7.0.4)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- actionpack (5.2.5)
11
- actionview (= 5.2.5)
12
- activesupport (= 5.2.5)
13
- rack (~> 2.0, >= 2.0.8)
10
+ actioncable (7.0.4)
11
+ actionpack (= 7.0.4)
12
+ activesupport (= 7.0.4)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (>= 0.6.1)
15
+ actionmailbox (7.0.4)
16
+ actionpack (= 7.0.4)
17
+ activejob (= 7.0.4)
18
+ activerecord (= 7.0.4)
19
+ activestorage (= 7.0.4)
20
+ activesupport (= 7.0.4)
21
+ mail (>= 2.7.1)
22
+ net-imap
23
+ net-pop
24
+ net-smtp
25
+ actionmailer (7.0.4)
26
+ actionpack (= 7.0.4)
27
+ actionview (= 7.0.4)
28
+ activejob (= 7.0.4)
29
+ activesupport (= 7.0.4)
30
+ mail (~> 2.5, >= 2.5.4)
31
+ net-imap
32
+ net-pop
33
+ net-smtp
34
+ rails-dom-testing (~> 2.0)
35
+ actionpack (7.0.4)
36
+ actionview (= 7.0.4)
37
+ activesupport (= 7.0.4)
38
+ rack (~> 2.0, >= 2.2.0)
14
39
  rack-test (>= 0.6.3)
15
40
  rails-dom-testing (~> 2.0)
16
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
17
- actionview (5.2.5)
18
- activesupport (= 5.2.5)
41
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
42
+ actiontext (7.0.4)
43
+ actionpack (= 7.0.4)
44
+ activerecord (= 7.0.4)
45
+ activestorage (= 7.0.4)
46
+ activesupport (= 7.0.4)
47
+ globalid (>= 0.6.0)
48
+ nokogiri (>= 1.8.5)
49
+ actionview (7.0.4)
50
+ activesupport (= 7.0.4)
19
51
  builder (~> 3.1)
20
52
  erubi (~> 1.4)
21
53
  rails-dom-testing (~> 2.0)
22
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
23
- activemodel (5.2.5)
24
- activesupport (= 5.2.5)
25
- activerecord (5.2.5)
26
- activemodel (= 5.2.5)
27
- activesupport (= 5.2.5)
28
- arel (>= 9.0)
29
- activestorage (5.2.5)
30
- actionpack (= 5.2.5)
31
- activerecord (= 5.2.5)
32
- marcel (~> 1.0.0)
33
- activesupport (5.2.5)
54
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
55
+ activejob (7.0.4)
56
+ activesupport (= 7.0.4)
57
+ globalid (>= 0.3.6)
58
+ activemodel (7.0.4)
59
+ activesupport (= 7.0.4)
60
+ activerecord (7.0.4)
61
+ activemodel (= 7.0.4)
62
+ activesupport (= 7.0.4)
63
+ activestorage (7.0.4)
64
+ actionpack (= 7.0.4)
65
+ activejob (= 7.0.4)
66
+ activerecord (= 7.0.4)
67
+ activesupport (= 7.0.4)
68
+ marcel (~> 1.0)
69
+ mini_mime (>= 1.1.0)
70
+ activesupport (7.0.4)
34
71
  concurrent-ruby (~> 1.0, >= 1.0.2)
35
- i18n (>= 0.7, < 2)
36
- minitest (~> 5.1)
37
- tzinfo (~> 1.1)
38
- arel (9.0.0)
39
- ast (2.4.2)
72
+ i18n (>= 1.6, < 2)
73
+ minitest (>= 5.1)
74
+ tzinfo (~> 2.0)
75
+ aws-eventstream (1.2.0)
76
+ aws-partitions (1.664.0)
77
+ aws-sdk-core (3.168.1)
78
+ aws-eventstream (~> 1, >= 1.0.2)
79
+ aws-partitions (~> 1, >= 1.651.0)
80
+ aws-sigv4 (~> 1.5)
81
+ jmespath (~> 1, >= 1.6.1)
82
+ aws-sdk-kms (1.59.0)
83
+ aws-sdk-core (~> 3, >= 3.165.0)
84
+ aws-sigv4 (~> 1.1)
85
+ aws-sdk-s3 (1.117.1)
86
+ aws-sdk-core (~> 3, >= 3.165.0)
87
+ aws-sdk-kms (~> 1)
88
+ aws-sigv4 (~> 1.4)
89
+ aws-sigv4 (1.5.2)
90
+ aws-eventstream (~> 1, >= 1.0.2)
40
91
  builder (3.2.4)
92
+ coderay (1.1.3)
41
93
  concurrent-ruby (1.1.10)
42
94
  crass (1.0.6)
43
95
  diff-lcs (1.5.0)
44
96
  erubi (1.11.0)
97
+ ffi (1.15.5)
98
+ formatador (1.1.0)
99
+ globalid (1.0.0)
100
+ activesupport (>= 5.0)
101
+ guard (2.18.0)
102
+ formatador (>= 0.2.4)
103
+ listen (>= 2.7, < 4.0)
104
+ lumberjack (>= 1.0.12, < 2.0)
105
+ nenv (~> 0.1)
106
+ notiffany (~> 0.0)
107
+ pry (>= 0.13.0)
108
+ shellany (~> 0.0)
109
+ thor (>= 0.18.1)
110
+ guard-compat (1.2.1)
111
+ guard-rspec (4.7.3)
112
+ guard (~> 2.1)
113
+ guard-compat (~> 1.1)
114
+ rspec (>= 2.99.0, < 4.0)
45
115
  i18n (1.12.0)
46
116
  concurrent-ruby (~> 1.0)
47
- loofah (2.18.0)
117
+ jmespath (1.6.1)
118
+ listen (3.7.1)
119
+ rb-fsevent (~> 0.10, >= 0.10.3)
120
+ rb-inotify (~> 0.9, >= 0.9.10)
121
+ loofah (2.19.0)
48
122
  crass (~> 1.0.2)
49
123
  nokogiri (>= 1.5.9)
124
+ lumberjack (1.2.8)
125
+ mail (2.7.1)
126
+ mini_mime (>= 0.1.1)
50
127
  marcel (1.0.2)
128
+ method_source (1.0.0)
129
+ mini_mime (1.1.2)
51
130
  mini_portile2 (2.8.0)
52
- minitest (5.16.2)
53
- nokogiri (1.13.8)
131
+ minitest (5.16.3)
132
+ nenv (0.3.0)
133
+ net-imap (0.3.1)
134
+ net-protocol
135
+ net-pop (0.1.2)
136
+ net-protocol
137
+ net-protocol (0.1.3)
138
+ timeout
139
+ net-smtp (0.3.3)
140
+ net-protocol
141
+ nio4r (2.5.8)
142
+ nokogiri (1.13.9)
54
143
  mini_portile2 (~> 2.8.0)
55
144
  racc (~> 1.4)
56
- parallel (1.22.1)
57
- parser (3.1.1.0)
58
- ast (~> 2.4.1)
145
+ notiffany (0.1.3)
146
+ nenv (~> 0.1)
147
+ shellany (~> 0.0)
148
+ propshaft (0.6.4)
149
+ actionpack (>= 7.0.0)
150
+ activesupport (>= 7.0.0)
151
+ rack
152
+ railties (>= 7.0.0)
153
+ pry (0.14.1)
154
+ coderay (~> 1.1)
155
+ method_source (~> 1.0)
59
156
  racc (1.6.0)
60
157
  rack (2.2.4)
61
158
  rack-test (2.0.2)
62
159
  rack (>= 1.3)
160
+ rails (7.0.4)
161
+ actioncable (= 7.0.4)
162
+ actionmailbox (= 7.0.4)
163
+ actionmailer (= 7.0.4)
164
+ actionpack (= 7.0.4)
165
+ actiontext (= 7.0.4)
166
+ actionview (= 7.0.4)
167
+ activejob (= 7.0.4)
168
+ activemodel (= 7.0.4)
169
+ activerecord (= 7.0.4)
170
+ activestorage (= 7.0.4)
171
+ activesupport (= 7.0.4)
172
+ bundler (>= 1.15.0)
173
+ railties (= 7.0.4)
63
174
  rails-dom-testing (2.0.3)
64
175
  activesupport (>= 4.2.0)
65
176
  nokogiri (>= 1.6)
66
177
  rails-html-sanitizer (1.4.3)
67
178
  loofah (~> 2.3)
68
- rainbow (3.1.1)
179
+ railties (7.0.4)
180
+ actionpack (= 7.0.4)
181
+ activesupport (= 7.0.4)
182
+ method_source
183
+ rake (>= 12.2)
184
+ thor (~> 1.0)
185
+ zeitwerk (~> 2.5)
69
186
  rake (13.0.6)
70
- regexp_parser (2.5.0)
71
- rexml (3.2.5)
72
- rspec (3.11.0)
73
- rspec-core (~> 3.11.0)
74
- rspec-expectations (~> 3.11.0)
75
- rspec-mocks (~> 3.11.0)
76
- rspec-core (3.11.0)
77
- rspec-support (~> 3.11.0)
78
- rspec-expectations (3.11.0)
187
+ rb-fsevent (0.11.2)
188
+ rb-inotify (0.10.1)
189
+ ffi (~> 1.0)
190
+ rspec (3.12.0)
191
+ rspec-core (~> 3.12.0)
192
+ rspec-expectations (~> 3.12.0)
193
+ rspec-mocks (~> 3.12.0)
194
+ rspec-core (3.12.0)
195
+ rspec-support (~> 3.12.0)
196
+ rspec-expectations (3.12.0)
79
197
  diff-lcs (>= 1.2.0, < 2.0)
80
- rspec-support (~> 3.11.0)
81
- rspec-mocks (3.11.1)
198
+ rspec-support (~> 3.12.0)
199
+ rspec-mocks (3.12.0)
82
200
  diff-lcs (>= 1.2.0, < 2.0)
83
- rspec-support (~> 3.11.0)
84
- rspec-support (3.11.0)
85
- rubocop (1.27.0)
86
- parallel (~> 1.10)
87
- parser (>= 3.1.0.0)
88
- rainbow (>= 2.2.2, < 4.0)
89
- regexp_parser (>= 1.8, < 3.0)
90
- rexml
91
- rubocop-ast (>= 1.16.0, < 2.0)
92
- ruby-progressbar (~> 1.7)
93
- unicode-display_width (>= 1.4.0, < 3.0)
94
- rubocop-ast (1.17.0)
95
- parser (>= 3.1.1.0)
96
- ruby-progressbar (1.11.0)
97
- thread_safe (0.3.6)
98
- tzinfo (1.2.10)
99
- thread_safe (~> 0.1)
100
- unicode-display_width (2.1.0)
201
+ rspec-support (~> 3.12.0)
202
+ rspec-rails (5.1.2)
203
+ actionpack (>= 5.2)
204
+ activesupport (>= 5.2)
205
+ railties (>= 5.2)
206
+ rspec-core (~> 3.10)
207
+ rspec-expectations (~> 3.10)
208
+ rspec-mocks (~> 3.10)
209
+ rspec-support (~> 3.10)
210
+ rspec-support (3.12.0)
211
+ shellany (0.0.1)
212
+ sqlite3 (1.5.4)
213
+ mini_portile2 (~> 2.8.0)
214
+ thor (1.2.1)
215
+ timeout (0.3.0)
216
+ tzinfo (2.0.5)
217
+ concurrent-ruby (~> 1.0)
218
+ websocket-driver (0.7.5)
219
+ websocket-extensions (>= 0.1.0)
220
+ websocket-extensions (0.1.5)
221
+ zeitwerk (2.6.6)
101
222
 
102
223
  PLATFORMS
103
224
  ruby
104
225
 
105
226
  DEPENDENCIES
106
227
  activestorage_saas!
107
- rake (~> 13.0)
108
- rspec (~> 3.0)
109
- rubocop (~> 1.21)
228
+ aws-sdk-s3 (~> 1.114)
229
+ guard-rspec (~> 4.7)
230
+ propshaft
231
+ rails (~> 7.0)
232
+ rspec-rails (~> 5.1)
233
+ sqlite3
110
234
 
111
235
  BUNDLED WITH
112
- 2.3.10
236
+ 2.3.17
data/Guardfile ADDED
@@ -0,0 +1,42 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ # Note: The cmd option is now required due to the increasing number of ways
19
+ # rspec may be run, below are examples of the most common uses.
20
+ # * bundler: 'bundle exec rspec'
21
+ # * bundler binstubs: 'bin/rspec'
22
+ # * spring: 'bin/rspec' (This will use spring if running and you have
23
+ # installed the spring binstubs per the docs)
24
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
25
+ # * 'just' rspec: 'rspec'
26
+
27
+ guard :rspec, cmd: "bundle exec rspec" do
28
+ require "guard/rspec/dsl"
29
+ dsl = Guard::RSpec::Dsl.new(self)
30
+
31
+ # Feel free to open issues for suggestions and improvements
32
+
33
+ # RSpec files
34
+ rspec = dsl.rspec
35
+ watch(rspec.spec_helper) { rspec.spec_dir }
36
+ watch(rspec.spec_support) { rspec.spec_dir }
37
+ watch(rspec.spec_files)
38
+
39
+ # Ruby files
40
+ ruby = dsl.ruby
41
+ dsl.watch_spec_files_for(ruby.lib_files)
42
+ end
data/Rakefile CHANGED
@@ -1,12 +1,8 @@
1
- # frozen_string_literal: true
1
+ require "bundler/setup"
2
2
 
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
3
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
7
5
 
8
- require "rubocop/rake_task"
6
+ load "rails/tasks/statistics.rake"
9
7
 
10
- RuboCop::RakeTask.new
11
-
12
- task default: %i[spec rubocop]
8
+ require "bundler/gem_tasks"
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- activestorage_version = '5.2.5'
4
- gem_version = 2
5
-
6
3
  Gem::Specification.new do |spec|
7
4
  spec.name = "activestorage_saas"
8
- spec.version = "#{activestorage_version}.#{gem_version}"
5
+ spec.version = "7.0.4"
9
6
  spec.authors = ["xiaohui"]
10
7
  spec.email = ["xiaohui@tanmer.com"]
11
8
 
@@ -29,5 +26,5 @@ Gem::Specification.new do |spec|
29
26
 
30
27
  spec.require_paths = ["lib"]
31
28
 
32
- spec.add_dependency "activestorage", activestorage_version
29
+ spec.add_dependency "activestorage", '>=7.0.3.1', '<= 7.0.4'
33
30
  end
@@ -0,0 +1,6 @@
1
+ Rails.application.configure do
2
+ # config.active_storage_saas.service_resolver = ->(blob) { StorageServiceConfiguration.from_service_name(blob.service_name)&.to_service }
3
+ # config.active_storage_saas.service_name_converter = ->(controller) { controller.send(:current_tenant).storage_service&.to_service_name }
4
+ # config.active_storage_saas.service_name_validator = ->(service_name) { StorageServiceConfiguration.valid_service_name?(service_name) }
5
+ # config.active_storage_saas.direct_upload_extra_blob_args = ->(controller) { { tenant: controller.send(:current_tenant) } }
6
+ end
@@ -1,3 +1,4 @@
1
+ require 'ruby2_keywords'
1
2
  module ActiveStorage
2
3
  class Service
3
4
  # # config/storage.yml
@@ -22,25 +23,32 @@ module ActiveStorage
22
23
  end
23
24
 
24
25
  Service.public_instance_methods(false).each do |name|
26
+ next if name.in?(%i[name=])
27
+
25
28
  define_method name do |*args, &block|
26
29
  service.public_send(name, *args, &block)
27
30
  end
31
+ ruby2_keywords name
32
+ end
33
+
34
+ def name=(_)
35
+ # do nothing, name should be service.name
28
36
  end
29
37
 
30
38
  redefine_method :url_expires_in do
31
39
  @options.fetch(:url_expires_in, 5.minutes)
32
40
  end
33
41
 
34
- def method_missing(name, *args, &block)
42
+ ruby2_keywords def method_missing(name, *args, &block)
35
43
  service.public_send(name, *args, &block)
36
44
  end
37
45
 
38
- def respond_to_missing?(method)
46
+ def respond_to_missing?(method, _include_all)
39
47
  service.respond_to?(method)
40
48
  end
41
49
 
42
50
  def http_method_for_direct_upload
43
- service.service_http_method_for_direct_upload if service.respond_to?(:http_method_for_direct_upload)
51
+ service.http_method_for_direct_upload if service.respond_to?(:http_method_for_direct_upload)
44
52
  end
45
53
 
46
54
  def http_response_type_for_direct_upload
@@ -65,15 +73,12 @@ module ActiveStorage
65
73
  end
66
74
 
67
75
  def service
68
- @service ||= blob&.tenant_storage_service&.to_service || default_service
76
+ @service ||= (blob && ActiveStorageSaas.service_resolver.call(blob)) || default_service
69
77
  end
70
78
 
71
- private
72
-
73
- def default_service
74
- @default_service ||= ActiveStorage::Service.configure @options[:default_service],
75
- Rails.configuration.active_storage.service_configurations
76
- end
79
+ def default_service
80
+ @default_service ||= ActiveStorage::Blob.services.fetch(@options[:default_service])
81
+ end
77
82
  end
78
83
  end
79
84
  end
@@ -0,0 +1,48 @@
1
+ require 'active_storage/service/saas_service'
2
+
3
+ module ActiveStorageSaas
4
+ module BlobModelMixin
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ prepend InstanceMethods
9
+ end
10
+
11
+ prepended do
12
+ prepend InstanceMethods
13
+ end
14
+
15
+ module InstanceMethods
16
+ extend ActiveSupport::Concern
17
+
18
+ prepended do
19
+ redefine_method :service do
20
+ @service ||= begin
21
+ raise KeyError, "storage service name should not be 'saas'" if service_name.to_s == 'saas'
22
+
23
+ services.fetch(service_name) { |_| ActiveStorageSaas.service_resolver.call(self) } || global_service
24
+ end
25
+ end
26
+
27
+ validate on: :save do
28
+ errors.add(:service_name, :invalid) if service_name.to_s == 'saas'
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def global_service
35
+ self.class.service
36
+ end
37
+
38
+ def analyzer_class
39
+ analyzers = service.class.respond_to?(:analyzers) ? service.class.analyzers : ActiveStorage.analyzers
40
+ analyzers.detect { |klass| klass.accept?(self) } || ActiveStorage::Analyzer::NullAnalyzer
41
+ end
42
+
43
+ def validate_service_name_in_services
44
+ ActiveStorageSaas.service_name_validator.call(service_name) || super
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveStorageSaas
2
+ module DirectUploadsControllerMixin
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ prepend InstanceMethods
7
+ end
8
+
9
+ prepended do
10
+ prepend InstanceMethods
11
+ end
12
+
13
+ module InstanceMethods
14
+ def create
15
+ blob = ActiveStorage::Blob.create!(blob_args)
16
+ render json: direct_upload_json(blob)
17
+ end
18
+
19
+ def callback
20
+ ActiveStorageSaas.direct_upload_callback.call(self)
21
+ end
22
+
23
+ private
24
+
25
+ def blob_args
26
+ super.merge(ActiveStorageSaas.direct_upload_extra_blob_args.call(self))
27
+ .merge(service_name: ActiveStorageSaas.service_name_converter.call(self))
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,17 +1,38 @@
1
+ require 'active_storage_saas/blob_model_mixin'
2
+ require 'active_storage_saas/direct_uploads_controller_mixin'
3
+ require 'active_storage_saas/storage_service_configuration_model_mixin'
1
4
  module ActiveStorageSaas
2
5
  class Engine < Rails::Engine
3
- config.before_configuration do
4
- # disable active_storage routes
5
- ActiveStorage::Engine.config.paths['config/routes.rb'] = []
6
+ config.generators do |g|
7
+ g.test_framework :rspec
8
+ end
9
+ config.active_storage_saas = ActiveSupport::OrderedOptions.new
10
+
11
+ initializer "active_storage_saas.configs" do
12
+ config.after_initialize do |app|
13
+ default_service_resolver = ->(blob) { StorageServiceConfiguration.from_service_name(blob.service_name)&.to_service }
14
+ default_service_name_converter = ->(controller) { StorageServiceConfiguration.first.to_service_name }
15
+ default_service_name_validator = ->(service_name) { StorageServiceConfiguration.valid_service_name?(service_name) }
16
+ default_direct_upload_extra_blob_args = ->(controller) { { } }
17
+ ActiveStorageSaas.service_resolver = app.config.active_storage_saas.service_resolver || default_service_resolver
18
+ ActiveStorageSaas.service_name_converter = app.config.active_storage_saas.service_name_converter || default_service_name_converter
19
+ ActiveStorageSaas.service_name_validator = app.config.active_storage_saas.service_name_validator || default_service_name_validator
20
+ ActiveStorageSaas.direct_upload_extra_blob_args = app.config.active_storage_saas.direct_upload_extra_blob_args || default_direct_upload_extra_blob_args
21
+ ActiveStorage::Blob # call this class to load mixin
22
+ end
23
+
24
+ config.to_prepare do
25
+ ActiveStorage::DirectUploadsController.include ActiveStorageSaas::DirectUploadsControllerMixin
26
+ end
6
27
  end
7
28
 
8
29
  config.to_prepare do
9
30
  ActionDispatch::Routing::Mapper.include Routes
10
31
  end
11
32
 
12
- initializer 'patch' do
33
+ initializer 'active_storage_saas.load_mixins' do
13
34
  ActiveSupport.on_load(:active_storage_blob) do
14
- include ActiveStorageSaas::BlobPatch
35
+ include ActiveStorageSaas::BlobModelMixin
15
36
  end
16
37
  end
17
38
  end
@@ -3,9 +3,9 @@ module ActiveStorageSaas
3
3
  # rubocop: disable Layout/LineLength
4
4
  def draw_active_storage_saas_routes(
5
5
  prefix: '/rails/active_storage',
6
- blobs_controller: 'active_storage/blobs',
7
- representations_controller: 'active_storage/representations',
8
- disk_controller: 'active_storage/disk',
6
+ blobs_controller: 'active_storage_saas/blobs',
7
+ representations_controller: 'active_storage_saas/representations',
8
+ disk_controller: 'active_storage_saas/disk',
9
9
  direct_uploads_controller: 'active_storage_saas/direct_uploads',
10
10
  **option_overrides
11
11
  )
@@ -0,0 +1,47 @@
1
+ module ActiveStorageSaas
2
+ module StorageServiceConfigurationMixin
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ prepend InstanceMethods
7
+ end
8
+
9
+ prepended do
10
+ prepend InstanceMethods
11
+ end
12
+
13
+ module InstanceMethods
14
+ extend ActiveSupport::Concern
15
+
16
+ class_methods do
17
+ def service_name_pattern
18
+ /^#{name}:(\d+)$/
19
+ end
20
+
21
+ def from_service_name(service_name)
22
+ find_by(id: Regexp.last_match(1)) if service_name =~ service_name_pattern
23
+ end
24
+
25
+ def valid_service_name?(service_name)
26
+ service_name_pattern.match?(service_name)
27
+ end
28
+ end
29
+
30
+ def to_service
31
+ defined_service = ActiveStorage::Blob.services.fetch(service_name)
32
+ klass = defined_service.class
33
+ options = ActiveStorage::Blob.services.send(:configurations).fetch(service_name.to_sym)
34
+ options.deep_merge! service_options.deep_symbolize_keys
35
+ klass.new(**options).tap do |instance|
36
+ instance.instance_eval <<~RUBY
37
+ define_singleton_method(:name) { "#{to_service_name}" }
38
+ RUBY
39
+ end
40
+ end
41
+
42
+ def to_service_name
43
+ "#{self.class.name}:#{id}"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -3,9 +3,11 @@
3
3
  module ActiveStorageSaas
4
4
  class Error < StandardError; end
5
5
 
6
- mattr_accessor :tenant_class_name, default: 'Tenant'
6
+ mattr_accessor :service_resolver
7
+ mattr_accessor :service_name_converter
8
+ mattr_accessor :service_name_validator
9
+ mattr_accessor :direct_upload_extra_blob_args
7
10
 
8
11
  require 'active_storage_saas/routes'
9
- require 'active_storage_saas/blob_patch'
10
12
  require 'active_storage_saas/engine'
11
13
  end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Install ActiveStorageSaas
3
+
4
+ Example:
5
+ bin/rails generate active_storage_saas:install
@@ -0,0 +1,19 @@
1
+ module ActiveStorageSaas
2
+ class InstallGenerator < Rails::Generators::Base
3
+ source_root File.expand_path("templates", __dir__)
4
+ class_option :configuration_model, type: :string, default: 'StorageServiceConfiguration'
5
+
6
+ def generate_model
7
+ generate "model", "#{options['configuration_model']} service_name service_options:json"
8
+ inject_into_file "app/models/#{options['configuration_model'].underscore}.rb", after: /^class .+\n/ do <<-'RUBY'
9
+ include ActiveStorageSaas::StorageServiceConfigurationMixin
10
+
11
+ RUBY
12
+ end
13
+ end
14
+
15
+ def copy_files
16
+ template 'config/initializers/active_storage_saas.rb'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ Rails.application.configure do
2
+ # config.active_storage_saas.service_resolver = ->(blob) { StorageServiceConfiguration.from_service_name(blob.service_name)&.to_service }
3
+ # config.active_storage_saas.service_name_converter = ->(controller) { controller.send(:current_tenant).storage_service&.to_service_name }
4
+ # config.active_storage_saas.service_name_validator = ->(service_name) { StorageServiceConfiguration.valid_service_name?(service_name) }
5
+ # config.active_storage_saas.direct_upload_extra_blob_args = ->(controller) { { tenant: controller.send(:current_tenant) } }
6
+ end
metadata CHANGED
@@ -1,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activestorage_saas
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.5.2
4
+ version: 7.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - xiaohui
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-13 00:00:00.000000000 Z
11
+ date: 2022-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activestorage
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 5.2.5
19
+ version: 7.0.3.1
20
+ - - "<="
21
+ - !ruby/object:Gem::Version
22
+ version: 7.0.4
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - '='
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 7.0.3.1
30
+ - - "<="
25
31
  - !ruby/object:Gem::Version
26
- version: 5.2.5
32
+ version: 7.0.4
27
33
  description: Each tenant can set its own storage service, ActiveStorage service can
28
34
  be dynamically loaded.
29
35
  email:
@@ -37,25 +43,23 @@ files:
37
43
  - CHANGELOG.md
38
44
  - Gemfile
39
45
  - Gemfile.lock
46
+ - Guardfile
40
47
  - LICENSE.txt
41
48
  - README.md
42
49
  - Rakefile
43
50
  - activestorage_saas.gemspec
44
- - app/controller/active_storage_saas/direct_uploads_controller.rb
45
- - app/javascript/active_storage_saas/direct_upload_controller.js
46
- - app/javascript/active_storage_saas/direct_upload_controller/blob_record.js
47
- - app/javascript/active_storage_saas/direct_upload_controller/blob_upload.js
48
- - app/javascript/active_storage_saas/direct_upload_controller/direct_upload.js
49
- - app/javascript/active_storage_saas/direct_upload_controller/file_checksum.js
50
- - app/javascript/active_storage_saas/direct_upload_controller/helpers.js
51
- - app/models/tenant_storage_service.rb
52
- - db/migrate/001_activestorage_saas_tables.rb
51
+ - config/initializers/active_storage_saas.rb
53
52
  - lib/active_storage/service/saas_service.rb
54
53
  - lib/active_storage_saas.rb
55
- - lib/active_storage_saas/blob_patch.rb
54
+ - lib/active_storage_saas/blob_model_mixin.rb
55
+ - lib/active_storage_saas/direct_uploads_controller_mixin.rb
56
56
  - lib/active_storage_saas/engine.rb
57
57
  - lib/active_storage_saas/routes.rb
58
+ - lib/active_storage_saas/storage_service_configuration_model_mixin.rb
58
59
  - lib/activestorage_saas.rb
60
+ - lib/generators/active_storage_saas/install/USAGE
61
+ - lib/generators/active_storage_saas/install/install_generator.rb
62
+ - lib/generators/active_storage_saas/install/templates/config/initializers/active_storage_saas.rb
59
63
  - sig/activestorage_saas.rbs
60
64
  homepage: https://github.com/xiaohui-zhangxh/activestorage_saas
61
65
  licenses:
@@ -1,28 +0,0 @@
1
- class ActiveStorageSaas::DirectUploadsController < ActiveStorage::BaseController
2
- def create
3
- blob = ActiveStorage::Blob.create!(blob_args)
4
- render json: direct_upload_json(blob)
5
- end
6
-
7
- def callback
8
- raise NotImplementedError
9
- end
10
-
11
- private
12
-
13
- def blob_args
14
- blob_args = params.require(:blob).permit(:filename, :byte_size, :checksum, :content_type).to_h.symbolize_keys
15
- blob_args.merge! tenant: current_tenant, tenant_storage_service: current_tenant.storage_service
16
- end
17
-
18
- def direct_upload_json(blob)
19
- blob.as_json(root: false, methods: :signed_id, only: :signed_id)
20
- .merge(direct_upload: {
21
- url: blob.service_url_for_direct_upload,
22
- method: blob.service_http_method_for_direct_upload,
23
- responseType: blob.service_http_response_type_for_direct_upload,
24
- headers: blob.service_headers_for_direct_upload,
25
- formData: blob.service_form_data_for_direct_upload.presence
26
- })
27
- end
28
- end
@@ -1,73 +0,0 @@
1
- import { getMetaValue } from "./helpers"
2
-
3
- export class BlobRecord {
4
- constructor(file, checksum, url) {
5
- this.file = file
6
-
7
- this.attributes = {
8
- filename: file.name,
9
- content_type: file.type || "application/octet-stream",
10
- byte_size: file.size,
11
- checksum: checksum
12
- }
13
-
14
- this.xhr = new XMLHttpRequest
15
- this.xhr.open("POST", url, true)
16
- this.xhr.responseType = "json"
17
- this.xhr.setRequestHeader("Content-Type", "application/json")
18
- this.xhr.setRequestHeader("Accept", "application/json")
19
- this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
20
-
21
- const csrfToken = getMetaValue("csrf-token")
22
- if (csrfToken != undefined) {
23
- this.xhr.setRequestHeader("X-CSRF-Token", csrfToken)
24
- }
25
-
26
- this.xhr.addEventListener("load", event => this.requestDidLoad(event))
27
- this.xhr.addEventListener("error", event => this.requestDidError(event))
28
- }
29
-
30
- get status() {
31
- return this.xhr.status
32
- }
33
-
34
- get response() {
35
- const { responseType, response } = this.xhr
36
- if (responseType == "json") {
37
- return response
38
- } else {
39
- // Shim for IE 11: https://connect.microsoft.com/IE/feedback/details/794808
40
- return JSON.parse(response)
41
- }
42
- }
43
-
44
- create(callback) {
45
- this.callback = callback
46
- this.xhr.send(JSON.stringify({ blob: this.attributes }))
47
- }
48
-
49
- requestDidLoad(event) {
50
- if (this.status >= 200 && this.status < 300) {
51
- const { response } = this
52
- const { direct_upload } = response
53
- delete response.direct_upload
54
- this.attributes = response
55
- this.directUploadData = direct_upload
56
- this.callback(null, this.toJSON())
57
- } else {
58
- this.requestDidError(event)
59
- }
60
- }
61
-
62
- requestDidError(event) {
63
- this.callback(event, this)
64
- }
65
-
66
- toJSON() {
67
- const result = {}
68
- for (const key in this.attributes) {
69
- result[key] = this.attributes[key]
70
- }
71
- return result
72
- }
73
- }
@@ -1,45 +0,0 @@
1
- export class BlobUpload {
2
- constructor(blob) {
3
- this.blob = blob
4
- this.file = blob.file
5
-
6
- const { url, headers, method, responseType } = blob.directUploadData
7
-
8
- this.xhr = new XMLHttpRequest
9
- this.xhr.open(method || "PUT", url, true)
10
- this.xhr.responseType = responseType || "text"
11
- for (const key in headers) {
12
- this.xhr.setRequestHeader(key, headers[key])
13
- }
14
- this.xhr.addEventListener("load", event => this.requestDidLoad(event))
15
- this.xhr.addEventListener("error", event => this.requestDidError(event))
16
- }
17
-
18
- create(callback) {
19
- this.callback = callback
20
- if(this.blob.directUploadData.formData){
21
- var formData
22
- formData = new FormData()
23
- for(const key in this.blob.directUploadData.formData){
24
- formData.append(key, this.blob.directUploadData.formData[key])
25
- }
26
- formData.append('file', this.file)
27
- this.xhr.send(formData)
28
- }else{
29
- this.xhr.send(this.file.slice())
30
- }
31
- }
32
-
33
- requestDidLoad(event) {
34
- const { status, response } = this.xhr
35
- if (status >= 200 && status < 300) {
36
- this.callback(null, response)
37
- } else {
38
- this.requestDidError(event)
39
- }
40
- }
41
-
42
- requestDidError(event) {
43
- this.callback(event, this)
44
- }
45
- }
@@ -1,48 +0,0 @@
1
- import { FileChecksum } from "./file_checksum"
2
- import { BlobRecord } from "./blob_record"
3
- import { BlobUpload } from "./blob_upload"
4
-
5
- let id = 0
6
-
7
- export class DirectUpload {
8
- constructor(file, url, delegate) {
9
- this.id = ++id
10
- this.file = file
11
- this.url = url
12
- this.delegate = delegate
13
- }
14
-
15
- create(callback) {
16
- FileChecksum.create(this.file, (error, checksum) => {
17
- if (error) {
18
- callback(error)
19
- return
20
- }
21
-
22
- const blob = new BlobRecord(this.file, checksum, this.url)
23
- notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr)
24
-
25
- blob.create(error => {
26
- if (error) {
27
- callback(error)
28
- } else {
29
- const upload = new BlobUpload(blob)
30
- notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr)
31
- upload.create(error => {
32
- if (error) {
33
- callback(error)
34
- } else {
35
- callback(null, blob.toJSON())
36
- }
37
- })
38
- }
39
- })
40
- })
41
- }
42
- }
43
-
44
- function notify(object, methodName, ...messages) {
45
- if (object && typeof object[methodName] == "function") {
46
- return object[methodName](...messages)
47
- }
48
- }
@@ -1,53 +0,0 @@
1
- import SparkMD5 from "spark-md5"
2
-
3
- const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
4
-
5
- export class FileChecksum {
6
- static create(file, callback) {
7
- const instance = new FileChecksum(file)
8
- instance.create(callback)
9
- }
10
-
11
- constructor(file) {
12
- this.file = file
13
- this.chunkSize = 2097152 // 2MB
14
- this.chunkCount = Math.ceil(this.file.size / this.chunkSize)
15
- this.chunkIndex = 0
16
- }
17
-
18
- create(callback) {
19
- this.callback = callback
20
- this.md5Buffer = new SparkMD5.ArrayBuffer
21
- this.fileReader = new FileReader
22
- this.fileReader.addEventListener("load", event => this.fileReaderDidLoad(event))
23
- this.fileReader.addEventListener("error", event => this.fileReaderDidError(event))
24
- this.readNextChunk()
25
- }
26
-
27
- fileReaderDidLoad(event) {
28
- this.md5Buffer.append(event.target.result)
29
-
30
- if (!this.readNextChunk()) {
31
- const binaryDigest = this.md5Buffer.end(true)
32
- const base64digest = btoa(binaryDigest)
33
- this.callback(null, base64digest)
34
- }
35
- }
36
-
37
- fileReaderDidError(event) {
38
- this.callback(`Error reading ${this.file.name}`)
39
- }
40
-
41
- readNextChunk() {
42
- if (this.chunkIndex < this.chunkCount || (this.chunkIndex == 0 && this.chunkCount == 0)) {
43
- const start = this.chunkIndex * this.chunkSize
44
- const end = Math.min(start + this.chunkSize, this.file.size)
45
- const bytes = fileSlice.call(this.file, start, end)
46
- this.fileReader.readAsArrayBuffer(bytes)
47
- this.chunkIndex++
48
- return true
49
- } else {
50
- return false
51
- }
52
- }
53
- }
@@ -1,51 +0,0 @@
1
- export function getMetaValue(name) {
2
- const element = findElement(document.head, `meta[name="${name}"]`)
3
- if (element) {
4
- return element.getAttribute("content")
5
- }
6
- }
7
-
8
- export function findElements(root, selector) {
9
- if (typeof root == "string") {
10
- selector = root
11
- root = document
12
- }
13
- const elements = root.querySelectorAll(selector)
14
- return toArray(elements)
15
- }
16
-
17
- export function findElement(root, selector) {
18
- if (typeof root == "string") {
19
- selector = root
20
- root = document
21
- }
22
- return root.querySelector(selector)
23
- }
24
-
25
- export function dispatchEvent(element, type, eventInit = {}) {
26
- const { disabled } = element
27
- const { bubbles, cancelable, detail } = eventInit
28
- const event = document.createEvent("Event")
29
-
30
- event.initEvent(type, bubbles || true, cancelable || true)
31
- event.detail = detail || {}
32
-
33
- try {
34
- element.disabled = false
35
- element.dispatchEvent(event)
36
- } finally {
37
- element.disabled = disabled
38
- }
39
-
40
- return event
41
- }
42
-
43
- export function toArray(value) {
44
- if (Array.isArray(value)) {
45
- return value
46
- } else if (Array.from) {
47
- return Array.from(value)
48
- } else {
49
- return [].slice.call(value)
50
- }
51
- }
@@ -1,78 +0,0 @@
1
- import { Controller } from "@hotwired/stimulus";
2
- import { DirectUpload } from "./direct_upload_controller/direct_upload";
3
-
4
- const URL = window.URL || window.webkitURL
5
-
6
- function setupFilePreview(target, file){
7
- const onLoaded = function(){
8
- URL.revokeObjectURL(target.src)
9
- target.removeEventListener('load', onLoaded)
10
- }
11
- target.addEventListener('load', onLoaded)
12
- target.src = URL.createObjectURL(file)
13
- }
14
- export default class extends Controller {
15
- static targets = ['file', 'preview'];
16
- static values = {
17
- url: String,
18
- }
19
-
20
- initialize(){
21
- this.onFileChange = this.onFileChange.bind(this)
22
- }
23
-
24
- fileTargetConnected(target){
25
- if(!target.hiddenInput){
26
- const hiddenInput = document.createElement("input")
27
- hiddenInput.type = "hidden"
28
- hiddenInput.name = target.name
29
- target.insertAdjacentElement("beforebegin", hiddenInput)
30
- target.removeAttribute('name')
31
- target.hiddenInput = hiddenInput
32
- }
33
- target.addEventListener('change', this.onFileChange)
34
- }
35
-
36
- fileTargetDisconnected(target){
37
- target.removeEventListener('change', this.onFileChange)
38
- }
39
-
40
- onFileChange(event){
41
- const { target } = event
42
- const { hiddenInput, files } = target
43
- const file = files[0]
44
- if(!file) return
45
-
46
- const directUpload = new DirectUpload(file, this.urlValue, this)
47
-
48
- if(this.hasPreviewTarget){
49
- setupFilePreview(this.previewTarget, file)
50
- }
51
-
52
- directUpload.create((error, attributes) => {
53
- if(error){
54
- hiddenInput.removeAttribute('value')
55
- }else{
56
- hiddenInput.setAttribute('value', attributes.signed_id)
57
- }
58
- })
59
- }
60
-
61
- directUploadWillCreateBlobWithXHR(xhr) {
62
- this.dispatch("before-blob-request", { detail: xhr })
63
- }
64
-
65
- directUploadWillStoreFileWithXHR(xhr) {
66
- this.dispatch('started', { detail: xhr })
67
- this.dispatch('progress', { detail: { percent: 0 } })
68
- xhr.upload.addEventListener("progress", event => this.uploadRequestDidProgress(event))
69
- }
70
-
71
- uploadRequestDidProgress(event){
72
- const percent = event.loaded / event.total
73
- this.dispatch('progress', { detail: { percent } })
74
- if(percent == 1){
75
- this.dispatch('uploaded')
76
- }
77
- }
78
- }
@@ -1,5 +0,0 @@
1
- class TenantStorageService < ApplicationRecord
2
- belongs_to :tenant, inverse_of: :storage_services
3
-
4
- store :service_options, coder: ActiveRecord::Coders::JSON
5
- end
@@ -1,17 +0,0 @@
1
- class ActivestorageSaasTables < ActiveRecord::Migration[5.2]
2
- def change
3
- tenant_class = ActiveStorageSaas.tenant_class_name.constantize
4
- tenant_primary_key = tenant_class.columns.find{|col| col.name == tenant_class.primary_key }
5
- create_table :tenant_storage_services do |t|
6
- t.references :tenant, type: tenant_primary_key.type, foreign_key: { to_table: tenant_class.table_name }
7
- t.string :service_name
8
- t.json :service_options
9
-
10
- t.timestamps
11
- end
12
-
13
- add_reference tenant_class.table_name.to_sym, :tenant_storage_service, foreign_key: true
14
- add_reference :active_storage_blobs, :tenant, type: tenant_primary_key.type, foreign_key: true
15
- add_reference :active_storage_blobs, :tenant_storage_service, foreign_key: true
16
- end
17
- end
@@ -1,51 +0,0 @@
1
- require 'active_storage/service/saas_service'
2
-
3
- module ActiveStorageSaas
4
- module BlobPatch
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- belongs_to :tenant, class_name: ActiveStorageSaas.tenant_class_name # rubocop: disable Rails/ReflectionClassName
9
- belongs_to :tenant_storage_service, optional: true
10
-
11
- redefine_method :service do
12
- class_service = self.class.service
13
- if class_service.is_a?(ActiveStorage::Service::SaasService)
14
- @service ||= ActiveStorage::Service::SaasService.new(class_service.options.merge(blob: self))
15
- else
16
- class_service
17
- end
18
- end
19
- end
20
-
21
- def service_http_method_for_direct_upload
22
- service.respond_to?(:http_method_for_direct_upload) ? service.http_method_for_direct_upload : nil
23
- end
24
-
25
- def service_http_response_type_for_direct_upload
26
- service.respond_to?(:http_response_type_for_direct_upload) ? service.http_response_type_for_direct_upload : nil
27
- end
28
-
29
- # support sending data from form instead of headers
30
- def service_form_data_for_direct_upload(expires_in: service.url_expires_in)
31
- return {} unless service.respond_to?(:form_data_for_direct_upload)
32
-
33
- service.form_data_for_direct_upload(key,
34
- expires_in: expires_in,
35
- content_type: content_type,
36
- content_length: byte_size,
37
- checksum: checksum)
38
- end
39
-
40
- private
41
-
42
- def saas_service?
43
- service.is_a?(ActiveStorage::Service::SaasService)
44
- end
45
-
46
- def analyzer_class
47
- analyzers = saas_service? ? service.analyzers : ActiveStorage.analyzers
48
- analyzers.detect { |klass| klass.accept?(self) } || ActiveStorage::Analyzer::NullAnalyzer
49
- end
50
- end
51
- end