file_blobs_rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.travis.yml +16 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +397 -0
- data/Gemfile.rails5 +18 -0
- data/LICENSE.txt +20 -0
- data/README.md +62 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/file_blobs_rails.gemspec +131 -0
- data/lib/file_blobs_rails/action_controller_data_streaming_extensions.rb +60 -0
- data/lib/file_blobs_rails/active_record_extensions.rb +257 -0
- data/lib/file_blobs_rails/active_record_fixture_set_extensions.rb +58 -0
- data/lib/file_blobs_rails/active_record_migration_extensions.rb +31 -0
- data/lib/file_blobs_rails/active_record_table_definition_extensions.rb +35 -0
- data/lib/file_blobs_rails/active_support_test_extensions.rb +44 -0
- data/lib/file_blobs_rails/blob_model.rb +105 -0
- data/lib/file_blobs_rails/engine.rb +10 -0
- data/lib/file_blobs_rails/file_blob_proxy.rb +7 -0
- data/lib/file_blobs_rails/generators/blob_model_generator.rb +25 -0
- data/lib/file_blobs_rails/generators/blob_owner_generator.rb +28 -0
- data/lib/file_blobs_rails/generators/templates/001_create_file_blobs.rb.erb +7 -0
- data/lib/file_blobs_rails/generators/templates/002_create_blob_owners.rb.erb +7 -0
- data/lib/file_blobs_rails/generators/templates/blob_owner.rb.erb +3 -0
- data/lib/file_blobs_rails/generators/templates/blob_owner_test.rb.erb +13 -0
- data/lib/file_blobs_rails/generators/templates/blob_owners.yml.erb +11 -0
- data/lib/file_blobs_rails/generators/templates/file_blob.rb.erb +11 -0
- data/lib/file_blobs_rails/generators/templates/file_blob_test.rb.erb +9 -0
- data/lib/file_blobs_rails/generators/templates/file_blobs.yml.erb +7 -0
- data/lib/file_blobs_rails/generators/templates/files/invoice.pdf +137 -0
- data/lib/file_blobs_rails/generators/templates/files/ruby.png +0 -0
- data/lib/file_blobs_rails.rb +21 -0
- data/test/blob_model_test.rb +23 -0
- data/test/blob_owner_test.rb +9 -0
- data/test/controller_extensions_test.rb +80 -0
- data/test/file_blob_proxy_test.rb +100 -0
- data/test/file_blob_test.rb +8 -0
- data/test/file_blobs_fixture_test.rb +21 -0
- data/test/fixtures/003_create_gem_test_blobs.rb +8 -0
- data/test/fixtures/004_create_gem_test_messages.rb +15 -0
- data/test/fixtures/files/invoice.pdf +137 -0
- data/test/fixtures/files/ruby.png +0 -0
- data/test/fixtures/gem_test_blob.rb +5 -0
- data/test/fixtures/gem_test_message.rb +5 -0
- data/test/garbage_collection_test.rb +84 -0
- data/test/helpers/action_controller.rb +5 -0
- data/test/helpers/db_setup.rb +22 -0
- data/test/helpers/fixtures.rb +41 -0
- data/test/helpers/i18n.rb +1 -0
- data/test/helpers/migrations.rb +41 -0
- data/test/helpers/rails.rb +9 -0
- data/test/helpers/routes.rb +18 -0
- data/test/helpers/test_order.rb +1 -0
- data/test/test_extensions_test.rb +21 -0
- data/test/test_helper.rb +36 -0
- metadata +283 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'digest/sha2'
|
3
|
+
|
4
|
+
require 'active_model'
|
5
|
+
require 'active_support'
|
6
|
+
|
7
|
+
module FileBlobs
|
8
|
+
|
9
|
+
# Included in the model that stores file data.
|
10
|
+
module BlobModel
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
included do
|
14
|
+
# A cryptographic hash over the file's data.
|
15
|
+
#
|
16
|
+
# This will be an URL-safe string.
|
17
|
+
validates :id, presence: true, length: 1..48, uniqueness: true
|
18
|
+
|
19
|
+
# The raw data in the file.
|
20
|
+
validates :data, presence: true
|
21
|
+
validates_each :data do |record, attr, value|
|
22
|
+
if value && value.bytesize > 128.kilobytes
|
23
|
+
record.errors.add attr, 'exceeds 128 kilobytes'.freeze
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Class methods on models that include FileBlobsRails::BlobModel.
|
29
|
+
module ClassMethods
|
30
|
+
# The URL-safe base64-encoded SHA-256 of the given data.
|
31
|
+
#
|
32
|
+
# @param [String] blob_contents the file data to be hashed
|
33
|
+
# @return [String] a cryptographically strong hash of the given data
|
34
|
+
def id_for(blob_contents)
|
35
|
+
# This needs to be kept in sync with
|
36
|
+
# active_record_fixture_set_extensions.rb.
|
37
|
+
Base64.urlsafe_encode64 Digest::SHA256.digest(blob_contents)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Declares the model classes that use `has_file_blob`.
|
41
|
+
#
|
42
|
+
# This list is necessary for garbage collection to work correctly. If the
|
43
|
+
# class list is incomplete, a blob model may be garbage-collected
|
44
|
+
# prematurely.
|
45
|
+
#
|
46
|
+
# @param [Enumerable<String>] class_names the names of the model classes
|
47
|
+
# that include the HasFileBlob concern, and therefore point to the
|
48
|
+
# file blob model (the class that includes FileBlobsRails::BlobModel)
|
49
|
+
def blob_owner_class_names!(*class_names)
|
50
|
+
# We're using map to create a new array out of the class_list parameter,
|
51
|
+
# which can be any iterable.
|
52
|
+
#
|
53
|
+
# We're not particularly concerned with speed, because this is executed
|
54
|
+
# once in production, while the application boots. Also, the class_names
|
55
|
+
# array is expected to be small.
|
56
|
+
@_blob_owner_class_names ||=
|
57
|
+
class_names.map { |class_name| class_name.to_s }.freeze
|
58
|
+
end
|
59
|
+
|
60
|
+
# The classes that include FileBlobsRails::HasFileBlob.
|
61
|
+
#
|
62
|
+
# @return [Array<Class>] the model classes
|
63
|
+
def blob_owner_classes
|
64
|
+
@_blob_owner_classes ||= @_blob_owner_class_names.map do |class_name|
|
65
|
+
# Trigger ActiveSupport's autoloading behavior.
|
66
|
+
#
|
67
|
+
# ActiveSupport's String#constantize is pretty slow, but we're not
|
68
|
+
# particularly concerned with speed, because this is executed once in
|
69
|
+
# production, while the application boots. Also, the list of class
|
70
|
+
# names is expected to be small.
|
71
|
+
class_name.constantize
|
72
|
+
end.freeze
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Garbage-collects this blob if it is not referenced by any other model.
|
77
|
+
#
|
78
|
+
# @return [Boolean] true if the blob was garbage-collected
|
79
|
+
def maybe_garbage_collect
|
80
|
+
self.class.transaction do
|
81
|
+
if eligible_for_garbage_collection?
|
82
|
+
destroy
|
83
|
+
true
|
84
|
+
else
|
85
|
+
false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Checks if this blob can be garbage-collected.
|
91
|
+
#
|
92
|
+
# This check's result can become invalid after another Blob-owning model is
|
93
|
+
# created. To prevent data races, the check and its corresponding garbage
|
94
|
+
# collection must be done in the same database transaction.
|
95
|
+
#
|
96
|
+
# @return [Boolean] true if this blob is not referenced by any Blob-owning
|
97
|
+
# model, and thus is eligible for garbage collection
|
98
|
+
def eligible_for_garbage_collection?
|
99
|
+
self.class.blob_owner_classes.all? do |klass|
|
100
|
+
klass.file_blob_eligible_for_garbage_collection? self
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end # module FileBlobs::BlobModel
|
104
|
+
|
105
|
+
end # namespace FileBlobs
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module FileBlobs
|
2
|
+
|
3
|
+
class BlobModelGenerator < Rails::Generators::NamedBase
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
5
|
+
|
6
|
+
check_class_collision
|
7
|
+
|
8
|
+
def create_file_blob_model
|
9
|
+
template 'file_blob.rb.erb', File.join('app', 'models', "#{file_name}.rb")
|
10
|
+
template 'file_blob_test.rb.erb',
|
11
|
+
File.join('test', 'models', "#{file_name}_test.rb")
|
12
|
+
template '001_create_file_blobs.rb.erb',
|
13
|
+
File.join('db', 'migrate',
|
14
|
+
"20161029000001_create_#{file_name.tableize}.rb")
|
15
|
+
template 'file_blobs.yml.erb',
|
16
|
+
File.join('test', 'fixtures', "#{file_name.tableize}.yml")
|
17
|
+
|
18
|
+
copy_file File.join('files', 'invoice.pdf'),
|
19
|
+
File.join('test', 'fixtures', 'files', 'invoice.pdf')
|
20
|
+
copy_file File.join('files', 'ruby.png'),
|
21
|
+
File.join('test', 'fixtures', 'files', 'ruby.png')
|
22
|
+
end
|
23
|
+
end # class FileBlobs::BlobModelGenerator
|
24
|
+
|
25
|
+
end # namespace FileBlobs
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module FileBlobs
|
2
|
+
|
3
|
+
class BlobOwnerGenerator < Rails::Generators::NamedBase
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
5
|
+
|
6
|
+
check_class_collision
|
7
|
+
|
8
|
+
class_option :attr_name, type: :string, default: 'file',
|
9
|
+
desc: 'The name of the attribute pointing to the blob model'
|
10
|
+
class_option :blob_model, type: :string, default: 'FileBlob',
|
11
|
+
desc: 'The name of the model class that stores the file contents'
|
12
|
+
class_option :allow_nil, type: :boolean, default: false,
|
13
|
+
desc: "Support models that don't store files"
|
14
|
+
|
15
|
+
def create_file_blob_model
|
16
|
+
# Set up the template environment.
|
17
|
+
template 'blob_owner.rb.erb', File.join('app', 'models', "#{file_name}.rb")
|
18
|
+
template 'blob_owner_test.rb.erb',
|
19
|
+
File.join('test', 'models', "#{file_name}_test.rb")
|
20
|
+
template '002_create_blob_owners.rb.erb',
|
21
|
+
File.join('db', 'migrate',
|
22
|
+
"20161029000002_create_#{file_name.tableize}.rb")
|
23
|
+
template 'blob_owners.yml.erb',
|
24
|
+
File.join('test', 'fixtures', "#{file_name.tableize}.yml")
|
25
|
+
end
|
26
|
+
end # class FileBlobs::BlobModelGenerator
|
27
|
+
|
28
|
+
end # namespace FileBlobs
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class Create<%= file_name.classify.pluralize %> < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :<%= file_name.tableize %> do |t|
|
4
|
+
t.file_blob :<%= options[:attr_name] %>, null: <%= options[:allow_nil] %>, mime_type_limit: 64, file_name_limit: 256
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class <%= file_name.classify %>Test < ActiveSupport::TestCase
|
4
|
+
test '<%= options[:attr_name] %> is wired correctly' do
|
5
|
+
<%= file_name %> = <%= file_name.tableize %>(:ruby)
|
6
|
+
|
7
|
+
assert_equal 'image/png', <%= file_name %>.<%= options[:attr_name] %>.mime_type
|
8
|
+
assert_equal 'ruby.png', <%= file_name %>.<%= options[:attr_name] %>.original_name
|
9
|
+
assert_equal file_blob_id('files/ruby.png'), <%= file_name %>.<%= options[:attr_name] %>.blob.id
|
10
|
+
assert_equal file_blob_size('files/ruby.png'), <%= file_name %>.<%= options[:attr_name] %>.size
|
11
|
+
assert_equal file_blob_data('files/ruby.png'), <%= file_name %>.<%= options[:attr_name] %>.data
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
ruby:
|
2
|
+
<%= options[:attr_name] %>_blob_id: <%%= file_blob_id 'files/ruby.png' %>
|
3
|
+
<%= options[:attr_name] %>_size: <%%= file_blob_size 'files/ruby.png' %>
|
4
|
+
<%= options[:attr_name] %>_mime_type: image/png
|
5
|
+
<%= options[:attr_name] %>_original_name: ruby.png
|
6
|
+
|
7
|
+
invoice:
|
8
|
+
<%= options[:attr_name] %>_blob_id: <%%= file_blob_id 'files/invoice.pdf' %>
|
9
|
+
<%= options[:attr_name] %>_size: <%%= file_blob_size 'files/invoice.pdf' %>
|
10
|
+
<%= options[:attr_name] %>_mime_type: application/pdf
|
11
|
+
<%= options[:attr_name] %>_original_name: invoice.pdf
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Stores the contents of database-backed files for this application.
|
2
|
+
class <%= file_name.classify %> < ActiveRecord::Base
|
3
|
+
include FileBlobs::BlobModel
|
4
|
+
|
5
|
+
# The list below must include all the models that that use has_file_blob and
|
6
|
+
# store data using this model class. Omitting a module will lead to data
|
7
|
+
# loss, as content blobs will get garbage-collected prematurely.
|
8
|
+
blob_owner_class_names! 'BlobOwner'
|
9
|
+
|
10
|
+
# Place any extensions to the file contents blob class below.
|
11
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class <%= file_name.classify %>Test < ActiveSupport::TestCase
|
4
|
+
test 'blob_owner_classes all use has_file_blob' do
|
5
|
+
<%= file_name.camelize %>.blob_owner_classes.each do |klass|
|
6
|
+
assert klass.respond_to?(:file_blob_eligible_for_garbage_collection?), klass.name
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
%PDF-1.4
|
2
|
+
%����
|
3
|
+
5 0 obj
|
4
|
+
<<
|
5
|
+
/Type /Font
|
6
|
+
/Subtype /TrueType
|
7
|
+
/BaseFont /Arial
|
8
|
+
/Encoding /WinAnsiEncoding
|
9
|
+
>>
|
10
|
+
endobj
|
11
|
+
7 0 obj
|
12
|
+
<<
|
13
|
+
/Type /FontDescriptor
|
14
|
+
/FontName /BookAntiqua,Bold
|
15
|
+
/Flags 16418
|
16
|
+
/FontBBox [-250 -260 1236 930]
|
17
|
+
/MissingWidth 750
|
18
|
+
/StemV 146
|
19
|
+
/StemH 146
|
20
|
+
/ItalicAngle 0
|
21
|
+
/CapHeight 930
|
22
|
+
/XHeight 651
|
23
|
+
/Ascent 930
|
24
|
+
/Descent 260
|
25
|
+
/Leading 210
|
26
|
+
/MaxWidth 1030
|
27
|
+
/AvgWidth 460
|
28
|
+
>>
|
29
|
+
endobj
|
30
|
+
6 0 obj
|
31
|
+
<<
|
32
|
+
/Type /Font
|
33
|
+
/Subtype /TrueType
|
34
|
+
/BaseFont /BookAntiqua,Bold
|
35
|
+
/FirstChar 31
|
36
|
+
/LastChar 255
|
37
|
+
/Widths [750 250 278 402 606 500 889 833 227 333 333 444 606 250 333 250 296 500 500 500 500 500 500 500 500 500 500 250 250 606 606 606 444 747 778 667 722 833 611 556 833 833 389 389 778 611 1000 833 833 611 833 722 611 667 778 778 1000 667 667 667 333 606 333 606 500 333 500 611 444 611 500 389 556 611 333 333 611 333 889 611 556 611 611 389 444 333 611 556 833 500 556 500 310 606 310 606 750 500 750 333 500 500 1000 500 500 333 1000 611 389 1000 750 750 750 750 278 278 500 500 606 500 1000 333 998 444 389 833 750 750 667 250 278 500 500 606 500 606 500 333 747 438 500 606 333 747 500 400 549 361 361 333 576 641 250 333 361 488 500 889 890 889 444 778 778 778 778 778 778 1000 722 611 611 611 611 389 389 389 389 833 833 833 833 833 833 833 606 833 778 778 778 778 667 611 611 500 500 500 500 500 500 778 444 500 500 500 500 333 333 333 333 556 611 556 556 556 556 556 549 556 611 611 611 611 556 611 556]
|
38
|
+
/Encoding /WinAnsiEncoding
|
39
|
+
/FontDescriptor 7 0 R
|
40
|
+
>>
|
41
|
+
endobj
|
42
|
+
8 0 obj
|
43
|
+
[/PDF /Text]
|
44
|
+
endobj
|
45
|
+
10 0 obj
|
46
|
+
246
|
47
|
+
endobj
|
48
|
+
9 0 obj
|
49
|
+
<<
|
50
|
+
/Length 246
|
51
|
+
/Filter /FlateDecode
|
52
|
+
>>
|
53
|
+
stream
|
54
|
+
H�͐�J�0�� ��{��f�$M��n�-���[&je���ۤ�~�$���}�Ʌ�Ij���s����~�X�-],��$Y���)�'N�u�1!���V�?��?
|
55
|
+
�b1Rbb�҉�H�[��TD:#�&ح��X���i�$qnf�����]������a��{��أ���q|J�Ls]�Q�I��j�%��9��`���U�ite�z�$����OeB�Ēү�R��@zܗ���g���<���
|
56
|
+
endstream
|
57
|
+
endobj
|
58
|
+
4 0 obj
|
59
|
+
<<
|
60
|
+
/Type /Page
|
61
|
+
/MediaBox [0 0 612 792]
|
62
|
+
/Resources <<
|
63
|
+
/Font <<
|
64
|
+
/F0 5 0 R
|
65
|
+
/F1 6 0 R
|
66
|
+
>>
|
67
|
+
/ProcSet 8 0 R
|
68
|
+
>>
|
69
|
+
/Contents 9 0 R
|
70
|
+
/Parent 2 0 R
|
71
|
+
>>
|
72
|
+
endobj
|
73
|
+
11 0 obj
|
74
|
+
[/CalGray <<
|
75
|
+
/WhitePoint [0.9505 1 1.0891]
|
76
|
+
/Gamma 0.2468
|
77
|
+
>>]
|
78
|
+
endobj
|
79
|
+
12 0 obj
|
80
|
+
[/CalRGB <<
|
81
|
+
/WhitePoint [0.9505 1 1.0891]
|
82
|
+
/Gamma [0.2468 0.2468 0.2468]
|
83
|
+
/Matrix [0.4361 0.2225 0.0139 0.3851 0.7169 0.0971 0.1431 0.0606 0.7141]
|
84
|
+
>>]
|
85
|
+
endobj
|
86
|
+
2 0 obj
|
87
|
+
<<
|
88
|
+
/Type /Pages
|
89
|
+
/Kids [4 0 R]
|
90
|
+
/Count 1
|
91
|
+
>>
|
92
|
+
endobj
|
93
|
+
1 0 obj
|
94
|
+
<<
|
95
|
+
/Type /Catalog
|
96
|
+
/Pages 2 0 R
|
97
|
+
/DefaultGray 11 0 R
|
98
|
+
/DefaultRGB 12 0 R
|
99
|
+
>>
|
100
|
+
endobj
|
101
|
+
3 0 obj
|
102
|
+
<<
|
103
|
+
/Creator (1725.fm)
|
104
|
+
/CreationDate (1-Jan-3 18:15PM)
|
105
|
+
/Title (1725.PDF)
|
106
|
+
/Author (Unknown)
|
107
|
+
/Keywords <>
|
108
|
+
/Subject <>
|
109
|
+
/ModDate (D:20131124155828Z)
|
110
|
+
/Producer (3-Heights\(TM\) PDF Producer 4.2.26.0 \(http://www.pdf-tools.com\))
|
111
|
+
>>
|
112
|
+
endobj
|
113
|
+
xref
|
114
|
+
0 13
|
115
|
+
0000000000 65535 f
|
116
|
+
0000002266 00000 n
|
117
|
+
0000002209 00000 n
|
118
|
+
0000002354 00000 n
|
119
|
+
0000001816 00000 n
|
120
|
+
0000000015 00000 n
|
121
|
+
0000000376 00000 n
|
122
|
+
0000000111 00000 n
|
123
|
+
0000001450 00000 n
|
124
|
+
0000001498 00000 n
|
125
|
+
0000001478 00000 n
|
126
|
+
0000001967 00000 n
|
127
|
+
0000002044 00000 n
|
128
|
+
trailer
|
129
|
+
<<
|
130
|
+
/Size 13
|
131
|
+
/Root 1 0 R
|
132
|
+
/Info 3 0 R
|
133
|
+
/ID [<F2B22CD0880D25E8E743A676B47199CD> <5B1A04D8F6660EB95B25A6F7A746258B>]
|
134
|
+
>>
|
135
|
+
startxref
|
136
|
+
2595
|
137
|
+
%%EOF
|
Binary file
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'active_support/dependencies'
|
2
|
+
|
3
|
+
module FileBlobs
|
4
|
+
extend ActiveSupport::Autoload
|
5
|
+
|
6
|
+
autoload :BlobModel, 'file_blobs_rails/blob_model.rb'
|
7
|
+
autoload :FileBlobProxy, 'file_blobs_rails/file_blob_proxy.rb'
|
8
|
+
end # module FileBlobs
|
9
|
+
|
10
|
+
require_relative(
|
11
|
+
'file_blobs_rails/action_controller_data_streaming_extensions.rb')
|
12
|
+
require_relative 'file_blobs_rails/active_record_extensions.rb'
|
13
|
+
require_relative 'file_blobs_rails/active_record_fixture_set_extensions.rb'
|
14
|
+
require_relative 'file_blobs_rails/active_record_migration_extensions.rb'
|
15
|
+
require_relative(
|
16
|
+
'file_blobs_rails/active_record_table_definition_extensions.rb')
|
17
|
+
require_relative 'file_blobs_rails/active_support_test_extensions.rb'
|
18
|
+
|
19
|
+
if defined?(Rails)
|
20
|
+
require_relative 'file_blobs_rails/engine.rb'
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class BlobModelTest < ActiveSupport::TestCase
|
4
|
+
setup do
|
5
|
+
blob_data = 'Hello blob world!'
|
6
|
+
@blob = GemTestBlob.new data: blob_data,
|
7
|
+
id: GemTestBlob.id_for('blob_data')
|
8
|
+
end
|
9
|
+
|
10
|
+
test 'setup' do
|
11
|
+
assert @blob.valid?, @blob.errors
|
12
|
+
end
|
13
|
+
|
14
|
+
test 'requires an id' do
|
15
|
+
@blob.id = nil
|
16
|
+
assert !@blob.valid?
|
17
|
+
end
|
18
|
+
|
19
|
+
test 'requires data' do
|
20
|
+
@blob.data = nil
|
21
|
+
assert !@blob.valid?
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
# Run the tests in the generator, to make sure they pass.
|
4
|
+
template_path = File.expand_path(
|
5
|
+
'../../lib/file_blobs_rails/generators/templates/blob_owner_test.rb.erb',
|
6
|
+
__FILE__)
|
7
|
+
eval Erubis::Eruby.new(File.read(template_path)).result(
|
8
|
+
options: { attr_name: 'file', allow_nil: false, blob_model: 'FileBlob' },
|
9
|
+
file_name: 'blob_owner'), nil, template_path
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class FileBlobGemTestController < ActionController::Base
|
4
|
+
before_action :set_blob_owner, only: [:show]
|
5
|
+
|
6
|
+
# POST /file_blob_gem_test
|
7
|
+
def create
|
8
|
+
blob_owner = BlobOwner.create! blob_owner_params
|
9
|
+
render json: blob_owner
|
10
|
+
end
|
11
|
+
|
12
|
+
# GET /file_blob_gem_test
|
13
|
+
def show
|
14
|
+
send_file_blob @blob_owner.file
|
15
|
+
end
|
16
|
+
|
17
|
+
# Use callbacks to share common setup or constraints between actions.
|
18
|
+
def set_blob_owner
|
19
|
+
@blob_owner = BlobOwner.find params[:id]
|
20
|
+
end
|
21
|
+
private :set_blob_owner
|
22
|
+
|
23
|
+
# Only allow a trusted parameter "white list" through.
|
24
|
+
def blob_owner_params
|
25
|
+
params.require(:blob_owner).permit(:file)
|
26
|
+
end
|
27
|
+
private :blob_owner_params
|
28
|
+
end
|
29
|
+
|
30
|
+
class ControllerExtensionsTest < ActionController::TestCase
|
31
|
+
tests FileBlobGemTestController
|
32
|
+
|
33
|
+
# Workaround the issue that is well-documented but incorrectly addressed in
|
34
|
+
# https://github.com/rails/rails/pull/25796
|
35
|
+
# include ActionDispatch::TestProcess
|
36
|
+
|
37
|
+
test 'can read files from ActionDispatch::Http::UploadedFile' do
|
38
|
+
BlobOwner.destroy_all
|
39
|
+
FileBlob.destroy_all
|
40
|
+
|
41
|
+
post :create, params: { blob_owner: {
|
42
|
+
file: fixture_file_upload('files/ruby.png', 'image/png', :binary) } }
|
43
|
+
|
44
|
+
assert_response :success
|
45
|
+
|
46
|
+
assert_equal 1, BlobOwner.count
|
47
|
+
|
48
|
+
blob_owner = BlobOwner.last
|
49
|
+
assert_equal 'image/png', blob_owner.file.mime_type
|
50
|
+
assert_equal 'ruby.png', blob_owner.file.original_name
|
51
|
+
assert_equal file_blob_size('files/ruby.png'), blob_owner.file.size
|
52
|
+
assert_equal file_blob_data('files/ruby.png'), blob_owner.file.data
|
53
|
+
end
|
54
|
+
|
55
|
+
test '#send_file_blob generates a fresh response correctly' do
|
56
|
+
get :show, params: { id: blob_owners(:ruby).id }
|
57
|
+
|
58
|
+
assert_response :success
|
59
|
+
assert_equal 'image/png', response.headers['Content-Type']
|
60
|
+
assert_equal file_blob_id('files/ruby.png'), response.headers['ETag']
|
61
|
+
assert_equal file_blob_data('files/ruby.png'), response.body
|
62
|
+
end
|
63
|
+
|
64
|
+
test '#send_file_blob sends 200 for requests with non-matching e-tags' do
|
65
|
+
request.set_header 'HTTP_IF_NONE_MATCH', '1234'
|
66
|
+
get :show, params: { id: blob_owners(:ruby).id }
|
67
|
+
|
68
|
+
assert_response :success
|
69
|
+
assert_equal 'image/png', response.headers['Content-Type']
|
70
|
+
assert_equal file_blob_id('files/ruby.png'), response.headers['ETag']
|
71
|
+
assert_equal file_blob_data('files/ruby.png'), response.body
|
72
|
+
end
|
73
|
+
|
74
|
+
test '#send_file_blob sends 304 for requests with matching e-tags' do
|
75
|
+
request.set_header 'HTTP_IF_NONE_MATCH', file_blob_id('files/ruby.png')
|
76
|
+
get :show, params: { id: blob_owners(:ruby).id }
|
77
|
+
|
78
|
+
assert_response :not_modified
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class FileBlobProxyTest < ActiveSupport::TestCase
|
4
|
+
setup do
|
5
|
+
@blob_owner = blob_owners(:ruby)
|
6
|
+
@message = GemTestMessage.new
|
7
|
+
end
|
8
|
+
|
9
|
+
test '#blob_class' do
|
10
|
+
assert_equal FileBlob, @blob_owner.file.blob_class
|
11
|
+
assert_equal GemTestBlob, @message.attachment.blob_class
|
12
|
+
end
|
13
|
+
|
14
|
+
test '#allows_nil?' do
|
15
|
+
assert_equal false, @blob_owner.file.allows_nil?
|
16
|
+
assert_equal true, @message.attachment.allows_nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
test '#owner' do
|
20
|
+
assert_equal @blob_owner, @blob_owner.file.owner
|
21
|
+
assert_equal @message, @message.attachment.owner
|
22
|
+
end
|
23
|
+
|
24
|
+
test 'proxy getter on the owner model' do
|
25
|
+
proxy = @blob_owner.file
|
26
|
+
|
27
|
+
assert_kind_of FileBlobs::FileBlobProxy, proxy
|
28
|
+
end
|
29
|
+
|
30
|
+
test 'getter proxies' do
|
31
|
+
proxy = @blob_owner.file
|
32
|
+
|
33
|
+
assert_equal 'image/png', proxy.mime_type
|
34
|
+
assert_equal 'ruby.png', proxy.original_name
|
35
|
+
assert_equal file_blob_id('files/ruby.png'), proxy.blob_id
|
36
|
+
assert_equal file_blob_size('files/ruby.png'), proxy.size
|
37
|
+
assert_equal file_blob_data('files/ruby.png'), proxy.data
|
38
|
+
assert_equal file_blobs(:ruby_png), proxy.blob
|
39
|
+
end
|
40
|
+
|
41
|
+
test '#mime_type=' do
|
42
|
+
proxy = @blob_owner.file
|
43
|
+
proxy.mime_type = 'x-test/x-setter'
|
44
|
+
|
45
|
+
assert_equal 'x-test/x-setter', proxy.mime_type
|
46
|
+
assert_equal 'ruby.png', proxy.original_name
|
47
|
+
assert_equal file_blob_id('files/ruby.png'), proxy.blob_id
|
48
|
+
assert_equal file_blob_size('files/ruby.png'), proxy.size
|
49
|
+
assert_equal file_blob_data('files/ruby.png'), proxy.data
|
50
|
+
assert_equal file_blobs(:ruby_png), proxy.blob
|
51
|
+
end
|
52
|
+
|
53
|
+
test '#original_name=' do
|
54
|
+
proxy = @blob_owner.file
|
55
|
+
proxy.original_name = 'setter-test.png'
|
56
|
+
|
57
|
+
assert_equal 'image/png', proxy.mime_type
|
58
|
+
assert_equal 'setter-test.png', proxy.original_name
|
59
|
+
assert_equal file_blob_id('files/ruby.png'), proxy.blob_id
|
60
|
+
assert_equal file_blob_size('files/ruby.png'), proxy.size
|
61
|
+
assert_equal file_blob_data('files/ruby.png'), proxy.data
|
62
|
+
assert_equal file_blobs(:ruby_png), proxy.blob
|
63
|
+
end
|
64
|
+
|
65
|
+
test '#data=' do
|
66
|
+
proxy = @blob_owner.file
|
67
|
+
proxy.data = file_blob_data('files/invoice.pdf')
|
68
|
+
|
69
|
+
assert_equal 'image/png', proxy.mime_type
|
70
|
+
assert_equal 'ruby.png', proxy.original_name
|
71
|
+
assert_equal file_blob_id('files/invoice.pdf'), proxy.blob_id
|
72
|
+
assert_equal file_blob_size('files/invoice.pdf'), proxy.size
|
73
|
+
assert_equal file_blob_data('files/invoice.pdf'), proxy.data
|
74
|
+
assert_equal file_blobs(:invoice_pdf), proxy.blob
|
75
|
+
end
|
76
|
+
|
77
|
+
test 'proxy setter on the owner model with same-table blobs' do
|
78
|
+
new_blob_owner = BlobOwner.new file: @blob_owner.file
|
79
|
+
|
80
|
+
assert_equal 'ruby.png', new_blob_owner.file_original_name
|
81
|
+
assert_equal file_blob_id('files/ruby.png'), new_blob_owner.file_blob_id
|
82
|
+
assert_equal file_blob_size('files/ruby.png'), new_blob_owner.file_size
|
83
|
+
assert_equal file_blob_data('files/ruby.png'), new_blob_owner.file_data
|
84
|
+
assert_equal file_blobs(:ruby_png), new_blob_owner.file_blob
|
85
|
+
end
|
86
|
+
|
87
|
+
test 'proxy setter on the owner model with different-table blobs' do
|
88
|
+
@message.attachment = @blob_owner.file
|
89
|
+
|
90
|
+
assert_equal 'ruby.png', @message.attachment_original_name
|
91
|
+
assert_equal file_blob_id('files/ruby.png'), @message.attachment_blob_id
|
92
|
+
assert_equal file_blob_size('files/ruby.png'), @message.attachment_size
|
93
|
+
|
94
|
+
assert_instance_of GemTestBlob, @message.attachment_blob
|
95
|
+
assert_equal file_blob_id('files/ruby.png'), @message.attachment_blob.id
|
96
|
+
assert_equal file_blob_data('files/ruby.png'),
|
97
|
+
@message.attachment_blob.data
|
98
|
+
assert_equal true, @message.attachment_blob.new_record?
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
# Run the tests in the generator, to make sure they pass.
|
4
|
+
template_path = File.expand_path(
|
5
|
+
'../../lib/file_blobs_rails/generators/templates/file_blob_test.rb.erb',
|
6
|
+
__FILE__)
|
7
|
+
eval Erubis::Eruby.new(File.read(template_path)).result(
|
8
|
+
file_name: 'file_blob'), nil, template_path
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class FileBlobsFixtureTest < ActiveSupport::TestCase
|
4
|
+
setup do
|
5
|
+
@ruby = FileBlob.where(id: file_blob_id('files/ruby.png')).first
|
6
|
+
@invoice = FileBlob.where(id: file_blob_id('files/invoice.pdf')).first
|
7
|
+
|
8
|
+
@ruby_path = File.expand_path '../fixtures/files/ruby.png', __FILE__
|
9
|
+
@invoice_path = File.expand_path '../fixtures/files/invoice.pdf', __FILE__
|
10
|
+
end
|
11
|
+
|
12
|
+
test 'ruby fixture' do
|
13
|
+
assert @ruby
|
14
|
+
assert_equal file_blob_data('files/ruby.png'), @ruby.data
|
15
|
+
end
|
16
|
+
|
17
|
+
test 'invoice fixture' do
|
18
|
+
assert @invoice
|
19
|
+
assert_equal file_blob_data('files/invoice.pdf'), @invoice.data
|
20
|
+
end
|
21
|
+
end
|