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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5455342b089699d34e3f5e4386465431a93258ab9fa6610c8ba91b7c2c2f53f4
4
- data.tar.gz: 86790589793eb8d0f6f5f4e781a3f5a161282d6a4f9409936a0ce0b5378ff243
3
+ metadata.gz: 0167f8b1c0fa434977a9fc46cc743c11e67602355eb483cb67c5b47366fc6aa5
4
+ data.tar.gz: fc760095ce36ad3a77a21bc803461589de2407c759bebb1439bfcb395fd0f51e
5
5
  SHA512:
6
- metadata.gz: 2cc59ba9f5dd3510810412b6173b56b6e40f1a43570d1976a07c769c01a5f015d23c779f6ecde67e1d7e8471c4fa39a8dbc848c6ee943a24fc4f88c91d7017c3
7
- data.tar.gz: 792e00cf27b413d645475dd500425b7e77b8ce96e82e32e4b5c474c025916dcc5b5593edf2fe13e1db8656ab429b9a60ed8ce6a1e78608af4392ee232a2423b0
6
+ metadata.gz: 2e990c95440f59b89c93bacc4aa994258b468db54c1a73ce6cc4bbf7ae92c497938cf9874bca6dbe7a8ec6b2280b32f466b1b8a7970bef62e2dd6fb88e53d49c
7
+ data.tar.gz: f8ba1f9b8931a1c1fba1f9a3b59e9747850a201cde129f68af80644d2cefe4e8cea15ee3f6d96fe9eabe361e607872557fe3c24f6162c90d076aa4d3bc9ca5c3
data/README.md CHANGED
@@ -5,15 +5,13 @@
5
5
  [![maintainability](https://api.codeclimate.com/v1/badges/92e1e703c308744a0f66/maintainability)](https://codeclimate.com/github/blocknotes/active_storage_db/maintainability)
6
6
 
7
7
  [![linters](https://github.com/blocknotes/active_storage_db/actions/workflows/linters.yml/badge.svg)](https://github.com/blocknotes/active_storage_db/actions/workflows/linters.yml)
8
- [![specs Postgres](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_postgres_71.yml/badge.svg)](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_postgres_71.yml)
9
- [![specs MySQL](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_mysql_71.yml/badge.svg)](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_mysql_71.yml)
8
+ [![Specs Postgres Rails 8.0](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_postgres_rails80.yml/badge.svg)](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_postgres_rails80.yml)
9
+ [![Specs MySQL Rails 8.0](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_mysql_rails80.yml/badge.svg)](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. Experimental support also for MSSQL.
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
- Main features:
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
- app_ver = ENV.fetch('RAILS', '').tr('.', '')
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 : :unprocessable_entity)
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(:unprocessable_entity)
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 'active_storage/service/db_service_rails60'
4
- require 'active_storage/service/db_service_rails61'
5
- require 'active_storage/service/db_service_rails70'
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('ASDB_CHUNK_SIZE') { 1.megabytes }
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
- ::ActiveStorageDB::File.find_by(ref: key)&.destroy
61
- # Ignore files already deleted
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
- ::ActiveStorageDB::File.where('ref LIKE ?', "#{ApplicationRecord.sanitize_sql_like(prefix)}%").destroy_all
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
- answer = ::ActiveStorageDB::File.where(ref: key).exists?
74
- payload[:exist] = answer
75
- answer
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 : 'db'
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
- { 'Content-Type' => content_type }
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 ||= ActiveStorageDB::File.connection.adapter_name == '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 : 'db'
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
- as_file = fields ? ::ActiveStorageDB::File.select(*fields) : ::ActiveStorageDB::File
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? ? 'DATALENGTH(data)' : 'OCTET_LENGTH(data)'
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorageDB
4
- VERSION = '1.3.1'
4
+ VERSION = '1.5.0'
5
5
  end
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.3.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: 2024-09-07 00:00:00.000000000 Z
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.4.19
116
- signing_key:
85
+ rubygems_version: 3.6.9
117
86
  specification_version: 4
118
87
  summary: ActiveStorage DB Service
119
88
  test_files: []