file_blobs_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.travis.yml +16 -0
  4. data/Gemfile +18 -0
  5. data/Gemfile.lock +397 -0
  6. data/Gemfile.rails5 +18 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.md +62 -0
  9. data/Rakefile +38 -0
  10. data/VERSION +1 -0
  11. data/file_blobs_rails.gemspec +131 -0
  12. data/lib/file_blobs_rails/action_controller_data_streaming_extensions.rb +60 -0
  13. data/lib/file_blobs_rails/active_record_extensions.rb +257 -0
  14. data/lib/file_blobs_rails/active_record_fixture_set_extensions.rb +58 -0
  15. data/lib/file_blobs_rails/active_record_migration_extensions.rb +31 -0
  16. data/lib/file_blobs_rails/active_record_table_definition_extensions.rb +35 -0
  17. data/lib/file_blobs_rails/active_support_test_extensions.rb +44 -0
  18. data/lib/file_blobs_rails/blob_model.rb +105 -0
  19. data/lib/file_blobs_rails/engine.rb +10 -0
  20. data/lib/file_blobs_rails/file_blob_proxy.rb +7 -0
  21. data/lib/file_blobs_rails/generators/blob_model_generator.rb +25 -0
  22. data/lib/file_blobs_rails/generators/blob_owner_generator.rb +28 -0
  23. data/lib/file_blobs_rails/generators/templates/001_create_file_blobs.rb.erb +7 -0
  24. data/lib/file_blobs_rails/generators/templates/002_create_blob_owners.rb.erb +7 -0
  25. data/lib/file_blobs_rails/generators/templates/blob_owner.rb.erb +3 -0
  26. data/lib/file_blobs_rails/generators/templates/blob_owner_test.rb.erb +13 -0
  27. data/lib/file_blobs_rails/generators/templates/blob_owners.yml.erb +11 -0
  28. data/lib/file_blobs_rails/generators/templates/file_blob.rb.erb +11 -0
  29. data/lib/file_blobs_rails/generators/templates/file_blob_test.rb.erb +9 -0
  30. data/lib/file_blobs_rails/generators/templates/file_blobs.yml.erb +7 -0
  31. data/lib/file_blobs_rails/generators/templates/files/invoice.pdf +137 -0
  32. data/lib/file_blobs_rails/generators/templates/files/ruby.png +0 -0
  33. data/lib/file_blobs_rails.rb +21 -0
  34. data/test/blob_model_test.rb +23 -0
  35. data/test/blob_owner_test.rb +9 -0
  36. data/test/controller_extensions_test.rb +80 -0
  37. data/test/file_blob_proxy_test.rb +100 -0
  38. data/test/file_blob_test.rb +8 -0
  39. data/test/file_blobs_fixture_test.rb +21 -0
  40. data/test/fixtures/003_create_gem_test_blobs.rb +8 -0
  41. data/test/fixtures/004_create_gem_test_messages.rb +15 -0
  42. data/test/fixtures/files/invoice.pdf +137 -0
  43. data/test/fixtures/files/ruby.png +0 -0
  44. data/test/fixtures/gem_test_blob.rb +5 -0
  45. data/test/fixtures/gem_test_message.rb +5 -0
  46. data/test/garbage_collection_test.rb +84 -0
  47. data/test/helpers/action_controller.rb +5 -0
  48. data/test/helpers/db_setup.rb +22 -0
  49. data/test/helpers/fixtures.rb +41 -0
  50. data/test/helpers/i18n.rb +1 -0
  51. data/test/helpers/migrations.rb +41 -0
  52. data/test/helpers/rails.rb +9 -0
  53. data/test/helpers/routes.rb +18 -0
  54. data/test/helpers/test_order.rb +1 -0
  55. data/test/test_extensions_test.rb +21 -0
  56. data/test/test_helper.rb +36 -0
  57. 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,10 @@
1
+ module FileBlobs
2
+
3
+ class Engine < Rails::Engine
4
+ generators do
5
+ require_relative 'generators/blob_model_generator.rb'
6
+ require_relative 'generators/blob_owner_generator.rb'
7
+ end
8
+ end # class FileBlobs::Engine
9
+
10
+ end # namespace FileBlobs
@@ -0,0 +1,7 @@
1
+ module FileBlobs
2
+
3
+ # Base class for the proxies generated by has_file_blob.
4
+ class FileBlobProxy
5
+ end # module FileBlobs::FileBlobProxy
6
+
7
+ 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_file_blobs_table :<%= file_name.tableize %>, blob_limit: 1.megabyte do |t|
4
+ # Build any custom table structure here.
5
+ end
6
+ end
7
+ end
@@ -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,3 @@
1
+ class <%= file_name.classify %> < ActiveRecord::Base
2
+ has_file_blob :<%= options[:attr_name] %>, allows_nil: <%= options[:allow_nil] %>, blob_model: <%= options[:blob_model].classify.inspect %>
3
+ 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,7 @@
1
+ ruby_png:
2
+ id: <%%= file_blob_id 'files/ruby.png' %>
3
+ data: <%%= file_blob_data 'files/ruby.png' %>
4
+
5
+ invoice_pdf:
6
+ id: <%%= file_blob_id 'files/invoice.pdf' %>
7
+ data: <%%= file_blob_data 'files/invoice.pdf' %>
@@ -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
@@ -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