activestorage_saas 5.2.5.1 → 7.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1b5d632c8d8737928c64de3799d5871205fdc50b94306574a85323d47f22eda
4
- data.tar.gz: 8c8b86b0f37605aa95ea80ddf6cb88c44f5ab74c70e81a2e684d25aa3c692754
3
+ metadata.gz: d2582d10000f7aae9780b0d273c172c4a76cb08c97bf092126979ce4110d00d8
4
+ data.tar.gz: 7f88a5f75a7a18bf1587d114e2b87d7d9888afd633d88488868910b495b2c72a
5
5
  SHA512:
6
- metadata.gz: 819a58f6f078ca03aba8736559ac5e04379e54076f80264ad363b1f9007913033797aaff0896b2bbf418e246f13f91092232d13edb836471c25a634736001a33
7
- data.tar.gz: 9bf4a83504cc82ffe698f6f3725eb7c82b642a6d018ba3d14d1269b1f8d20408ea45c93ec3338e326280e491d125e8e739b856fc6c95fb4a513c828d788e719d
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 = 1
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,23 +23,38 @@ 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
 
50
+ def http_method_for_direct_upload
51
+ service.http_method_for_direct_upload if service.respond_to?(:http_method_for_direct_upload)
52
+ end
53
+
54
+ def http_response_type_for_direct_upload
55
+ service.http_response_type_for_direct_upload if service.respond_to?(:http_response_type_for_direct_upload)
56
+ end
57
+
42
58
  def form_data_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
43
59
  if service.respond_to?(:form_data_for_direct_upload)
44
60
  service.form_data_for_direct_upload key,
@@ -57,14 +73,11 @@ module ActiveStorage
57
73
  end
58
74
 
59
75
  def service
60
- @service ||= blob&.tenant_storage_service&.to_service || default_service
76
+ @service ||= (blob && ActiveStorageSaas.service_resolver.call(blob)) || default_service
61
77
  end
62
78
 
63
- private
64
-
65
79
  def default_service
66
- @default_service ||= ActiveStorage::Service.configure @options[:default_service],
67
- Rails.configuration.active_storage.service_configurations
80
+ @default_service ||= ActiveStorage::Blob.services.fetch(@options[:default_service])
68
81
  end
69
82
  end
70
83
  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
@@ -0,0 +1,39 @@
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'
4
+ module ActiveStorageSaas
5
+ class Engine < Rails::Engine
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
27
+ end
28
+
29
+ config.to_prepare do
30
+ ActionDispatch::Routing::Mapper.include Routes
31
+ end
32
+
33
+ initializer 'active_storage_saas.load_mixins' do
34
+ ActiveSupport.on_load(:active_storage_blob) do
35
+ include ActiveStorageSaas::BlobModelMixin
36
+ end
37
+ end
38
+ end
39
+ end
@@ -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.1
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-10 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,22 +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/base_controller.rb
45
- - app/controller/active_storage_saas/blobs_controller.rb
46
- - app/controller/active_storage_saas/direct_uploads_controller.rb
47
- - app/controller/active_storage_saas/disk_controller.rb
48
- - app/controller/active_storage_saas/representations_controller.rb
49
- - db/migrate/001_activestorage_saas_tables.rb
51
+ - config/initializers/active_storage_saas.rb
50
52
  - lib/active_storage/service/saas_service.rb
51
53
  - lib/active_storage_saas.rb
52
- - lib/actives_torage_saas/blob_patch.rb
53
- - lib/actives_torage_saas/engine.rb
54
- - lib/actives_torage_saas/routes.rb
54
+ - lib/active_storage_saas/blob_model_mixin.rb
55
+ - lib/active_storage_saas/direct_uploads_controller_mixin.rb
56
+ - lib/active_storage_saas/engine.rb
57
+ - lib/active_storage_saas/routes.rb
58
+ - lib/active_storage_saas/storage_service_configuration_model_mixin.rb
55
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
56
63
  - sig/activestorage_saas.rbs
57
64
  homepage: https://github.com/xiaohui-zhangxh/activestorage_saas
58
65
  licenses:
@@ -76,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
83
  - !ruby/object:Gem::Version
77
84
  version: '0'
78
85
  requirements: []
79
- rubygems_version: 3.2.3
86
+ rubygems_version: 3.3.7
80
87
  signing_key:
81
88
  specification_version: 4
82
89
  summary: Wraps multi-tenant storage services as ActiveStorage service
@@ -1,7 +0,0 @@
1
- class ActiveStorageSaas::BaseController < ActiveStorage::BaseController
2
- unless method_defined?(:current_tenant)
3
- def current_tenant
4
- raise NotImplementedError
5
- end
6
- end
7
- end
@@ -1,8 +0,0 @@
1
- class ActiveStorageSaas::BlobsController < ActiveStorageSaas::BaseController
2
- include ActiveStorage::SetBlob
3
-
4
- def show
5
- expires_in ActiveStorage::Blob.service.url_expires_in
6
- redirect_to @blob.service_url(disposition: params[:disposition])
7
- end
8
- end
@@ -1,26 +0,0 @@
1
- class ActiveStorageSaas::DirectUploadsController < ActiveStorageSaas::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
- headers: blob.service_headers_for_direct_upload,
23
- formData: blob.service_form_data_for_direct_upload
24
- })
25
- end
26
- end
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Serves files stored with the disk service in the same way that the cloud services do.
4
- # This means using expiring, signed URLs that are meant for immediate access, not permanent linking.
5
- # Always go through the BlobsController, or your own authenticated controller, rather than directly
6
- # to the service url.
7
- class ActiveStorageSaas::DiskController < ActiveStorage::BaseController
8
- skip_forgery_protection
9
-
10
- def show
11
- if key = decode_verified_key
12
- serve_file disk_service.path_for(key[:key]), content_type: key[:content_type], disposition: key[:disposition]
13
- else
14
- head :not_found
15
- end
16
- end
17
-
18
- def update
19
- if token = decode_verified_token
20
- if acceptable_content?(token)
21
- disk_service.upload token[:key], request.body, checksum: token[:checksum]
22
- head :no_content
23
- else
24
- head :unprocessable_entity
25
- end
26
- end
27
- rescue ActiveStorage::IntegrityError
28
- head :unprocessable_entity
29
- end
30
-
31
- private
32
- def disk_service
33
- ActiveStorage::Blob.service
34
- end
35
-
36
-
37
- def decode_verified_key
38
- ActiveStorage.verifier.verified(params[:encoded_key], purpose: :blob_key)
39
- end
40
-
41
- def serve_file(path, content_type:, disposition:)
42
- Rack::File.new(nil).serving(request, path).tap do |(status, headers, body)|
43
- self.status = status
44
- self.response_body = body
45
-
46
- headers.each do |name, value|
47
- response.headers[name] = value
48
- end
49
-
50
- response.headers["Content-Type"] = content_type || DEFAULT_SEND_FILE_TYPE
51
- response.headers["Content-Disposition"] = disposition || DEFAULT_SEND_FILE_DISPOSITION
52
- end
53
- end
54
-
55
-
56
- def decode_verified_token
57
- ActiveStorage.verifier.verified(params[:encoded_token], purpose: :blob_token)
58
- end
59
-
60
- def acceptable_content?(token)
61
- token[:content_type] == request.content_mime_type && token[:content_length] == request.content_length
62
- end
63
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Take a signed permanent reference for a blob representation and turn it into an expiring service URL for download.
4
- # Note: These URLs are publicly accessible. If you need to enforce access protection beyond the
5
- # security-through-obscurity factor of the signed blob and variation reference, you'll need to implement your own
6
- # authenticated redirection controller.
7
- class ActiveStorageSaas::RepresentationsController < ActiveStorage::BaseController
8
- include ActiveStorage::SetBlob
9
-
10
- def show
11
- expires_in ActiveStorage::Blob.service.url_expires_in
12
- redirect_to @blob.representation(params[:variation_key]).processed.service_url(disposition: params[:disposition])
13
- end
14
- 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,41 +0,0 @@
1
- module ActiveStorageSaas
2
- module BlobPatch
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- belongs_to :tenant, class_name: ActiveStorageSaas.tenant_class_name # rubocop: disable Rails/ReflectionClassName
7
- belongs_to :tenant_storage_service, optional: true
8
-
9
- redefine_method :service do
10
- class_service = self.class.service
11
- if class_service.is_a?(ActiveStorage::Service::SaasService)
12
- @service ||= ActiveStorage::Service::SaasService.new(class_service.options.merge(blob: self))
13
- else
14
- class_service
15
- end
16
- end
17
- end
18
-
19
- # support sending data from form instead of headers
20
- def service_form_data_for_direct_upload(expires_in: service.url_expires_in)
21
- return {} unless service.respond_to?(:form_data_for_direct_upload)
22
-
23
- service.form_data_for_direct_upload(key,
24
- expires_in: expires_in,
25
- content_type: content_type,
26
- content_length: byte_size,
27
- checksum: checksum)
28
- end
29
-
30
- private
31
-
32
- def saas_service?
33
- service.is_a?(ActiveStorage::Service::SaasService)
34
- end
35
-
36
- def analyzer_class
37
- analyzers = saas_service? ? service.analyzers : ActiveStorage.analyzers
38
- analyzers.detect { |klass| klass.accept?(self) } || ActiveStorage::Analyzer::NullAnalyzer
39
- end
40
- end
41
- end
@@ -1,18 +0,0 @@
1
- module ActiveStorageSaas
2
- class Engine < Rails::Engine
3
- config.before_configuration do
4
- # disable active_storage routes
5
- ActiveStorage::Engine.config.paths['config/routes.rb'] = []
6
- end
7
-
8
- config.to_prepare do
9
- ActionDispatch::Routing::Mapper.include Routes
10
- end
11
-
12
- initializer 'patch' do
13
- ActiveSupport.on_load(:active_storage_blob) do
14
- include ActiveStorageSaas::BlobPatch
15
- end
16
- end
17
- end
18
- end