active_storage_db 0.2.0 → 1.1.2

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: c326008fa309fa9fe2ac2768a92b5ce17eae3f907e4185d054169d54ac675d89
4
- data.tar.gz: a495dc3271c0df2cd1b0c8ca5a6332aa052475aac6e38cc9b42b4c073e8796ba
3
+ metadata.gz: 33872893aefc8f1086a8c422819990e10d1d46d2b97bd87d67662fd153536243
4
+ data.tar.gz: 0e662cd62a06ec353c9e02706c1c553500edafa146e4149d21fa57626d129d5a
5
5
  SHA512:
6
- metadata.gz: 41a2e02d066df9bfa01b15f50a50ceb73ebc2af089cdc2d70fb3d7c3b0896fcc37bbac27ae0aaa648e56c556ae8e157843e4fff35e99d5d3643ba17cd10bfcd9
7
- data.tar.gz: fe6e84d048c51b6c02db33fec2c53d95fa639ee35e654c2d3479df57884bccd56a87987780f7aa58e6f9c51dbac218ee0b18c26bcfc5c7968b594222481e890f
6
+ metadata.gz: a418655f7275cc54264b25733712cbe813eeede79eb7d08833c2e333427d7a9c1cb44f1c46b3a338cf20407abf0d7d9b59e2dfcdec0f1467b77f47ca85b7357e
7
+ data.tar.gz: ac8d6c3c8fc9c44177d474c709f67ff5a46a8c1835c15e9f753773ac846851e23bd13ef2e8e0c3fd13185ea0d8f528f31aac8ae48275a46288bde22a6ad04dee
data/README.md CHANGED
@@ -1,38 +1,60 @@
1
- # Active Storage DB [![Gem Version](https://badge.fury.io/rb/active_storage_db.svg)](https://badge.fury.io/rb/active_storage_db) [![Specs - Postgres](https://github.com/blocknotes/active_storage_db/actions/workflows/postgres.yml/badge.svg)](https://github.com/blocknotes/active_storage_db/actions/workflows/postgres.yml) [![Specs - MySQL](https://github.com/blocknotes/active_storage_db/actions/workflows/mysql.yml/badge.svg)](https://github.com/blocknotes/active_storage_db/actions/workflows/mysql.yml)
1
+ # Active Storage DB
2
+
3
+ [![gem version](https://badge.fury.io/rb/active_storage_db.svg)](https://badge.fury.io/rb/active_storage_db)
4
+ [![gem downloads](https://badgen.net/rubygems/dt/active_storage_db)](https://rubygems.org/gems/active_storage_db)
5
+ [![maintainability](https://api.codeclimate.com/v1/badges/92e1e703c308744a0f66/maintainability)](https://codeclimate.com/github/blocknotes/active_storage_db/maintainability)
6
+
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_70.yml/badge.svg)](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_postgres_70.yml)
9
+ [![Specs MySQL](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_mysql_70.yml/badge.svg)](https://github.com/blocknotes/active_storage_db/actions/workflows/specs_mysql_70.yml)
10
+
2
11
  An Active Storage service upload/download plugin that stores files in a PostgreSQL or MySQL database.
3
12
 
4
13
  Main features:
14
+ - supports Rails _6.0_, _6.1_ and _7.0_;
5
15
  - all service methods implemented;
6
- - data is saved using a binary field (or blob);
7
- - RSpec tests.
16
+ - attachment data stored in a binary field (or blob).
8
17
 
9
18
  Useful also with platforms like Heroku (due to their ephemeral file system).
10
19
 
11
20
  ## Installation
21
+
12
22
  - Setup Active Storage in your Rails application
13
23
  - Add to your Gemfile `gem 'active_storage_db'` (and execute: `bundle`)
14
24
  - Install the gem migrations: `bin/rails active_storage_db:install:migrations` (and execute: `bin/rails db:migrate`)
15
25
  - Add to your `config/routes.rb`: `mount ActiveStorageDB::Engine => '/active_storage_db'`
16
26
  - Change Active Storage service in *config/environments/development.rb* to: `config.active_storage.service = :db`
17
27
  - Add to *config/storage.yml*:
28
+
18
29
  ```
19
30
  db:
20
31
  service: DB
21
32
  ```
22
33
 
23
34
  ## Misc
24
- Some rake tasks are available:
25
- - `asdb:list`: list the stored attachments
26
- - `asdb:get`: download an attachment (ex. `bin/rails "asdb:get[ruby-logo.png,/tmp]"`)
35
+
36
+ Some utility tasks are available:
37
+
38
+ ```sh
39
+ # list attachments ordered by blob id desc (with limit 100):
40
+ bin/rails 'asdb:list'
41
+ # search attachments by filename (or part of it)
42
+ bin/rails 'asdb:search[some_filename]'
43
+ # download attachment by blob id (retrieved with list or search tasks) - the second argument is the destination:
44
+ bin/rails 'asdb:download[123,/tmp]'
45
+ ```
27
46
 
28
47
  ## Do you like it? Star it!
48
+
29
49
  If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
30
50
 
31
51
  Or consider offering me a coffee, it's a small thing but it is greatly appreciated: [about me](https://www.blocknot.es/about-me).
32
52
 
33
53
  ## Contributors
54
+
34
55
  - [Mattia Roccoberton](https://blocknot.es/): author
35
56
  - Inspired by [activestorage-database-service](https://github.com/TitovDigital/activestorage-database-service) project
36
57
 
37
58
  ## License
59
+
38
60
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -16,7 +16,8 @@ RDoc::Task.new(:rdoc) do |rdoc|
16
16
  rdoc.rdoc_files.include('lib/**/*.rb')
17
17
  end
18
18
 
19
- APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
19
+ app_ver = ENV.fetch('RAILS', '').tr('.', '')
20
+ APP_RAKEFILE = File.expand_path("spec/dummy#{app_ver}/Rakefile", __dir__)
20
21
  load 'rails/tasks/engine.rake'
21
22
 
22
23
  load 'rails/tasks/statistics.rake'
@@ -8,34 +8,41 @@ module ActiveStorageDB
8
8
  if (key = decode_verified_key)
9
9
  serve_file(key[:key], content_type: key[:content_type], disposition: key[:disposition])
10
10
  else
11
- head :not_found
11
+ head(:not_found)
12
12
  end
13
13
  rescue ActiveStorage::FileNotFoundError
14
- head :not_found
14
+ head(:not_found)
15
15
  end
16
16
 
17
17
  def update
18
18
  if (token = decode_verified_token)
19
- if acceptable_content?(token)
20
- db_service.upload(token[:key], request.body, checksum: token[:checksum])
21
- else
22
- head :unprocessable_entity
23
- end
19
+ file_uploaded = upload_file(token, body: request.body)
20
+ head(file_uploaded ? :no_content : :unprocessable_entity)
24
21
  else
25
- head :not_found
22
+ head(:not_found)
26
23
  end
27
24
  rescue ActiveStorage::IntegrityError
28
- head :unprocessable_entity
25
+ head(:unprocessable_entity)
29
26
  end
30
27
 
31
28
  private
32
29
 
30
+ def acceptable_content?(token)
31
+ token[:content_type] == request.content_mime_type && token[:content_length] == request.content_length
32
+ end
33
+
33
34
  def db_service
34
35
  ActiveStorage::Blob.service
35
36
  end
36
37
 
37
38
  def decode_verified_key
38
- ActiveStorage.verifier.verified(params[:encoded_key], purpose: :blob_key)
39
+ key = ActiveStorage.verifier.verified(params[:encoded_key], purpose: :blob_key)
40
+ key&.deep_symbolize_keys
41
+ end
42
+
43
+ def decode_verified_token
44
+ token = ActiveStorage.verifier.verified(params[:encoded_token], purpose: :blob_token)
45
+ token&.deep_symbolize_keys
39
46
  end
40
47
 
41
48
  def serve_file(key, content_type:, disposition:)
@@ -43,15 +50,14 @@ module ActiveStorageDB
43
50
  type: content_type || DEFAULT_SEND_FILE_TYPE,
44
51
  disposition: disposition || DEFAULT_SEND_FILE_DISPOSITION
45
52
  }
46
- send_data db_service.download(key), options
53
+ send_data(db_service.download(key), options)
47
54
  end
48
55
 
49
- def decode_verified_token
50
- ActiveStorage.verifier.verified(params[:encoded_token], purpose: :blob_token)
51
- end
56
+ def upload_file(token, body:)
57
+ return false unless acceptable_content?(token)
52
58
 
53
- def acceptable_content?(token)
54
- token[:content_type] == request.content_mime_type && token[:content_length] == request.content_length
59
+ db_service.upload(token[:key], request.body, checksum: token[:checksum])
60
+ true
55
61
  end
56
62
  end
57
63
  end
@@ -2,12 +2,25 @@
2
2
 
3
3
  class CreateActiveStorageDBFiles < ActiveRecord::Migration[6.0]
4
4
  def change
5
- create_table :active_storage_db_files do |t|
5
+ create_table :active_storage_db_files, id: primary_key_type do |t|
6
6
  t.string :ref, null: false
7
7
  t.binary :data, null: false
8
- t.datetime :created_at, null: false
8
+
9
+ if connection.supports_datetime_with_precision?
10
+ t.datetime :created_at, precision: 6, null: false
11
+ else
12
+ t.datetime :created_at, null: false
13
+ end
9
14
 
10
15
  t.index [:ref], unique: true
11
16
  end
12
17
  end
18
+
19
+ private
20
+
21
+ def primary_key_type
22
+ config = Rails.configuration.generators
23
+ primary_key_type = config.options[config.orm][:primary_key_type]
24
+ primary_key_type || :primary_key
25
+ end
13
26
  end
@@ -1,9 +1,24 @@
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'
6
+
3
7
  module ActiveStorage
4
- class Service::DBService < Service # rubocop:disable Style/ClassAndModuleChildren
5
- def initialize(**_config)
8
+ # Wraps a DB table as an Active Storage service. See ActiveStorage::Service
9
+ # for the generic API documentation that applies to all services.
10
+ class Service::DBService < Service
11
+ if Rails::VERSION::MAJOR >= 7
12
+ include ActiveStorage::DBServiceRails70
13
+ elsif Rails::VERSION::MAJOR == 6 && Rails::VERSION::MINOR == 1
14
+ include ActiveStorage::DBServiceRails61
15
+ else
16
+ include ActiveStorage::DBServiceRails60
17
+ end
18
+
19
+ def initialize(public: false, **)
6
20
  @chunk_size = ENV.fetch('ASDB_CHUNK_SIZE') { 1.megabytes }
21
+ @public = public
7
22
  end
8
23
 
9
24
  def upload(key, io, checksum: nil, **)
@@ -21,29 +36,30 @@ module ActiveStorage
21
36
  end
22
37
  else
23
38
  instrument :download, key: key do
24
- record = ::ActiveStorageDB::File.find_by(ref: key)
25
- record&.data || raise(ActiveStorage::FileNotFoundError)
39
+ retrieve_file(key)
26
40
  end
27
41
  end
28
42
  end
29
43
 
30
44
  def download_chunk(key, range)
31
45
  instrument :download_chunk, key: key, range: range do
32
- chunk_select = "SUBSTRING(data FROM #{range.begin + 1} FOR #{range.size}) AS chunk"
33
- ::ActiveStorageDB::File.select(chunk_select).find_by(ref: key)&.chunk ||
34
- raise(ActiveStorage::FileNotFoundError)
46
+ record = object_for(key, fields: "SUBSTRING(data FROM #{range.begin + 1} FOR #{range.size}) AS chunk")
47
+ raise(ActiveStorage::FileNotFoundError) unless record
48
+
49
+ record.chunk
35
50
  end
36
51
  end
37
52
 
38
53
  def delete(key)
39
54
  instrument :delete, key: key do
40
55
  ::ActiveStorageDB::File.find_by(ref: key)&.destroy
56
+ # Ignore files already deleted
41
57
  end
42
58
  end
43
59
 
44
60
  def delete_prefixed(prefix)
45
61
  instrument :delete_prefixed, prefix: prefix do
46
- ::ActiveStorageDB::File.where('ref LIKE ?', "#{prefix}%").destroy_all
62
+ ::ActiveStorageDB::File.where('ref LIKE ?', "#{ApplicationRecord.sanitize_sql_like(prefix)}%").destroy_all
47
63
  end
48
64
  end
49
65
 
@@ -55,53 +71,23 @@ module ActiveStorage
55
71
  end
56
72
  end
57
73
 
58
- def url(key, expires_in:, filename:, disposition:, content_type:)
59
- instrument :url, key: key do |payload|
60
- content_disposition = content_disposition_with(type: disposition, filename: filename)
61
- verified_key_with_expiration = ActiveStorage.verifier.generate(
62
- {
63
- key: key,
64
- disposition: content_disposition,
65
- content_type: content_type
66
- },
67
- expires_in: expires_in,
68
- purpose: :blob_key
69
- )
70
- current_uri = URI.parse(current_host)
71
- generated_url = url_helpers.service_url(
72
- verified_key_with_expiration,
73
- protocol: current_uri.scheme,
74
- host: current_uri.host,
75
- port: current_uri.port,
76
- disposition: content_disposition,
77
- content_type: content_type,
78
- filename: filename
79
- )
80
- payload[:url] = generated_url
81
-
82
- generated_url
83
- end
84
- end
85
-
86
- def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
74
+ def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, custom_metadata: {})
87
75
  instrument :url, key: key do |payload|
88
76
  verified_token_with_expiration = ActiveStorage.verifier.generate(
89
77
  {
90
78
  key: key,
91
79
  content_type: content_type,
92
80
  content_length: content_length,
93
- checksum: checksum
81
+ checksum: checksum,
82
+ service_name: respond_to?(:name) ? name : 'db'
94
83
  },
95
84
  expires_in: expires_in,
96
85
  purpose: :blob_token
97
86
  )
98
- generated_url = url_helpers.update_service_url(
99
- verified_token_with_expiration,
100
- host: current_host
101
- )
102
- payload[:url] = generated_url
103
87
 
104
- generated_url
88
+ url_helpers.update_service_url(verified_token_with_expiration, url_options).tap do |generated_url|
89
+ payload[:url] = generated_url
90
+ end
105
91
  end
106
92
  end
107
93
 
@@ -111,24 +97,54 @@ module ActiveStorage
111
97
 
112
98
  private
113
99
 
114
- def current_host
115
- ActiveStorage::Current.host
100
+ def generate_url(key, expires_in:, filename:, content_type:, disposition:)
101
+ content_disposition = content_disposition_with(type: disposition, filename: filename)
102
+ verified_key_with_expiration = ActiveStorage.verifier.generate(
103
+ {
104
+ key: key,
105
+ disposition: content_disposition,
106
+ content_type: content_type,
107
+ service_name: respond_to?(:name) ? name : 'db'
108
+ },
109
+ expires_in: expires_in,
110
+ purpose: :blob_key
111
+ )
112
+
113
+ current_uri = URI.parse(current_host)
114
+ url_helpers.service_url(
115
+ verified_key_with_expiration,
116
+ protocol: current_uri.scheme,
117
+ host: current_uri.host,
118
+ port: current_uri.port,
119
+ disposition: content_disposition,
120
+ content_type: content_type,
121
+ filename: filename
122
+ )
116
123
  end
117
124
 
118
125
  def ensure_integrity_of(key, checksum)
119
- file = ::ActiveStorageDB::File.find_by(ref: key)
120
- return if Digest::MD5.base64digest(file.data) == checksum
126
+ return if Digest::MD5.base64digest(object_for(key).data) == checksum
121
127
 
122
128
  delete(key)
123
129
  raise ActiveStorage::IntegrityError
124
130
  end
125
131
 
132
+ def retrieve_file(key)
133
+ file = object_for(key)
134
+ raise(ActiveStorage::FileNotFoundError) unless file
135
+
136
+ file.data
137
+ end
138
+
139
+ def object_for(key, fields: nil)
140
+ as_file = fields ? ::ActiveStorageDB::File.select(*fields) : ::ActiveStorageDB::File
141
+ as_file.find_by(ref: key)
142
+ end
143
+
126
144
  def stream(key)
127
- size =
128
- ::ActiveStorageDB::File.select('OCTET_LENGTH(data) AS size').find_by(ref: key)&.size ||
129
- raise(ActiveStorage::FileNotFoundError)
145
+ size = object_for(key, fields: 'OCTET_LENGTH(data) AS size')&.size || raise(ActiveStorage::FileNotFoundError)
130
146
  (size / @chunk_size.to_f).ceil.times.each do |i|
131
- range = (i * @chunk_size..(i + 1) * @chunk_size - 1)
147
+ range = (i * @chunk_size..((i + 1) * @chunk_size) - 1)
132
148
  yield download_chunk(key, range)
133
149
  end
134
150
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveStorage
4
+ module DBServiceRails60
5
+ def url(key, expires_in:, filename:, disposition:, content_type:)
6
+ instrument :url, key: key do |payload|
7
+ generate_url(key, expires_in: expires_in, filename: filename, content_type: content_type, disposition: disposition).tap do |generated_url|
8
+ payload[:url] = generated_url
9
+ end
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def current_host
16
+ url_options[:host]
17
+ end
18
+
19
+ def url_options
20
+ {
21
+ host: ActiveStorage::Current.host
22
+ }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveStorage
4
+ module DBServiceRails61
5
+ private
6
+
7
+ def current_host
8
+ url_options[:host]
9
+ end
10
+
11
+ def private_url(key, expires_in:, filename:, content_type:, disposition:, **)
12
+ generate_url(
13
+ key,
14
+ expires_in: expires_in,
15
+ filename: filename,
16
+ content_type: content_type,
17
+ disposition: disposition
18
+ )
19
+ end
20
+
21
+ def public_url(key, filename:, content_type: nil, disposition: :attachment, **)
22
+ generate_url(
23
+ key,
24
+ expires_in: nil,
25
+ filename: filename,
26
+ content_type: content_type,
27
+ disposition: disposition
28
+ )
29
+ end
30
+
31
+ def url_options
32
+ {
33
+ host: ActiveStorage::Current.host
34
+ }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveStorage
4
+ module DBServiceRails70
5
+ def compose(source_keys, destination_key, **)
6
+ buffer = nil
7
+ source_keys.each do |source_key|
8
+ data = ::ActiveStorageDB::File.find_by!(ref: source_key).data
9
+ if buffer
10
+ buffer << data
11
+ else
12
+ buffer = data
13
+ end
14
+ end
15
+ ::ActiveStorageDB::File.create!(ref: destination_key, data: buffer) if buffer
16
+ end
17
+
18
+ private
19
+
20
+ def current_host
21
+ opts = url_options || {}
22
+ url = "#{opts[:protocol]}#{opts[:host]}"
23
+ url + ":#{opts[:port]}" if opts[:port]
24
+ end
25
+
26
+ def private_url(key, expires_in:, filename:, content_type:, disposition:, **)
27
+ generate_url(
28
+ key,
29
+ expires_in: expires_in,
30
+ filename: filename,
31
+ content_type: content_type,
32
+ disposition: disposition
33
+ )
34
+ end
35
+
36
+ def public_url(key, filename:, content_type: nil, disposition: :attachment, **)
37
+ generate_url(
38
+ key,
39
+ expires_in: nil,
40
+ filename: filename,
41
+ content_type: content_type,
42
+ disposition: disposition
43
+ )
44
+ end
45
+
46
+ def url_options
47
+ ActiveStorage::Current.url_options
48
+ end
49
+ end
50
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorageDB
4
- VERSION = '0.2.0'
4
+ VERSION = '1.1.2'
5
5
  end
@@ -1,31 +1,64 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ module ActiveStorage
4
+ module Tasks
5
+ module_function
6
+
7
+ def print_blob_header(digits: 0)
8
+ puts ['Size'.rjust(8), 'Date'.rjust(18), 'Id'.rjust(digits + 2), ' Filename'].join
9
+ end
10
+
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}"
15
+ end
16
+ end
17
+ end
18
+
3
19
  namespace :asdb do
4
- desc 'ActiveStorageDB: list attachments'
20
+ desc 'ActiveStorageDB: list attachments ordered by blob id desc'
5
21
  task list: [:environment] do |_t, _args|
6
- ::ActiveStorage::Blob.order(:filename).pluck(:byte_size, :created_at, :filename).each do |size, dt, filename|
7
- size_k = (size / 1024).to_s.rjust(7)
8
- date = dt.strftime('%Y-%m-%d %H:%M')
9
- puts "#{size_k}K #{date} #{filename}"
22
+ query = ::ActiveStorage::Blob.order(id: :desc).limit(100)
23
+ digits = Math.log(query.maximum(:id), 10).to_i + 1
24
+
25
+ ::ActiveStorage::Tasks.print_blob_header(digits: digits)
26
+ query.each do |blob|
27
+ ::ActiveStorage::Tasks.print_blob(blob, digits: digits)
10
28
  end
11
29
  end
12
30
 
13
- desc 'ActiveStorageDB: download attachment'
14
- task :get, [:src, :dst] => [:environment] do |_t, args|
15
- src = args[:src]&.strip
16
- dst = args[:dst]&.strip
17
- abort('Required arguments: source file, destination file') if src.blank? || dst.blank?
18
-
19
- dst = "#{dst}/#{src}" if Dir.exist?(dst)
20
- dir = File.dirname(dst)
21
- abort("Can't write on: #{dir}") unless File.writable?(dir)
31
+ desc 'ActiveStorageDB: download attachment by blob id'
32
+ task :download, [:blob_id, :destination] => [:environment] do |_t, args|
33
+ blob_id = args[:blob_id]&.strip
34
+ destination = args[:destination]&.strip || Dir.pwd
35
+ abort('Required arguments: source blob id, destination path') if blob_id.blank? || destination.blank?
22
36
 
23
- blob = ::ActiveStorage::Blob.order(created_at: :desc).find_by(filename: src)
37
+ blob = ::ActiveStorage::Blob.find_by(id: blob_id)
24
38
  abort('Source file not found') unless blob
25
39
 
26
- ret = File.binwrite(dst, blob.download)
27
- puts "#{ret} bytes written"
28
- rescue StandardError => e
29
- puts e
40
+ destination = "#{destination}/#{blob.filename}" if Dir.exist?(destination)
41
+ dir = File.dirname(destination)
42
+ abort("Can't write on path: #{dir}") unless File.writable?(dir)
43
+
44
+ ret = File.binwrite(destination, blob.download)
45
+ puts "#{ret} bytes written - #{destination}"
46
+ end
47
+
48
+ desc 'ActiveStorageDB: search attachment by filename (or part of it)'
49
+ task :search, [:filename] => [:environment] do |_t, args|
50
+ filename = args[:filename]&.strip
51
+ abort('Required arguments: filename') if filename.blank?
52
+
53
+ blobs = ::ActiveStorage::Blob.where('filename LIKE ?', "%#{filename}%").order(id: :desc)
54
+ if blobs.any?
55
+ digits = Math.log(blobs.first.id, 10).to_i + 1
56
+ ::ActiveStorage::Tasks.print_blob_header(digits: digits)
57
+ blobs.each do |blob|
58
+ ::ActiveStorage::Tasks.print_blob(blob, digits: digits)
59
+ end
60
+ else
61
+ puts 'No results'
62
+ end
30
63
  end
31
64
  end
metadata CHANGED
@@ -1,43 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_storage_db
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Roccoberton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-29 00:00:00.000000000 Z
11
+ date: 2022-09-07 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
19
  version: '6.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '6.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rails
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '6.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  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'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: factory_bot_rails
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -69,6 +83,9 @@ files:
69
83
  - config/routes.rb
70
84
  - db/migrate/20200702202022_create_active_storage_db_files.rb
71
85
  - lib/active_storage/service/db_service.rb
86
+ - lib/active_storage/service/db_service_rails60.rb
87
+ - lib/active_storage/service/db_service_rails61.rb
88
+ - lib/active_storage/service/db_service_rails70.rb
72
89
  - lib/active_storage_db.rb
73
90
  - lib/active_storage_db/engine.rb
74
91
  - lib/active_storage_db/version.rb
@@ -76,7 +93,10 @@ files:
76
93
  homepage: https://github.com/blocknotes/active_storage_db
77
94
  licenses:
78
95
  - MIT
79
- metadata: {}
96
+ metadata:
97
+ homepage_uri: https://github.com/blocknotes/active_storage_db
98
+ source_code_uri: https://github.com/blocknotes/active_storage_db
99
+ rubygems_mfa_required: 'true'
80
100
  post_install_message:
81
101
  rdoc_options: []
82
102
  require_paths:
@@ -85,14 +105,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
105
  requirements:
86
106
  - - ">="
87
107
  - !ruby/object:Gem::Version
88
- version: 2.5.0
108
+ version: 2.6.0
89
109
  required_rubygems_version: !ruby/object:Gem::Requirement
90
110
  requirements:
91
111
  - - ">="
92
112
  - !ruby/object:Gem::Version
93
113
  version: '0'
94
114
  requirements: []
95
- rubygems_version: 3.0.3
115
+ rubygems_version: 3.1.6
96
116
  signing_key:
97
117
  specification_version: 4
98
118
  summary: ActiveStorage DB Service