diff --git a/lib/active_storage_support/base64_attach.rb b/lib/active_storage_support/base64_attach.rb index 8eda4a4..b737eac 100644 --- a/lib/active_storage_support/base64_attach.rb +++ b/lib/active_storage_support/base64_attach.rb @@ -19,7 +19,7 @@ def fill_attachment_data(attachment, base64_data) return unless base64_data.try(:is_a?, String) && base64_data.strip.start_with?('data') headers, data = base64_data.split(',') - decoded_data = Base64.decode64(data) + decoded_data = Base64.decode64(data || '') attachment[:io] = StringIO.new(decoded_data) attachment[:content_type] ||= content_type(headers) diff --git a/spec/dummy/app/models/user.rb b/spec/dummy/app/models/user.rb index 025efb2..340e3d8 100644 --- a/spec/dummy/app/models/user.rb +++ b/spec/dummy/app/models/user.rb @@ -6,4 +6,6 @@ class User < ApplicationRecord has_many_base64_attached :pictures do |attachable| attachable.variant :thumb, resize_to_fit: [1, 1] end + + has_one_base64_attached :file end diff --git a/spec/fixtures/empty.txt b/spec/fixtures/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/spec/user_base64_spec.rb b/spec/user_base64_spec.rb index 374d3ed..bf0b69d 100644 --- a/spec/user_base64_spec.rb +++ b/spec/user_base64_spec.rb @@ -11,11 +11,18 @@ File.open(File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', second_filename))).read end + let(:empty_filename) { 'empty.txt' } + let(:empty_file) do + File.open(File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', empty_filename))).read + end + let(:base64_data) { { data: "data:image/jpeg;base64,#{Base64.encode64(file)}" } } let(:second_base64_data) { { data: "data:image/png;base64,#{Base64.encode64(second_file)}" } } + let(:empty_base64_data) { { data: "data:text/plain;base64,#{Base64.encode64(empty_file)}" } } let(:data_with_filename) { base64_data.merge(filename: filename) } let(:second_data_with_filename) { second_base64_data.merge(filename: second_filename) } + let(:empty_data_with_filename) { second_base64_data.merge(filename: empty_filename) } let!(:rails_url) { Rails.application.routes.url_helpers } let(:user) { User.create } @@ -833,4 +840,230 @@ end end end + + context 'when user uses an empty file' do + context 'when user does not have a file attached yet' do + it 'does not have a file attached' do + expect(user.file.attached?).not_to be + end + + context 'when "user.file.attach" is called' do + context 'when comes in the form of a Hash' do + context 'when only data is specified' do + it 'attaches a file to the user' do + user.file.attach(empty_base64_data) + + expect(user.file.attached?).to be + end + + it 'matches the attachment file' do + user.file.attach(empty_base64_data) + + expect( + File.open(ActiveStorage::Blob.service.send(:path_for, user.file.key)).read + ).to match(empty_file) + end + end + + context 'when filename is specified along with data' do + it 'assigns the specified filename' do + user.file.attach(empty_data_with_filename) + + expect(user.file.filename.to_s).to eq(empty_filename) + end + end + end + + context 'when comes in the form of an ActionController::Parameters' do + let(:empty_base64_data) do + params = ActionController::Parameters.new( + data: "data:text/plain;base64,#{Base64.encode64(empty_file)}" + ) + params.permit(:data) + end + + context 'when only data is specified' do + it 'attaches a file to the user' do + user.file.attach(empty_base64_data) + + expect(user.file.attached?).to be + end + + it 'matches the attachment file' do + user.file.attach(empty_base64_data) + + expect( + File.open(ActiveStorage::Blob.service.send(:path_for, user.file.key)).read + ).to match(empty_file) + end + end + + context 'when filename is specified along with data' do + it 'assigns the specified filename' do + user.file.attach(empty_data_with_filename) + + expect(user.file.filename.to_s).to eq(empty_filename) + end + end + end + end + + context 'when "user.file=" is called' do + context 'when it comes in the form of a Hash' do + context 'when only data is specified' do + it 'attaches a file to the user' do + user.file = empty_base64_data + user.save + + expect(user.file.attached?).to be + end + + it 'attached file matches attachment file' do + user.file = empty_base64_data + user.save + + expect( + File.open(ActiveStorage::Blob.service.send(:path_for, user.file.key)).read + ).to match(empty_file) + end + end + + context 'when filename is specified along with data' do + it 'assigns the specified filename' do + user.file = empty_data_with_filename + user.save + + expect(user.file.filename.to_s).to eq(empty_filename) + end + end + end + + context 'when it comes in the form of an ActionController::Parameters' do + let(:empty_base64_data) do + params = ActionController::Parameters.new( + data: "data:text/plain;base64,#{Base64.encode64(empty_file)}" + ) + params.permit(:data) + end + + context 'when only data is specified' do + it 'attaches a file to the user' do + user.file = empty_base64_data + user.save + + expect(user.file.attached?).to be + end + + it 'attached file matches attachment file' do + user.file = empty_base64_data + user.save + + expect( + File.open(ActiveStorage::Blob.service.send(:path_for, user.file.key)).read + ).to match(empty_file) + end + end + + context 'when filename is specified along with data' do + it 'assigns the specified filename' do + user.file = empty_data_with_filename + user.save + + expect(user.file.filename.to_s).to eq(empty_filename) + end + end + end + end + + context 'when user is created' do + context 'when it comes in the form of a Hash' do + context 'when only data is specified' do + it 'attaches a file to the user' do + user = User.create!(username: 'peterparker', file: empty_base64_data) + + expect(user.file.attached?).to be + end + + it 'attached file matches attachment file' do + user = User.create!(username: 'peterparker', file: empty_base64_data) + + expect( + File.open(ActiveStorage::Blob.service.send(:path_for, user.file.key)).read + ).to match(empty_file) + end + end + + context 'when filename is specified along with data' do + it 'assigns the specified filename' do + user = User.create!(username: 'peterparker', file: empty_data_with_filename) + + expect(user.file.filename).to eq(empty_filename) + end + end + end + + context 'when it comes in the form of ActionController::Parameters' do + let(:file_params) { { data: "data:text/plain;base64,#{Base64.encode64(empty_file)}" } } + let(:user_params) do + params = ActionController::Parameters.new( + username: 'peterparker', file: file_params + ) + params.permit(:name, file: file_params.keys) + end + + it 'attaches a file to the user' do + user = User.create!(user_params) + + expect(user.file.attached?).to be + end + + it 'attached file matches attachment file' do + user = User.create!(user_params) + + expect( + File.open(ActiveStorage::Blob.service.send(:path_for, user.file.key)).read + ).to match(empty_file) + end + + context 'when filename is specified along with data' do + let(:file_params) { empty_data_with_filename } + + it 'assigns the specified filename' do + user = User.create!(user_params) + + expect(user.file.filename).to eq(empty_filename) + end + end + end + end + + context 'when a link to the file is required' do + it 'can not generate the URL and raises an error' do + expect do + rails_url.rails_blob_url(user.file, disposition: 'attachment') + end.to raise_error(StandardError) + end + end + end + + context 'when user already has a file attached' do + let(:user) { User.create!(file: empty_base64_data) } + + context 'when the user wants to remove the file' do + it 'removes the file' do + user.file.purge + + expect(user.file.attached?).not_to be + end + end + + context 'when a link to the file file is required' do + it 'returns a link' do + url = rails_url.rails_blob_url(user.file, disposition: 'attachment') + + expect(url).to be + end + end + end + end end diff --git a/spec/user_file_spec.rb b/spec/user_file_spec.rb index ecfc184..f24cb4b 100644 --- a/spec/user_file_spec.rb +++ b/spec/user_file_spec.rb @@ -11,6 +11,16 @@ } end + let(:empty_filename) { 'empty.txt' } + let(:empty_file) do + { + io: File.open( + File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', empty_filename)) + ), + filename: empty_filename + } + end + let!(:rails_url) { Rails.application.routes.url_helpers } let(:user) { User.create } @@ -308,4 +318,84 @@ end end end + + context 'when user uses an empty file' do + context 'when user does not have a file attached yet' do + it 'does not have an file attached' do + expect(user.file.attached?).not_to be + end + + context 'when "user.file.attach" is called' do + it 'attaches a file to the user' do + user.file.attach(empty_file) + + expect(user.file.attached?).to be + end + + it 'assigns the specified filename' do + user.file.attach(empty_file) + + expect(user.file.filename.to_s).to eq(empty_filename) + end + end + + context 'when "user.file=" is called' do + it 'attaches a file to the user' do + user.file = empty_file + user.save + + expect(user.file.attached?).to be + end + + it 'assigns the specified filename' do + user.file = empty_file + user.save + + expect(user.file.filename.to_s).to eq(empty_filename) + end + end + + context 'when the file is sent as a hash parameter to the user' do + it 'attaches a file to the user' do + user = User.create!(file: empty_file) + + expect(user.file.attached?).to be + end + + it 'assigns the specified filename' do + user = User.create!(file: empty_file) + + expect(user.file.filename.to_s).to eq(empty_filename) + end + end + + context 'when a link to the file file is required' do + it 'can not generate the URL and raises an error' do + expect do + rails_url.rails_blob_url(user.file, disposition: 'attachment') + end.to raise_error(StandardError) + end + end + end + + context 'when user has a file attached' do + let(:user) { User.create!(file: empty_file) } + + context 'when the user wants to remove the file' do + it 'removes the file' do + user.file.purge + + expect(user.file.attached?).not_to be + end + end + + context 'when a link to the file is required' do + it 'returns a link' do + url = rails_url.rails_blob_url(user.file, disposition: 'attachment') + + expect(url).to be + end + end + end + end end