active_storage_db 1.4.0 → 1.6.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: cc9d5bd1bf2f6b199a9632ad1f7927074de7613d9170c318f82a6c79b270c862
4
- data.tar.gz: fa1e168bff6d46c650c71ea64316c7c31e64c9a3061746f50004c6738b3eda7b
3
+ metadata.gz: ef4de196ae1110b81dfa6adfdd641e82bd009d3d1ed7fd0fbdd497d66cee089f
4
+ data.tar.gz: f02ae2f4d33d4f054814b0c1fa178af09ec2f6084beb02448af43cf16b45db40
5
5
  SHA512:
6
- metadata.gz: 25faf4f5a9ca098017364f5f73b2a1208972382e08bce0e3ea578f80e922526642d385d6e6deb6e5902262deda0508afbf95ef6405a6b4cedc2d8801979426a2
7
- data.tar.gz: 582eda7e799f912d955f18d99213ed9dc7d80a060329732817e6696812562e0c2e103b1624bddbf6b967d7b66252de4c23b4423a85fdcfea50cdcc963f010664
6
+ metadata.gz: 331e0ccf899fba5d8ed88eacfd95612b074fb403696092b0744a81641dcea1d83f7c723dd430281e3a91224a01657f132ce5cdf9eb06fc04b4e9b6ac98ba6c5d
7
+ data.tar.gz: 4e657d03432899f4c18bac98201c639c0473150fedd0f83bbfe1ae674000e3765aca3111d823aaf46178a9e4195c44c9ca28297256b1e573bcbe3747cda16f72
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.1](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_postgres_rails81.yml/badge.svg)](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_postgres_rails81.yml)
9
+ [![Specs MySQL Rails 8.1](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_mysql_rails81.yml/badge.svg)](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_mysql_rails81.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 and SQLite.
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
 
@@ -23,8 +21,8 @@ Useful also with platforms like Heroku (due to their ephemeral file system).
23
21
  - Add to your Gemfile `gem 'active_storage_db'` (and execute: `bundle`)
24
22
  - Install the gem migrations: `bin/rails active_storage_db:install:migrations` (and execute: `bin/rails db:migrate`)
25
23
  - Add to your `config/routes.rb`: `mount ActiveStorageDB::Engine => '/active_storage_db'`
26
- - Change Active Storage service in *config/environments/development.rb* to: `config.active_storage.service = :db`
27
- - Add to *config/storage.yml*:
24
+ - Change Active Storage service in _config/environments/development.rb_ to: `config.active_storage.service = :db`
25
+ - Add to _config/storage.yml_:
28
26
 
29
27
  ```yml
30
28
  db:
@@ -69,17 +67,18 @@ bin/rails 'asdb:search[some_filename]'
69
67
  bin/rails 'asdb:download[123,/tmp]'
70
68
  ```
71
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
+
72
76
  ## Do you like it? Star it!
73
77
 
74
78
  If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
75
79
 
76
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).
77
81
 
78
- ## Development
79
-
80
- - Author: [Mattia Roccoberton](https://blocknot.es/)
81
- - Inspired by [activestorage-database-service](https://github.com/TitovDigital/activestorage-database-service) project
82
-
83
82
  ## License
84
83
 
85
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'
@@ -16,19 +16,20 @@ module ActiveStorageDB
16
16
 
17
17
  def update
18
18
  if (token = decode_verified_token)
19
- file_uploaded = upload_file(token, body: request.body)
20
- head(file_uploaded ? :no_content : :unprocessable_entity)
19
+ file_uploaded = upload_file(token)
20
+ head(file_uploaded ? :no_content : ActiveStorageDB::UNPROCESSABLE_STATUS)
21
21
  else
22
22
  head(:not_found)
23
23
  end
24
24
  rescue ActiveStorage::IntegrityError
25
- head(:unprocessable_entity)
25
+ head(ActiveStorageDB::UNPROCESSABLE_STATUS)
26
26
  end
27
27
 
28
28
  private
29
29
 
30
30
  def acceptable_content?(token)
31
- token[:content_type] == request.content_mime_type && token[:content_length] == request.content_length
31
+ token[:content_type] == request.media_type &&
32
+ token[:content_length] == request.content_length
32
33
  end
33
34
 
34
35
  def db_service
@@ -53,7 +54,7 @@ module ActiveStorageDB
53
54
  send_data(db_service.download(key), options)
54
55
  end
55
56
 
56
- def upload_file(token, body:)
57
+ def upload_file(token) # rubocop:disable Naming/PredicateMethod
57
58
  return false unless acceptable_content?(token)
58
59
 
59
60
  db_service.upload(token[:key], request.body, checksum: token[:checksum])
@@ -4,7 +4,7 @@ module ActiveStorageDB
4
4
  class File < ApplicationRecord
5
5
  validates :ref,
6
6
  presence: true,
7
- allow_blank: false,
8
7
  uniqueness: { case_sensitive: false }
8
+ validates :data, presence: true
9
9
  end
10
10
  end
@@ -20,7 +20,6 @@ class CreateActiveStorageDBFiles < ActiveRecord::Migration[6.0]
20
20
 
21
21
  def primary_key_type
22
22
  config = Rails.configuration.generators
23
- primary_key_type = config.options[config.orm][:primary_key_type]
24
- primary_key_type || :primary_key
23
+ config.options.dig(config.orm, :primary_key_type) || :primary_key
25
24
  end
26
25
  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
@@ -18,16 +18,26 @@ module ActiveStorage
18
18
  end
19
19
  # :nocov:
20
20
 
21
+ MINIMUM_CHUNK_SIZE = 1
22
+
21
23
  def initialize(public: false, **)
22
- @chunk_size = ENV.fetch('ASDB_CHUNK_SIZE') { 1.megabytes }
24
+ @chunk_size = [ENV.fetch("ASDB_CHUNK_SIZE") { 1.megabytes }.to_i, MINIMUM_CHUNK_SIZE].max
25
+ @max_size = ENV.fetch("ASDB_MAX_FILE_SIZE", nil)&.to_i
23
26
  @public = public
24
27
  end
25
28
 
26
29
  def upload(key, io, checksum: nil, **)
27
30
  instrument :upload, key: key, checksum: checksum do
28
- file = ::ActiveStorageDB::File.create!(ref: key, data: io.read)
29
- ensure_integrity_of(key, checksum) if checksum
30
- file
31
+ data = io.read
32
+ if @max_size && data.bytesize > @max_size
33
+ raise ArgumentError, "File size exceeds the maximum allowed size of #{@max_size} bytes"
34
+ end
35
+
36
+ if checksum
37
+ digest = Digest::MD5.base64digest(data)
38
+ raise ActiveStorage::IntegrityError unless digest == checksum
39
+ end
40
+ ::ActiveStorageDB::File.create!(ref: key, data: data)
31
41
  end
32
42
  end
33
43
 
@@ -45,11 +55,13 @@ module ActiveStorage
45
55
 
46
56
  def download_chunk(key, range)
47
57
  instrument :download_chunk, key: key, range: range do
58
+ # NOTE: from/size are derived from Range#begin and Range#size (always integers),
59
+ # so string interpolation into SQL is safe here.
48
60
  from = range.begin + 1
49
61
  size = range.size
50
62
  args = adapter_sqlserver? || adapter_sqlite? ? "data, #{from}, #{size}" : "data FROM #{from} FOR #{size}"
51
63
  record = object_for(key, fields: "SUBSTRING(#{args}) AS chunk")
52
- raise(ActiveStorage::FileNotFoundError) unless record
64
+ raise ActiveStorage::FileNotFoundError unless record
53
65
 
54
66
  record.chunk
55
67
  end
@@ -57,22 +69,28 @@ module ActiveStorage
57
69
 
58
70
  def delete(key)
59
71
  instrument :delete, key: key do
60
- ::ActiveStorageDB::File.find_by(ref: key)&.destroy
61
- # Ignore files already deleted
72
+ comment = "DBService#delete"
73
+ record = ::ActiveStorageDB::File.annotate(comment).find_by(ref: key)
74
+ record&.destroy
75
+ # NOTE: Ignore files already deleted
76
+ !record.nil?
62
77
  end
63
78
  end
64
79
 
65
80
  def delete_prefixed(prefix)
66
81
  instrument :delete_prefixed, prefix: prefix do
67
- ::ActiveStorageDB::File.where('ref LIKE ?', "#{ApplicationRecord.sanitize_sql_like(prefix)}%").destroy_all
82
+ comment = "DBService#delete_prefixed"
83
+ sanitized_prefix = "#{ActiveRecord::Base.sanitize_sql_like(prefix)}%"
84
+ ::ActiveStorageDB::File.annotate(comment).where("ref LIKE ?", sanitized_prefix).destroy_all
68
85
  end
69
86
  end
70
87
 
71
88
  def exist?(key)
72
89
  instrument :exist, key: key do |payload|
73
- answer = ::ActiveStorageDB::File.where(ref: key).exists?
74
- payload[:exist] = answer
75
- answer
90
+ comment = "DBService#exist?"
91
+ result = ::ActiveStorageDB::File.annotate(comment).exists?(ref: key)
92
+ payload[:exist] = result
93
+ result
76
94
  end
77
95
  end
78
96
 
@@ -84,7 +102,7 @@ module ActiveStorage
84
102
  content_type: content_type,
85
103
  content_length: content_length,
86
104
  checksum: checksum,
87
- service_name: respond_to?(:name) ? name : 'db'
105
+ service_name: service_name_for_token
88
106
  },
89
107
  expires_in: expires_in,
90
108
  purpose: :blob_token
@@ -97,17 +115,33 @@ module ActiveStorage
97
115
  end
98
116
 
99
117
  def headers_for_direct_upload(_key, content_type:, **)
100
- { 'Content-Type' => content_type }
118
+ { "Content-Type" => content_type }
101
119
  end
102
120
 
103
121
  private
104
122
 
123
+ def service_name_for_token
124
+ name.presence || "db"
125
+ end
126
+
105
127
  def adapter_sqlite?
106
- @adapter_sqlite ||= ActiveStorageDB::File.connection.adapter_name == 'SQLite'
128
+ return @adapter_sqlite if defined?(@adapter_sqlite)
129
+
130
+ @adapter_sqlite = active_storage_db_adapter_name == "SQLite"
107
131
  end
108
132
 
109
133
  def adapter_sqlserver?
110
- @adapter_sqlserver ||= ActiveStorageDB::File.connection.adapter_name == 'SQLServer'
134
+ return @adapter_sqlserver if defined?(@adapter_sqlserver)
135
+
136
+ @adapter_sqlserver = active_storage_db_adapter_name == "SQLServer"
137
+ end
138
+
139
+ def active_storage_db_adapter_name
140
+ if ActiveStorageDB::File.respond_to?(:lease_connection)
141
+ ActiveStorageDB::File.lease_connection.adapter_name
142
+ else
143
+ ActiveStorageDB::File.connection.adapter_name
144
+ end
111
145
  end
112
146
 
113
147
  def generate_url(key, expires_in:, filename:, content_type:, disposition:)
@@ -117,7 +151,7 @@ module ActiveStorage
117
151
  key: key,
118
152
  disposition: content_disposition,
119
153
  content_type: content_type,
120
- service_name: respond_to?(:name) ? name : 'db'
154
+ service_name: service_name_for_token
121
155
  },
122
156
  expires_in: expires_in,
123
157
  purpose: :blob_key
@@ -135,34 +169,38 @@ module ActiveStorage
135
169
  )
136
170
  end
137
171
 
138
- def ensure_integrity_of(key, checksum)
139
- return if Digest::MD5.base64digest(object_for(key).data) == checksum
140
-
141
- delete(key)
142
- raise ActiveStorage::IntegrityError
143
- end
144
-
145
172
  def retrieve_file(key)
146
173
  file = object_for(key)
147
- raise(ActiveStorage::FileNotFoundError) unless file
174
+ raise ActiveStorage::FileNotFoundError unless file
148
175
 
149
176
  file.data
150
177
  end
151
178
 
152
179
  def object_for(key, fields: nil)
153
- as_file = fields ? ::ActiveStorageDB::File.select(*fields) : ::ActiveStorageDB::File
154
- as_file.find_by(ref: key)
180
+ comment = "DBService#object_for"
181
+ scope = ::ActiveStorageDB::File.annotate(comment)
182
+ scope = scope.select(fields) if fields
183
+ scope.find_by(ref: key)
155
184
  end
156
185
 
157
186
  def stream(key)
158
- data_size = adapter_sqlserver? ? 'DATALENGTH(data)' : 'OCTET_LENGTH(data)'
159
- size = object_for(key, fields: "#{data_size} AS size")&.size || raise(ActiveStorage::FileNotFoundError)
187
+ size = object_for(key, fields: data_size)&.size || raise(ActiveStorage::FileNotFoundError)
160
188
  (size / @chunk_size.to_f).ceil.times.each do |i|
161
- range = (i * @chunk_size..((i + 1) * @chunk_size) - 1)
189
+ range = (i * @chunk_size)..(((i + 1) * @chunk_size) - 1)
162
190
  yield download_chunk(key, range)
163
191
  end
164
192
  end
165
193
 
194
+ def data_size
195
+ if adapter_sqlserver?
196
+ "DATALENGTH(data) AS size"
197
+ elsif adapter_sqlite?
198
+ "LENGTH(data) AS size"
199
+ else
200
+ "OCTET_LENGTH(data) AS size"
201
+ end
202
+ end
203
+
166
204
  def url_helpers
167
205
  @url_helpers ||= ::ActiveStorageDB::Engine.routes.url_helpers
168
206
  end
@@ -4,12 +4,15 @@ 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
+ record = ::ActiveStorageDB::File.annotate(comment).select(:data).find_by(ref: source_key)
10
+ raise ActiveStorage::FileNotFoundError unless record
11
+
9
12
  if buffer
10
- buffer << data
13
+ buffer << record.data
11
14
  else
12
- buffer = +data
15
+ buffer = +record.data
13
16
  end
14
17
  end
15
18
  ::ActiveStorageDB::File.create!(ref: destination_key, data: buffer) if buffer
@@ -1,6 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorageDB
4
+ # :nocov:
5
+ UNPROCESSABLE_STATUS = if Rails::VERSION::MAJOR > 7 || (Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR >= 1)
6
+ :unprocessable_content
7
+ else
8
+ :unprocessable_entity
9
+ end
10
+ # :nocov:
11
+
4
12
  class Engine < ::Rails::Engine
5
13
  isolate_namespace ActiveStorageDB
6
14
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorageDB
4
- VERSION = '1.4.0'
4
+ VERSION = '1.6.0'
5
5
  end
@@ -5,22 +5,38 @@ module ActiveStorage
5
5
  module_function
6
6
 
7
7
  def print_blob_header(digits: 0)
8
- puts ['Size'.rjust(8), 'Date'.rjust(18), 'Id'.rjust(digits + 2), ' Filename'].join
8
+ puts ["Size".rjust(8), "Date".rjust(18), "Id".rjust(digits + 2), " Filename"].join
9
9
  end
10
10
 
11
11
  def print_blob(blob, digits: 0)
12
- size = (blob.byte_size / 1024).to_s.rjust(7)
13
- date = blob.created_at.strftime('%Y-%m-%d %H:%M')
14
- puts "#{size}K #{date} #{blob.id.to_s.rjust(digits)} #{blob.filename}"
12
+ size = format_size(blob.byte_size)
13
+ date = blob.created_at.strftime("%Y-%m-%d %H:%M")
14
+ puts "#{size} #{date} #{blob.id.to_s.rjust(digits)} #{blob.filename}"
15
+ end
16
+
17
+ def format_size(bytes)
18
+ if bytes >= 1.gigabyte
19
+ "#{(bytes / 1.gigabyte.to_f).round(1)}G".rjust(8)
20
+ elsif bytes >= 1.megabyte
21
+ "#{(bytes / 1.megabyte.to_f).round(1)}M".rjust(8)
22
+ elsif bytes >= 1.kilobyte
23
+ "#{bytes / 1024}K".rjust(8)
24
+ else
25
+ "#{bytes}B".rjust(8)
26
+ end
15
27
  end
16
28
  end
17
29
  end
18
30
 
19
31
  namespace :asdb do
20
- desc 'ActiveStorageDB: list attachments ordered by blob id desc'
21
- task list: [:environment] do |_t, _args|
22
- query = ActiveStorage::Blob.order(id: :desc).limit(100)
23
- digits = query.ids.inject(0) { |ret, id| size = id.to_s.size; [size, ret].max }
32
+ desc "ActiveStorageDB: list attachments ordered by blob id desc"
33
+ task :list, [:count] => [:environment] do |_t, args|
34
+ count = (args[:count] || 100).to_i
35
+ query = ActiveStorage::Blob.order(id: :desc).limit(count)
36
+ digits = query.ids.inject(0) { |ret, id|
37
+ size = id.to_s.size
38
+ [size, ret].max
39
+ }
24
40
 
25
41
  ActiveStorage::Tasks.print_blob_header(digits: digits)
26
42
  query.each do |blob|
@@ -28,14 +44,14 @@ namespace :asdb do
28
44
  end
29
45
  end
30
46
 
31
- desc 'ActiveStorageDB: download attachment by blob id'
47
+ desc "ActiveStorageDB: download attachment by blob id"
32
48
  task :download, [:blob_id, :destination] => [:environment] do |_t, args|
33
49
  blob_id = args[:blob_id]&.strip
34
50
  destination = args[:destination]&.strip || Dir.pwd
35
- abort('Required arguments: source blob id, destination path') if blob_id.blank? || destination.blank?
51
+ abort("Required arguments: source blob id, destination path") if blob_id.blank? || destination.blank?
36
52
 
37
53
  blob = ActiveStorage::Blob.find_by(id: blob_id)
38
- abort('Source file not found') unless blob
54
+ abort("Source file not found") unless blob
39
55
 
40
56
  destination = "#{destination}/#{blob.filename}" if Dir.exist?(destination)
41
57
  dir = File.dirname(destination)
@@ -45,20 +61,23 @@ namespace :asdb do
45
61
  puts "#{ret} bytes written - #{destination}"
46
62
  end
47
63
 
48
- desc 'ActiveStorageDB: search attachment by filename (or part of it)'
64
+ desc "ActiveStorageDB: search attachment by filename (or part of it)"
49
65
  task :search, [:filename] => [:environment] do |_t, args|
50
66
  filename = args[:filename]&.strip
51
- abort('Required arguments: filename') if filename.blank?
67
+ abort("Required arguments: filename") if filename.blank?
52
68
 
53
- blobs = ActiveStorage::Blob.where('filename LIKE ?', "%#{filename}%").order(id: :desc)
69
+ blobs = ActiveStorage::Blob.where("filename LIKE ?", "%#{ActiveRecord::Base.sanitize_sql_like(filename)}%").order(id: :desc)
54
70
  if blobs.any?
55
- digits = blobs.ids.inject(0) { |ret, id| size = id.to_s.size; [size, ret].max }
71
+ digits = blobs.ids.inject(0) { |ret, id|
72
+ size = id.to_s.size
73
+ [size, ret].max
74
+ }
56
75
  ActiveStorage::Tasks.print_blob_header(digits: digits)
57
76
  blobs.each do |blob|
58
77
  ActiveStorage::Tasks.print_blob(blob, digits: digits)
59
78
  end
60
79
  else
61
- puts 'No results'
80
+ puts "No results"
62
81
  end
63
82
  end
64
83
  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.4.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Roccoberton
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-03-09 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.5.22
116
- signing_key:
85
+ rubygems_version: 3.6.9
117
86
  specification_version: 4
118
87
  summary: ActiveStorage DB Service
119
88
  test_files: []