file_blobs_rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,8 @@
1
+ class CreateGemTestBlobs < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_file_blobs_table :gem_test_blobs, blob_limit: 64.kilobytes do |t|
4
+ # Test that the block is invoked with the correct argument.
5
+ t.timestamps
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ class CreateGemTestMessages < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_file_blobs_table :gem_test_messages, blob_limit: 64.kilobytes do |t|
4
+ # Test the options.
5
+ t.file_blob :attachment, null: true, mime_type_limit: 16,
6
+ original_name_limit: 64
7
+
8
+ t.file_blob :signature, null: true
9
+
10
+ # Test that the block is invoked with the correct argument.
11
+ t.string :subject, null: false, limit: 64
12
+ t.timestamps
13
+ end
14
+ end
15
+ 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,5 @@
1
+ class GemTestBlob < ActiveRecord::Base
2
+ include FileBlobs::BlobModel
3
+
4
+ blob_owner_class_names! 'GemTestMessage'
5
+ end
@@ -0,0 +1,5 @@
1
+ class GemTestMessage < ActiveRecord::Base
2
+ has_file_blob :attachment, blob_model: 'GemTestBlob', allow_nil: true
3
+
4
+ has_file_blob :signature, blob_model: 'GemTestBlob', allow_nil: true
5
+ end
@@ -0,0 +1,84 @@
1
+ require_relative 'test_helper'
2
+
3
+ class BlobModelTest < ActiveSupport::TestCase
4
+ test 'new blob with no reference gets garbage-collected' do
5
+ BlobOwner.destroy_all
6
+ FileBlob.destroy_all
7
+
8
+ blob = FileBlob.create! data: 'hello world',
9
+ id: FileBlob.id_for('hello world')
10
+
11
+ assert_equal true,
12
+ BlobOwner.file_blob_eligible_for_garbage_collection?(blob)
13
+
14
+ assert_equal true, blob.eligible_for_garbage_collection?
15
+
16
+ assert_difference -> { FileBlob.count }, -1 do
17
+ assert_equal true, blob.maybe_garbage_collect
18
+ end
19
+ assert_equal nil, FileBlob.where(id: blob.id).first
20
+ end
21
+
22
+ test 'blob with a reference does not get garbage-collected' do
23
+ blob = file_blobs(:ruby_png)
24
+
25
+ assert_equal false,
26
+ BlobOwner.file_blob_eligible_for_garbage_collection?(blob)
27
+ assert_equal false, blob.eligible_for_garbage_collection?
28
+ assert_no_difference -> { FileBlob.count } do
29
+ assert_equal false, blob.maybe_garbage_collect
30
+ end
31
+ end
32
+
33
+ test 'blob with one owner gets collected when owner gets destroyed' do
34
+ owner = blob_owners(:ruby)
35
+ blob = file_blobs(:ruby_png)
36
+
37
+ assert_difference -> { FileBlob.count }, -1 do
38
+ owner.destroy
39
+ end
40
+
41
+ assert_equal nil, FileBlob.where(id: blob.id).first
42
+ end
43
+
44
+ test 'blob with two owners is not collected when an owner gets destroyed' do
45
+ owner = blob_owners(:ruby)
46
+ new_owner = BlobOwner.create! file: owner.file
47
+ assert_equal owner.file_blob, new_owner.file_blob, 'Incorrect test setup'
48
+
49
+ blob = file_blobs(:ruby_png)
50
+
51
+ assert_no_difference -> { FileBlob.count } do
52
+ owner.destroy
53
+ end
54
+
55
+ assert_equal blob, FileBlob.where(id: blob.id).first
56
+ end
57
+
58
+ test 'blob with a reference gets garbage-collected when reference changes' do
59
+ owner = blob_owners(:ruby)
60
+ dead_blob = file_blobs(:ruby_png)
61
+ live_blob = file_blobs(:invoice_pdf)
62
+
63
+ owner.file_blob = live_blob
64
+
65
+ # First check that the blob doesn't get garbage-collected until the owner
66
+ # gets saved.
67
+ assert_equal false,
68
+ BlobOwner.file_blob_eligible_for_garbage_collection?(dead_blob)
69
+ assert_equal false, dead_blob.eligible_for_garbage_collection?
70
+ assert_no_difference -> { FileBlob.count } do
71
+ assert_equal false, dead_blob.maybe_garbage_collect
72
+ end
73
+
74
+ # Now check that the blob gets garbage-collected when the reference change
75
+ # is committed.
76
+
77
+ assert_difference -> { FileBlob.count }, -1 do
78
+ owner.save!
79
+ end
80
+
81
+ assert_equal nil, FileBlob.where(id: dead_blob.id).first
82
+ assert_equal live_blob, FileBlob.where(id: live_blob.id).first
83
+ end
84
+ end
@@ -0,0 +1,5 @@
1
+ # Raise exceptions so we can test require / permit on params.
2
+ ActionController::Parameters.action_on_unpermitted_parameters = :raise
3
+
4
+ # By default, CSRF protection is turned off in tests.
5
+ ActionController::Base.allow_forgery_protection = false
@@ -0,0 +1,22 @@
1
+ ar_config = { adapter: 'sqlite3', database: ':memory:'}
2
+
3
+ case ENV['DB']
4
+ when /mysql/i
5
+ create_sql = 'CREATE DATABASE plugin_dev DEFAULT CHARACTER SET utf8;'
6
+ if /:(.*)$/ =~ ENV['DB']
7
+ create_sql.sub! ';', " DEFAULT COLLATE #{$1};"
8
+ end
9
+
10
+ `mysql -u root -e "DROP DATABASE IF EXISTS plugin_dev; #{create_sql}"`
11
+ ar_config = { adapter: 'mysql2', database: 'plugin_dev',
12
+ username: 'root', password: '' }
13
+ when /pg/i
14
+ pg_user = ENV['DB_USER'] || ENV['USER']
15
+ `psql -U #{pg_user} -d postgres -c "DROP DATABASE IF EXISTS plugin_dev;"`
16
+ `psql -U #{pg_user} -d postgres -c "CREATE DATABASE plugin_dev;"`
17
+ ar_config = { adapter: 'postgresql', database: 'plugin_dev',
18
+ username: pg_user, password: '' }
19
+ end
20
+
21
+ ActiveRecord::Base.configurations = { 'test' => ar_config }
22
+ ActiveRecord::Base.establish_connection :test
@@ -0,0 +1,41 @@
1
+ require 'fileutils'
2
+
3
+ # Simulate generating the fixtures.
4
+
5
+ FileUtils.mkdir_p File.expand_path('../../../tmp/files', __FILE__)
6
+
7
+ template_path = File.expand_path(
8
+ '../../../lib/file_blobs_rails/generators/templates/file_blobs.yml.erb',
9
+ __FILE__)
10
+ output = Erubis::Eruby.new(File.read(template_path)).result(
11
+ file_name: 'file_blob')
12
+ File.write File.expand_path('../../../tmp/file_blobs.yml', __FILE__), output
13
+
14
+ template_path = File.expand_path(
15
+ '../../../lib/file_blobs_rails/generators/templates/blob_owners.yml.erb',
16
+ __FILE__)
17
+ output = Erubis::Eruby.new(File.read(template_path)).result(
18
+ file_name: 'blob_owner', options: { allow_nil: false, attr_name: 'file',
19
+ blob_model: 'FileBlob' })
20
+ File.write File.expand_path('../../../tmp/blob_owners.yml', __FILE__), output
21
+
22
+ FileUtils.cp File.expand_path(
23
+ '../../../lib/file_blobs_rails/generators/templates/files/invoice.pdf',
24
+ __FILE__), File.expand_path('../../../tmp/files', __FILE__)
25
+ FileUtils.cp File.expand_path(
26
+ '../../../lib/file_blobs_rails/generators/templates/files/ruby.png',
27
+ __FILE__), File.expand_path('../../../tmp/files', __FILE__)
28
+
29
+
30
+ # This needs to happen after the fixture files are created.
31
+
32
+ class ActiveSupport::TestCase
33
+ include ActiveRecord::TestFixtures
34
+
35
+ self.fixture_path = File.expand_path('../../../tmp', __FILE__)
36
+
37
+ self.use_transactional_tests = true
38
+ self.use_instantiated_fixtures = false
39
+ self.pre_loaded_fixtures = false
40
+ fixtures :all
41
+ end
@@ -0,0 +1 @@
1
+ I18n.config.enforce_available_locales = true
@@ -0,0 +1,41 @@
1
+ ActiveRecord::Migration.verbose = false
2
+
3
+ # Simulate Rails' db:migrate.
4
+
5
+ template = File.read File.expand_path(
6
+ '../../../lib/file_blobs_rails/generators/templates/001_create_file_blobs.rb.erb',
7
+ __FILE__)
8
+ eval Erubis::Eruby.new(template).result(file_name: 'file_blob')
9
+ CreateFileBlobs.migrate :up
10
+
11
+ template = File.read File.expand_path(
12
+ '../../../lib/file_blobs_rails/generators/templates/002_create_blob_owners.rb.erb',
13
+ __FILE__)
14
+ eval Erubis::Eruby.new(template).result(file_name: 'blob_owner',
15
+ options: { allow_nil: false, attr_name: 'file', blob_model: 'FileBlob' })
16
+ CreateBlobOwners.migrate :up
17
+
18
+ require_relative(
19
+ '../fixtures/003_create_gem_test_blobs.rb')
20
+ CreateGemTestBlobs.migrate :up
21
+ require_relative(
22
+ '../fixtures/004_create_gem_test_messages.rb')
23
+ CreateGemTestMessages.migrate :up
24
+
25
+ # Simulate Rails' autoloading.
26
+
27
+ template_path = File.expand_path(
28
+ '../../../lib/file_blobs_rails/generators/templates/file_blob.rb.erb',
29
+ __FILE__)
30
+ eval Erubis::Eruby.new(File.read(template_path)).result(
31
+ file_name: 'file_blob'), nil, template_path
32
+
33
+ template_path = File.expand_path(
34
+ '../../../lib/file_blobs_rails/generators/templates/blob_owner.rb.erb',
35
+ __FILE__)
36
+ eval Erubis::Eruby.new(File.read(template_path)).result(
37
+ file_name: 'blob_owner', options: { allow_nil: false, attr_name: 'file',
38
+ blob_model: 'FileBlob' }), nil, template_path
39
+
40
+ require_relative '../fixtures/gem_test_blob.rb'
41
+ require_relative '../fixtures/gem_test_message.rb'
@@ -0,0 +1,9 @@
1
+ # Mock Rails.root.
2
+ module Rails
3
+ class <<self
4
+ remove_method :root if Rails.respond_to? :root
5
+ def root
6
+ Pathname.new File.expand_path('../..', File.dirname(__FILE__))
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ def setup_file_blobs_routes
2
+ # The routes used in all the tests.
3
+ routes = ActionDispatch::Routing::RouteSet.new
4
+ routes.draw do
5
+ get '/file_blob_gem_test/:id' => 'file_blob_gem_test#show'
6
+ post '/file_blob_gem_test' => 'file_blob_gem_test#create'
7
+ end
8
+
9
+ # NOTE: ActionController tests expect @routes to be set to the drawn routes.
10
+ # We use the block form of define_method to capture the routes local
11
+ # variable.
12
+ ActionController::TestCase.send :define_method, :setup_file_blobs_routes do
13
+ @routes = routes
14
+ end
15
+ ActionController::TestCase.setup :setup_file_blobs_routes
16
+ end
17
+
18
+ setup_file_blobs_routes
@@ -0,0 +1 @@
1
+ ActiveSupport.test_order = :sorted
@@ -0,0 +1,21 @@
1
+ require_relative 'test_helper'
2
+
3
+ class TestExtensionsTest < ActiveSupport::TestCase
4
+ setup do
5
+ @ruby_path = File.expand_path '../fixtures/files/ruby.png', __FILE__
6
+ @invoice_path = File.expand_path '../fixtures/files/invoice.pdf', __FILE__
7
+ end
8
+
9
+ test 'file_blob_data' do
10
+ assert_equal File.binread(@ruby_path), file_blob_data('files/ruby.png')
11
+ assert_equal File.binread(@invoice_path),
12
+ file_blob_data('files/invoice.pdf')
13
+ end
14
+
15
+ test 'file_blob_id' do
16
+ assert_equal 'udXLga6YgZX7TR__OGt6NGw2u4ulhODFWndwUlwPoXU=',
17
+ file_blob_id('files/ruby.png')
18
+ assert_equal '30aM5cShJnhiJJCLahmhdWjk31Cq9d7hKoIHiEf2VbM=',
19
+ file_blob_id('files/invoice.pdf')
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+
11
+ require 'minitest'
12
+
13
+ require 'action_controller'
14
+ require 'active_record'
15
+ require 'active_support/core_ext'
16
+ require 'rails'
17
+
18
+ require 'mocha/setup'
19
+
20
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
21
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
22
+ require 'file_blobs_rails'
23
+
24
+ require 'helpers/action_controller.rb'
25
+ require 'helpers/db_setup.rb'
26
+ require 'helpers/fixtures.rb'
27
+ require 'helpers/i18n.rb'
28
+ require 'helpers/migrations.rb'
29
+ require 'helpers/rails.rb'
30
+ require 'helpers/routes.rb'
31
+ require 'helpers/test_order.rb'
32
+
33
+ class MiniTest::Test
34
+ end
35
+
36
+ require 'minitest/autorun'