Skip to content

Commit b22249e

Browse files
authored
Merge pull request #85 from blocknotes/internal-improvements-4
Internal improvements 4
2 parents d92e9b8 + 9d682b1 commit b22249e

9 files changed

Lines changed: 68 additions & 64 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
[![maintainability](https://api.codeclimate.com/v1/badges/92e1e703c308744a0f66/maintainability)](https://codeclimate.com/github/blocknotes/active_storage_db/maintainability)
66

77
[![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 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)
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)
1010

1111
An Active Storage service upload/download plugin that stores files in a PostgreSQL or MySQL database.
1212
Experimental support also for MSSQL and SQLite.
@@ -21,8 +21,8 @@ Useful also with platforms like Heroku (due to their ephemeral file system).
2121
- Add to your Gemfile `gem 'active_storage_db'` (and execute: `bundle`)
2222
- Install the gem migrations: `bin/rails active_storage_db:install:migrations` (and execute: `bin/rails db:migrate`)
2323
- Add to your `config/routes.rb`: `mount ActiveStorageDB::Engine => '/active_storage_db'`
24-
- Change Active Storage service in *config/environments/development.rb* to: `config.active_storage.service = :db`
25-
- 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_:
2626

2727
```yml
2828
db:

active_storage_db.gemspec

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
# frozen_string_literal: true
22

3-
$:.push File.expand_path('lib', __dir__)
3+
$:.push File.expand_path("lib", __dir__)
44

55
# Maintain your gem's version:
6-
require 'active_storage_db/version'
6+
require "active_storage_db/version"
77

88
# Describe your gem and declare its dependencies:
99
Gem::Specification.new do |spec|
10-
spec.name = 'active_storage_db'
11-
spec.version = ActiveStorageDB::VERSION
12-
spec.authors = ['Mattia Roccoberton']
13-
spec.email = ['mat@blocknot.es']
14-
spec.homepage = 'https://github.com/blocknotes/active_storage_db'
15-
spec.summary = 'ActiveStorage DB Service'
16-
spec.description = 'An ActiveStorage service plugin to store files in database.'
17-
spec.license = 'MIT'
10+
spec.name = "active_storage_db"
11+
spec.version = ActiveStorageDB::VERSION
12+
spec.authors = ["Mattia Roccoberton"]
13+
spec.email = ["mat@blocknot.es"]
14+
spec.homepage = "https://github.com/blocknotes/active_storage_db"
15+
spec.summary = "ActiveStorage DB Service"
16+
spec.description = "An ActiveStorage service plugin to store files in database."
17+
spec.license = "MIT"
1818

19-
spec.required_ruby_version = '>= 2.7.0'
19+
spec.required_ruby_version = ">= 2.7.0"
2020

21-
spec.metadata['homepage_uri'] = spec.homepage
22-
spec.metadata['source_code_uri'] = spec.homepage
23-
spec.metadata['rubygems_mfa_required'] = 'true'
21+
spec.metadata["homepage_uri"] = spec.homepage
22+
spec.metadata["source_code_uri"] = spec.homepage
23+
spec.metadata["rubygems_mfa_required"] = "true"
2424

25-
spec.files = Dir["{app,config,db,lib}/**/*", 'MIT-LICENSE', 'Rakefile', 'README.md']
25+
spec.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
2626

27-
spec.add_dependency 'activestorage', '>= 6.0'
28-
spec.add_dependency 'rails', '>= 6.0'
27+
spec.add_dependency "activestorage", ">= 6.0"
28+
spec.add_dependency "rails", ">= 6.0"
2929
end

app/models/active_storage_db/file.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module ActiveStorageDB
44
class File < ApplicationRecord
55
validates :ref,
66
presence: true,
7-
allow_blank: false,
87
uniqueness: { case_sensitive: false }
8+
validates :data, presence: true
99
end
1010
end

lib/tasks/active_storage_db_tasks.rake

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ module ActiveStorage
99
end
1010

1111
def print_blob(blob, digits: 0)
12-
size = (blob.byte_size / 1024).to_s.rjust(7)
12+
size = if blob.byte_size < 1024
13+
"#{blob.byte_size}B".rjust(8)
14+
else
15+
"#{blob.byte_size / 1024}K".rjust(8)
16+
end
1317
date = blob.created_at.strftime("%Y-%m-%d %H:%M")
14-
puts "#{size}K #{date} #{blob.id.to_s.rjust(digits)} #{blob.filename}"
18+
puts "#{size} #{date} #{blob.id.to_s.rjust(digits)} #{blob.filename}"
1519
end
1620
end
1721
end

spec/dummy/app/models/post.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ class Post < ApplicationRecord
55
attachable.variant :thumb, resize_to_limit: [100, 100]
66
end
77

8-
validates :title, presence: true, allow_blank: false
8+
validates :title, presence: true
99
end

spec/models/active_storage_db/file_spec.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@
4141
)
4242
end
4343
end
44+
45+
context "when data is nil" do
46+
it "raises record invalid exception" do
47+
expect { create(:active_storage_db_file, data: nil) }.to raise_exception(
48+
ActiveRecord::RecordInvalid,
49+
/Data can't be blank/
50+
)
51+
end
52+
end
4453
end
4554

4655
describe "CRUD lifecycle" do

spec/requests/file_controller_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def unprocessable
9898
put blob.service_url_for_direct_upload, params: data, headers: { "Content-Type" => "text/plain" }
9999

100100
expect(response).to have_http_status(:no_content)
101-
expect(data).to eq blob.download
101+
expect(blob.download).to eq data
102102
end
103103

104104
it "uses blob direct upload with mismatched content type" do

spec/service/active_storage/service/db_service_spec.rb

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@
110110

111111
before { upload }
112112

113-
it { is_expected.to be_truthy }
113+
it { is_expected.to be true }
114114

115115
it "deletes the file" do
116116
expect { delete }.to change(ActiveStorageDB::File, :count).from(1).to(0)
@@ -119,17 +119,23 @@
119119
context "when the attachment is not found" do
120120
subject(:delete) { service.delete("#{key}!") }
121121

122-
it { is_expected.to be_falsey }
122+
it { is_expected.to be false }
123123
end
124124
end
125125

126126
describe ".delete_prefixed" do
127127
subject(:delete_prefixed) { service.delete_prefixed(key[0..10]) }
128128

129-
before { upload }
129+
let(:other_file) { create(:active_storage_db_file, ref: "unrelated_prefix_file") }
130+
131+
before do
132+
other_file
133+
upload
134+
end
130135

131-
it "deletes the files" do
132-
expect { delete_prefixed }.to change(ActiveStorageDB::File, :count).from(1).to(0)
136+
it "deletes only matching files", :aggregate_failures do
137+
expect { delete_prefixed }.to change(ActiveStorageDB::File, :count).by(-1)
138+
expect(ActiveStorageDB::File.find_by(ref: "unrelated_prefix_file")).to be_present
133139
end
134140

135141
context "when no files match the prefix" do
@@ -151,8 +157,6 @@
151157
context "with an existing file" do
152158
before { upload }
153159

154-
after { service.delete(key) }
155-
156160
it "downloads the data" do
157161
expect(download).to eq fixture_data
158162
end
@@ -167,9 +171,9 @@
167171
it "yields multiple chunks when data exceeds chunk size", :aggregate_failures do
168172
chunks = []
169173
service.download(key) { |chunk| chunks << chunk }
170-
# fixture_data is a PNG (~100 bytes), chunk size is 100 bytes,
171-
# so we expect at least 1 chunk yielded via the streaming path
172-
expect(chunks).not_to be_empty
174+
# fixture_data is 126 bytes, chunk size is 100 bytes,
175+
# so we expect exactly 2 chunks via the streaming path
176+
expect(chunks.size).to eq 2
173177
expect(chunks.join).to eq fixture_data
174178
end
175179
end
@@ -192,8 +196,6 @@
192196

193197
before { upload }
194198

195-
after { service.delete(key) }
196-
197199
it { is_expected.to eq fixture_data[range] }
198200

199201
context "when the attachment is not found" do
@@ -206,14 +208,12 @@
206208
describe ".exist?" do
207209
subject { service.exist?(key) }
208210

209-
it { is_expected.to be_falsey }
211+
it { is_expected.to be false }
210212

211213
context "when a file is uploaded" do
212214
before { upload }
213215

214-
after { service.delete(key) }
215-
216-
it { is_expected.to be_truthy }
216+
it { is_expected.to be true }
217217
end
218218
end
219219

@@ -226,17 +226,13 @@
226226
describe ".upload" do
227227
it "uploads the data" do
228228
expect(upload).to be_a ActiveStorageDB::File
229-
ensure
230-
service.delete(key)
231229
end
232230

233231
context "with the checksum" do
234232
let(:upload) { service.upload(key, StringIO.new(fixture_data), checksum: checksum) }
235233

236234
it "uploads the data" do
237235
expect(upload).to be_a ActiveStorageDB::File
238-
ensure
239-
service.delete(key)
240236
end
241237
end
242238

@@ -256,8 +252,6 @@
256252
context "with a duplicate key" do
257253
before { upload }
258254

259-
after { service.delete(key) }
260-
261255
it "raises a uniqueness error" do
262256
expect {
263257
service.upload(key, StringIO.new(fixture_data))
@@ -286,8 +280,6 @@
286280
end
287281
end
288282

289-
after { service.delete(key) }
290-
291283
it { is_expected.to start_with "#{url_options[:protocol]}#{url_options[:host]}" }
292284

293285
it "generates a token that contains the expected fields" do

spec/tasks/active_storage_db_tasks_spec.rb

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
describe "asdb:list" do
77
subject(:task) { execute_task("asdb:list") }
88

9-
let!(:first_file) { create(:active_storage_blob, filename: "some file 1", created_at: 1.hour.ago) }
10-
let!(:second_file) { create(:active_storage_blob, filename: "some file 2", created_at: 5.hours.ago) }
11-
let!(:third_file) { create(:active_storage_blob, filename: "some file 3", created_at: 3.hours.ago) }
9+
let!(:first_file) { create(:active_storage_blob, filename: "some file 1") }
10+
let!(:second_file) { create(:active_storage_blob, filename: "some file 2") }
11+
let!(:third_file) { create(:active_storage_blob, filename: "some file 3") }
1212

1313
it "prints the columns header + the list of 3 files", :aggregate_failures do
1414
pattern = /#{third_file.id} #{third_file.filename}.+#{second_file.id} #{second_file.filename}.+#{first_file.id} #{first_file.filename}/m
@@ -29,10 +29,6 @@
2929
context "with a missing source" do
3030
subject(:task) { execute_task("asdb:download", blob_id: "some_file") }
3131

32-
before do
33-
allow(File).to receive(:writable?).and_return(true)
34-
end
35-
3632
it "exits showing a not found error" do
3733
with_captured_stderr do
3834
expect { task }.to raise_exception(SystemExit, /Source file not found/)
@@ -46,7 +42,7 @@
4642
let(:blob) { build_stubbed(:active_storage_blob) }
4743

4844
before do
49-
allow(File).to receive(:writable?).and_return(false)
45+
allow(File).to receive(:writable?).with(".").and_return(false)
5046
allow(ActiveStorage::Blob).to receive(:find_by).and_return(blob)
5147
end
5248

@@ -63,13 +59,16 @@
6359
let(:blob) { build_stubbed(:active_storage_blob) }
6460

6561
before do
66-
allow(File).to receive_messages(binwrite: 1000, writable?: true)
62+
allow(File).to receive_messages(binwrite: 1000)
63+
allow(File).to receive(:writable?).with(".").and_return(true)
6764
allow(ActiveStorage::Blob).to receive(:find_by).and_return(blob)
6865
allow(blob).to receive(:download).and_return("some data")
6966
end
7067

71-
it "prints the number of bytes written" do
68+
it "downloads the blob and writes it to the destination", :aggregate_failures do
7269
expect(task).to eq "1000 bytes written - some_path\n"
70+
expect(blob).to have_received(:download)
71+
expect(File).to have_received(:binwrite).with("some_path", "some data")
7372
end
7473
end
7574
end
@@ -93,13 +92,13 @@
9392
end
9493
end
9594

96-
context "with there are some results" do
95+
context "when there are some results" do
9796
subject(:task) { execute_task("asdb:search", filename: "just ") }
9897

9998
before do
100-
create(:active_storage_blob, filename: "just a file", created_at: 1.hour.ago)
101-
create(:active_storage_blob, filename: "just another file", created_at: 5.hours.ago)
102-
create(:active_storage_blob, filename: "the last file", created_at: 3.hours.ago)
99+
create(:active_storage_blob, filename: "just a file")
100+
create(:active_storage_blob, filename: "just another file")
101+
create(:active_storage_blob, filename: "the last file")
103102
end
104103

105104
it "prints the files that matches", :aggregate_failures do

0 commit comments

Comments
 (0)