active_storage_db 1.3.1 → 1.5.0
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.
- checksums.yaml +4 -4
- data/README.md +37 -13
- data/Rakefile +1 -2
- data/app/controllers/active_storage_db/files_controller.rb +6 -2
- data/lib/active_storage/service/db_service.rb +37 -18
- data/lib/active_storage/service/db_service_rails70.rb +3 -2
- data/lib/active_storage_db/version.rb +1 -1
- metadata +3 -34
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0167f8b1c0fa434977a9fc46cc743c11e67602355eb483cb67c5b47366fc6aa5
|
|
4
|
+
data.tar.gz: fc760095ce36ad3a77a21bc803461589de2407c759bebb1439bfcb395fd0f51e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2e990c95440f59b89c93bacc4aa994258b468db54c1a73ce6cc4bbf7ae92c497938cf9874bca6dbe7a8ec6b2280b32f466b1b8a7970bef62e2dd6fb88e53d49c
|
|
7
|
+
data.tar.gz: f8ba1f9b8931a1c1fba1f9a3b59e9747850a201cde129f68af80644d2cefe4e8cea15ee3f6d96fe9eabe361e607872557fe3c24f6162c90d076aa4d3bc9ca5c3
|
data/README.md
CHANGED
|
@@ -5,15 +5,13 @@
|
|
|
5
5
|
[](https://codeclimate.com/github/blocknotes/active_storage_db/maintainability)
|
|
6
6
|
|
|
7
7
|
[](https://github.com/blocknotes/active_storage_db/actions/workflows/linters.yml)
|
|
8
|
-
[](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_postgres_rails80.yml)
|
|
9
|
+
[](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_mysql_rails80.yml)
|
|
10
10
|
|
|
11
|
-
An Active Storage service upload/download plugin that stores files in a PostgreSQL or MySQL database.
|
|
11
|
+
An Active Storage service upload/download plugin that stores files in a PostgreSQL or MySQL database.
|
|
12
|
+
Experimental support also for MSSQL and SQLite.
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
- attachment data stored in a binary field (or blob);
|
|
15
|
-
- all service methods implemented;
|
|
16
|
-
- supports Rails _6_ and _7_.
|
|
14
|
+
Attachment data get stored in a binary field (or blob).
|
|
17
15
|
|
|
18
16
|
Useful also with platforms like Heroku (due to their ephemeral file system).
|
|
19
17
|
|
|
@@ -26,11 +24,36 @@ Useful also with platforms like Heroku (due to their ephemeral file system).
|
|
|
26
24
|
- Change Active Storage service in *config/environments/development.rb* to: `config.active_storage.service = :db`
|
|
27
25
|
- Add to *config/storage.yml*:
|
|
28
26
|
|
|
29
|
-
```
|
|
27
|
+
```yml
|
|
30
28
|
db:
|
|
31
29
|
service: DB
|
|
32
30
|
```
|
|
33
31
|
|
|
32
|
+
### Customizations
|
|
33
|
+
|
|
34
|
+
To setup a separate database connection for the `ActiveStorageDB` migrations and files data:
|
|
35
|
+
|
|
36
|
+
1. Add a different database configuration per environment to `config/database.yml`, e.g:
|
|
37
|
+
|
|
38
|
+
```yml
|
|
39
|
+
attachments:
|
|
40
|
+
database: attachments
|
|
41
|
+
migrations_paths: config/attachments_migrate
|
|
42
|
+
# other connection details ...
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
2. Extend the ActiveStorage base record class providing the `connects_to` options (updating _config/application.rb_ / using an initializer for _ActiveStorageDB_ / overriding the base model like in the [Rails guide](https://guides.rubyonrails.org/engines.html#overriding-models-and-controllers)):
|
|
46
|
+
|
|
47
|
+
```rb
|
|
48
|
+
# e.g. app/overrides/models/active_storage_db/application_record_override.rb
|
|
49
|
+
ActiveStorageDB::ApplicationRecord.class_eval do
|
|
50
|
+
connects_to database: { reading: :attachments, writing: :attachments }
|
|
51
|
+
end
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
3. Move the _ActiveStorageDB_ migrations to the specified migrations path
|
|
55
|
+
4. Execute the _rails db:migrate_ task
|
|
56
|
+
|
|
34
57
|
## Misc
|
|
35
58
|
|
|
36
59
|
Some utility tasks are available:
|
|
@@ -44,17 +67,18 @@ bin/rails 'asdb:search[some_filename]'
|
|
|
44
67
|
bin/rails 'asdb:download[123,/tmp]'
|
|
45
68
|
```
|
|
46
69
|
|
|
70
|
+
## Development
|
|
71
|
+
|
|
72
|
+
Project created by [Mattia Roccoberton](http://blocknot.es), thanks also to the good guys that opened issues and pull requests from time to time.
|
|
73
|
+
|
|
74
|
+
For development information please check [this document](extra/development.md).
|
|
75
|
+
|
|
47
76
|
## Do you like it? Star it!
|
|
48
77
|
|
|
49
78
|
If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
|
|
50
79
|
|
|
51
80
|
Or consider offering me a coffee, it's a small thing but it is greatly appreciated: [about me](https://www.blocknot.es/about-me).
|
|
52
81
|
|
|
53
|
-
## Contributors
|
|
54
|
-
|
|
55
|
-
- [Mattia Roccoberton](https://blocknot.es/): author
|
|
56
|
-
- Inspired by [activestorage-database-service](https://github.com/TitovDigital/activestorage-database-service) project
|
|
57
|
-
|
|
58
82
|
## License
|
|
59
83
|
|
|
60
84
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
|
@@ -16,8 +16,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
|
|
|
16
16
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
APP_RAKEFILE = File.expand_path("spec/dummy#{app_ver}/Rakefile", __dir__)
|
|
19
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
|
21
20
|
load 'rails/tasks/engine.rake'
|
|
22
21
|
|
|
23
22
|
load 'rails/tasks/statistics.rake'
|
|
@@ -17,12 +17,12 @@ module ActiveStorageDB
|
|
|
17
17
|
def update
|
|
18
18
|
if (token = decode_verified_token)
|
|
19
19
|
file_uploaded = upload_file(token, body: request.body)
|
|
20
|
-
head(file_uploaded ? :no_content :
|
|
20
|
+
head(file_uploaded ? :no_content : unprocessable)
|
|
21
21
|
else
|
|
22
22
|
head(:not_found)
|
|
23
23
|
end
|
|
24
24
|
rescue ActiveStorage::IntegrityError
|
|
25
|
-
head(
|
|
25
|
+
head(unprocessable)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
private
|
|
@@ -59,5 +59,9 @@ module ActiveStorageDB
|
|
|
59
59
|
db_service.upload(token[:key], request.body, checksum: token[:checksum])
|
|
60
60
|
true
|
|
61
61
|
end
|
|
62
|
+
|
|
63
|
+
def unprocessable
|
|
64
|
+
Gem::Version.new(Rails.version) >= Gem::Version.new("7.1") ? :unprocessable_content : :unprocessable_entity
|
|
65
|
+
end
|
|
62
66
|
end
|
|
63
67
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
3
|
+
require "active_storage/service/db_service_rails60"
|
|
4
|
+
require "active_storage/service/db_service_rails61"
|
|
5
|
+
require "active_storage/service/db_service_rails70"
|
|
6
6
|
|
|
7
7
|
module ActiveStorage
|
|
8
8
|
# Wraps a DB table as an Active Storage service. See ActiveStorage::Service
|
|
@@ -19,7 +19,7 @@ module ActiveStorage
|
|
|
19
19
|
# :nocov:
|
|
20
20
|
|
|
21
21
|
def initialize(public: false, **)
|
|
22
|
-
@chunk_size = ENV.fetch(
|
|
22
|
+
@chunk_size = ENV.fetch("ASDB_CHUNK_SIZE") { 1.megabytes }
|
|
23
23
|
@public = public
|
|
24
24
|
end
|
|
25
25
|
|
|
@@ -47,7 +47,7 @@ module ActiveStorage
|
|
|
47
47
|
instrument :download_chunk, key: key, range: range do
|
|
48
48
|
from = range.begin + 1
|
|
49
49
|
size = range.size
|
|
50
|
-
args = adapter_sqlserver? ? "data, #{from}, #{size}" : "data FROM #{from} FOR #{size}"
|
|
50
|
+
args = adapter_sqlserver? || adapter_sqlite? ? "data, #{from}, #{size}" : "data FROM #{from} FOR #{size}"
|
|
51
51
|
record = object_for(key, fields: "SUBSTRING(#{args}) AS chunk")
|
|
52
52
|
raise(ActiveStorage::FileNotFoundError) unless record
|
|
53
53
|
|
|
@@ -57,22 +57,28 @@ module ActiveStorage
|
|
|
57
57
|
|
|
58
58
|
def delete(key)
|
|
59
59
|
instrument :delete, key: key do
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
comment = "DBService#delete"
|
|
61
|
+
record = ::ActiveStorageDB::File.annotate(comment).find_by(ref: key)
|
|
62
|
+
record&.destroy
|
|
63
|
+
# NOTE: Ignore files already deleted
|
|
64
|
+
!record.nil?
|
|
62
65
|
end
|
|
63
66
|
end
|
|
64
67
|
|
|
65
68
|
def delete_prefixed(prefix)
|
|
66
69
|
instrument :delete_prefixed, prefix: prefix do
|
|
67
|
-
|
|
70
|
+
comment = "DBService#delete_prefixed"
|
|
71
|
+
sanitized_prefix = "#{ApplicationRecord.sanitize_sql_like(prefix)}%"
|
|
72
|
+
::ActiveStorageDB::File.annotate(comment).where("ref LIKE ?", sanitized_prefix).destroy_all
|
|
68
73
|
end
|
|
69
74
|
end
|
|
70
75
|
|
|
71
76
|
def exist?(key)
|
|
72
77
|
instrument :exist, key: key do |payload|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
comment = "DBService#exist?"
|
|
79
|
+
result = ::ActiveStorageDB::File.annotate(comment).where(ref: key).exists?
|
|
80
|
+
payload[:exist] = result
|
|
81
|
+
result
|
|
76
82
|
end
|
|
77
83
|
end
|
|
78
84
|
|
|
@@ -84,7 +90,7 @@ module ActiveStorage
|
|
|
84
90
|
content_type: content_type,
|
|
85
91
|
content_length: content_length,
|
|
86
92
|
checksum: checksum,
|
|
87
|
-
service_name: respond_to?(:name) ? name :
|
|
93
|
+
service_name: respond_to?(:name) ? name : "db"
|
|
88
94
|
},
|
|
89
95
|
expires_in: expires_in,
|
|
90
96
|
purpose: :blob_token
|
|
@@ -97,13 +103,25 @@ module ActiveStorage
|
|
|
97
103
|
end
|
|
98
104
|
|
|
99
105
|
def headers_for_direct_upload(_key, content_type:, **)
|
|
100
|
-
{
|
|
106
|
+
{ "Content-Type" => content_type }
|
|
101
107
|
end
|
|
102
108
|
|
|
103
109
|
private
|
|
104
110
|
|
|
111
|
+
def adapter_sqlite?
|
|
112
|
+
@adapter_sqlite ||= active_storage_db_adapter_name == "SQLite"
|
|
113
|
+
end
|
|
114
|
+
|
|
105
115
|
def adapter_sqlserver?
|
|
106
|
-
@adapter_sqlserver ||=
|
|
116
|
+
@adapter_sqlserver ||= active_storage_db_adapter_name == "SQLServer"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def active_storage_db_adapter_name
|
|
120
|
+
if ActiveStorageDB::File.respond_to?(:lease_connection)
|
|
121
|
+
ActiveStorageDB::File.lease_connection.adapter_name
|
|
122
|
+
else
|
|
123
|
+
ActiveStorageDB::File.connection.adapter_name
|
|
124
|
+
end
|
|
107
125
|
end
|
|
108
126
|
|
|
109
127
|
def generate_url(key, expires_in:, filename:, content_type:, disposition:)
|
|
@@ -113,7 +131,7 @@ module ActiveStorage
|
|
|
113
131
|
key: key,
|
|
114
132
|
disposition: content_disposition,
|
|
115
133
|
content_type: content_type,
|
|
116
|
-
service_name: respond_to?(:name) ? name :
|
|
134
|
+
service_name: respond_to?(:name) ? name : "db"
|
|
117
135
|
},
|
|
118
136
|
expires_in: expires_in,
|
|
119
137
|
purpose: :blob_key
|
|
@@ -146,15 +164,16 @@ module ActiveStorage
|
|
|
146
164
|
end
|
|
147
165
|
|
|
148
166
|
def object_for(key, fields: nil)
|
|
149
|
-
|
|
167
|
+
comment = "DBService#object_for"
|
|
168
|
+
as_file = fields ? ::ActiveStorageDB::File.annotate(comment).select(*fields) : ::ActiveStorageDB::File
|
|
150
169
|
as_file.find_by(ref: key)
|
|
151
170
|
end
|
|
152
171
|
|
|
153
172
|
def stream(key)
|
|
154
|
-
data_size = adapter_sqlserver? ?
|
|
173
|
+
data_size = adapter_sqlserver? ? "DATALENGTH(data)" : "OCTET_LENGTH(data)"
|
|
155
174
|
size = object_for(key, fields: "#{data_size} AS size")&.size || raise(ActiveStorage::FileNotFoundError)
|
|
156
175
|
(size / @chunk_size.to_f).ceil.times.each do |i|
|
|
157
|
-
range = (i * @chunk_size..((i + 1) * @chunk_size) - 1)
|
|
176
|
+
range = (i * @chunk_size)..(((i + 1) * @chunk_size) - 1)
|
|
158
177
|
yield download_chunk(key, range)
|
|
159
178
|
end
|
|
160
179
|
end
|
|
@@ -4,12 +4,13 @@ module ActiveStorage
|
|
|
4
4
|
module DBServiceRails70
|
|
5
5
|
def compose(source_keys, destination_key, **)
|
|
6
6
|
buffer = nil
|
|
7
|
+
comment = "DBService#compose"
|
|
7
8
|
source_keys.each do |source_key|
|
|
8
|
-
data = ::ActiveStorageDB::File.find_by!(ref: source_key).data
|
|
9
|
+
data = ::ActiveStorageDB::File.annotate(comment).find_by!(ref: source_key).data
|
|
9
10
|
if buffer
|
|
10
11
|
buffer << data
|
|
11
12
|
else
|
|
12
|
-
buffer = data
|
|
13
|
+
buffer = +data
|
|
13
14
|
end
|
|
14
15
|
end
|
|
15
16
|
::ActiveStorageDB::File.create!(ref: destination_key, data: buffer) if buffer
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_storage_db
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mattia Roccoberton
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: activestorage
|
|
@@ -38,34 +37,6 @@ dependencies:
|
|
|
38
37
|
- - ">="
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
39
|
version: '6.0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: appraisal
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - "~>"
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '2.4'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - "~>"
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '2.4'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: factory_bot_rails
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - "~>"
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '6.1'
|
|
62
|
-
type: :development
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - "~>"
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '6.1'
|
|
69
40
|
description: An ActiveStorage service plugin to store files in database.
|
|
70
41
|
email:
|
|
71
42
|
- mat@blocknot.es
|
|
@@ -97,7 +68,6 @@ metadata:
|
|
|
97
68
|
homepage_uri: https://github.com/blocknotes/active_storage_db
|
|
98
69
|
source_code_uri: https://github.com/blocknotes/active_storage_db
|
|
99
70
|
rubygems_mfa_required: 'true'
|
|
100
|
-
post_install_message:
|
|
101
71
|
rdoc_options: []
|
|
102
72
|
require_paths:
|
|
103
73
|
- lib
|
|
@@ -112,8 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
112
82
|
- !ruby/object:Gem::Version
|
|
113
83
|
version: '0'
|
|
114
84
|
requirements: []
|
|
115
|
-
rubygems_version: 3.
|
|
116
|
-
signing_key:
|
|
85
|
+
rubygems_version: 3.6.9
|
|
117
86
|
specification_version: 4
|
|
118
87
|
summary: ActiveStorage DB Service
|
|
119
88
|
test_files: []
|