active_storage_db 0.2.0 → 1.1.2

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: 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