activestorage_saas 5.2.5.2 → 7.0.4

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 (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