tessa 2.0 → 6.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -18
  3. data/app/assets/javascripts/tessa.esm.js +212 -173
  4. data/app/assets/javascripts/tessa.js +206 -167
  5. data/app/javascript/activestorage/file_checksum.ts +76 -0
  6. data/app/javascript/tessa/index.ts +264 -0
  7. data/app/javascript/tessa/types.ts +34 -0
  8. data/config/routes.rb +0 -1
  9. data/lib/tessa/simple_form/asset_input.rb +24 -25
  10. data/lib/tessa/version.rb +1 -1
  11. data/lib/tessa.rb +0 -80
  12. data/package.json +7 -2
  13. data/rollup.config.js +5 -5
  14. data/spec/dummy/app/models/single_asset_model.rb +1 -1
  15. data/spec/dummy/app/models/single_asset_model_form.rb +3 -5
  16. data/spec/dummy/bin/rails +2 -2
  17. data/spec/dummy/bin/rake +2 -2
  18. data/spec/dummy/bin/setup +14 -6
  19. data/spec/dummy/bin/yarn +9 -3
  20. data/spec/dummy/config/application.rb +11 -9
  21. data/spec/dummy/config/boot.rb +1 -1
  22. data/spec/dummy/config/environment.rb +1 -1
  23. data/spec/dummy/config/environments/development.rb +34 -5
  24. data/spec/dummy/config/environments/production.rb +49 -10
  25. data/spec/dummy/config/environments/test.rb +28 -12
  26. data/spec/dummy/config/initializers/backtrace_silencers.rb +4 -3
  27. data/spec/dummy/config/initializers/content_security_policy.rb +5 -0
  28. data/spec/dummy/config/initializers/filter_parameter_logging.rb +3 -1
  29. data/spec/dummy/config/initializers/new_framework_defaults_6_1.rb +67 -0
  30. data/spec/dummy/config/initializers/permissions_policy.rb +11 -0
  31. data/spec/dummy/config/initializers/wrap_parameters.rb +5 -0
  32. data/spec/dummy/config/locales/en.yml +1 -1
  33. data/spec/dummy/config/routes.rb +1 -1
  34. data/spec/dummy/config/storage.yml +31 -0
  35. data/spec/dummy/config.ru +2 -1
  36. data/spec/dummy/db/migrate/20230406194400_add_service_name_to_active_storage_blobs.active_storage.rb +22 -0
  37. data/spec/dummy/db/migrate/20230406194401_create_active_storage_variant_records.active_storage.rb +27 -0
  38. data/spec/dummy/db/schema.rb +15 -7
  39. data/tessa.gemspec +4 -5
  40. data/tsconfig.json +10 -0
  41. data/yarn.lock +74 -7
  42. metadata +36 -74
  43. data/app/javascript/activestorage/file_checksum.js +0 -53
  44. data/app/javascript/tessa/index.js.coffee +0 -183
  45. data/lib/tasks/tessa.rake +0 -18
  46. data/lib/tessa/active_storage/asset_wrapper.rb +0 -32
  47. data/lib/tessa/asset/failure.rb +0 -37
  48. data/lib/tessa/asset.rb +0 -47
  49. data/lib/tessa/asset_change.rb +0 -49
  50. data/lib/tessa/asset_change_set.rb +0 -49
  51. data/lib/tessa/config.rb +0 -16
  52. data/lib/tessa/controller_helpers.rb +0 -16
  53. data/lib/tessa/fake_connection.rb +0 -29
  54. data/lib/tessa/jobs/migrate_assets_job.rb +0 -222
  55. data/lib/tessa/model/dynamic_extensions.rb +0 -145
  56. data/lib/tessa/model/field.rb +0 -77
  57. data/lib/tessa/model.rb +0 -94
  58. data/lib/tessa/rack_upload_proxy.rb +0 -41
  59. data/lib/tessa/response_factory.rb +0 -15
  60. data/lib/tessa/upload/uploads_file.rb +0 -18
  61. data/lib/tessa/upload.rb +0 -31
  62. data/lib/tessa/view_helpers.rb +0 -23
  63. data/spec/dummy/app/models/multiple_asset_model.rb +0 -8
  64. data/spec/support/remote_call_macro.rb +0 -40
  65. data/spec/tessa/asset/failure_spec.rb +0 -48
  66. data/spec/tessa/asset_change_set_spec.rb +0 -198
  67. data/spec/tessa/asset_change_spec.rb +0 -86
  68. data/spec/tessa/asset_spec.rb +0 -196
  69. data/spec/tessa/config_spec.rb +0 -70
  70. data/spec/tessa/controller_helpers_spec.rb +0 -55
  71. data/spec/tessa/jobs/migrate_assets_job_spec.rb +0 -247
  72. data/spec/tessa/model_field_spec.rb +0 -72
  73. data/spec/tessa/model_spec.rb +0 -325
  74. data/spec/tessa/rack_upload_proxy_spec.rb +0 -83
  75. data/spec/tessa/upload/uploads_file_spec.rb +0 -72
  76. data/spec/tessa/upload_spec.rb +0 -125
  77. data/spec/tessa_spec.rb +0 -23
@@ -1,196 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Tessa::Asset do
4
- subject(:asset) { described_class.new(args) }
5
- let(:args) {
6
- {
7
- id: 123,
8
- status: 'completed',
9
- strategy: 'default',
10
- meta: { name: "foo" },
11
- public_url: "http://example.com/public",
12
- private_url: "http://example.com/private",
13
- private_download_url: "http://example.com/private?download",
14
- delete_url: "http://example.com/delete",
15
- }
16
- }
17
-
18
- describe "#initialize" do
19
- context "with all arguments" do
20
- it "sets id to attribute" do
21
- expect(asset.id).to eq(args[:id])
22
- end
23
-
24
- it "sets status to attribute" do
25
- expect(asset.status).to eq(args[:status])
26
- end
27
-
28
- it "sets strategy to attribute" do
29
- expect(asset.strategy).to eq(args[:strategy])
30
- end
31
-
32
- it "sets meta to attribute" do
33
- expect(asset.meta).to eq(args[:meta])
34
- end
35
-
36
- it "sets public_url to attribute" do
37
- expect(asset.public_url).to eq(args[:public_url])
38
- end
39
-
40
- it "sets private_url to attribute" do
41
- expect(asset.private_url).to eq(args[:private_url])
42
- end
43
-
44
- it "sets private_download_url to attribute" do
45
- expect(asset.private_download_url).to eq(args[:private_download_url])
46
- end
47
-
48
- it "sets delete_url to attribute" do
49
- expect(asset.delete_url).to eq(args[:delete_url])
50
- end
51
-
52
- end
53
- end
54
-
55
- describe "#complete!" do
56
- subject(:call) { asset.complete!(call_args) }
57
-
58
- include_examples "remote call macro", :patch, "/assets/123/completed", Tessa::Asset
59
- end
60
-
61
- describe "#cancel!" do
62
- subject(:call) { asset.cancel!(call_args) }
63
-
64
- include_examples "remote call macro", :patch, "/assets/123/cancelled", Tessa::Asset
65
- end
66
-
67
- describe "#delete!" do
68
- subject(:call) { asset.delete!(call_args) }
69
-
70
- include_examples "remote call macro", :delete, "/assets/123", Tessa::Asset
71
- end
72
-
73
- describe "#failure?" do
74
- it "returns false" do
75
- expect(asset.failure?).to be(false)
76
- end
77
- end
78
-
79
- describe ".find" do
80
- let(:faraday_stubs) {
81
- Faraday::Adapter::Test::Stubs.new do |stub|
82
- stub.get("/assets/#{id_query}") { |env| [200, {}, remote_response.to_json] }
83
- end
84
- }
85
- let(:connection) { Faraday.new { |f| f.adapter :test, faraday_stubs } }
86
-
87
- context "with a single id" do
88
- let(:id_query) { "ID" }
89
- let(:remote_response) { {} }
90
-
91
- it "calls get('/assets/ID') on connection" do
92
- expect(connection).to receive(:get).with("/assets/ID").and_call_original
93
- described_class.find("ID", connection: connection)
94
- end
95
-
96
- it "returns an instance of Asset" do
97
- response = described_class.find("ID", connection: connection)
98
- expect(response).to be_a(described_class)
99
- end
100
-
101
- it "defaults connection to Tessa.config.connection" do
102
- expect(Tessa.config).to receive(:connection).and_return(connection)
103
- expect(connection).to receive(:get).and_call_original
104
- described_class.find("ID")
105
- end
106
-
107
- context "with attributes in response" do
108
- subject(:response) { described_class.find("ID", connection: connection) }
109
- let(:remote_response) { args }
110
-
111
- it "sets attributes on models" do
112
- expect(response.id).to eq(args[:id])
113
- expect(response.status).to eq(args[:status])
114
- expect(response.meta).to eq(args[:meta])
115
- end
116
- end
117
- end
118
-
119
- context "with multiple ids" do
120
- subject(:response) {
121
- described_class.find("ID1", "ID2", connection: connection)
122
- }
123
- let(:remote_response) { [{}] }
124
- let(:id_query) { "ID1,ID2" }
125
-
126
- it "calls get('/assets/ID1,ID2') on connection" do
127
- expect(connection).to receive(:get).with("/assets/ID1,ID2").and_call_original
128
- response
129
- end
130
-
131
- it "returns instances of Asset" do
132
- expect(response).to all(be_a(described_class))
133
- end
134
-
135
- context "with attributes in response" do
136
- let(:remote_response) { [args, args] }
137
-
138
- it "sets attributes on models" do
139
- expect(response[0].id).to eq(args[:id])
140
- expect(response[1].id).to eq(args[:id])
141
- end
142
- end
143
- end
144
- end
145
-
146
- describe ".create" do
147
- let(:upload) { instance_double(Tessa::Upload) }
148
-
149
- before :each do
150
- allow(Tessa::Upload).to receive(:create).and_return(upload)
151
- allow(upload).to receive(:upload_file)
152
- end
153
-
154
- it "requires a file param" do
155
- expect { described_class.create }.to raise_error(ArgumentError)
156
- expect { described_class.create(file: __FILE__) }.to_not raise_error
157
- end
158
-
159
- it "creates a new upload" do
160
- expect(Tessa::Upload)
161
- .to receive(:create).with(hash_including(foo: :bar, baz: :boom)).and_return(upload)
162
- described_class.create(file: __FILE__, foo: :bar, baz: :boom)
163
- end
164
-
165
- it "uploads the passed file to the upload's URL" do
166
- expect(Tessa::Upload).to receive(:create).and_return(upload)
167
- expect(upload).to receive(:upload_file).with(__FILE__)
168
- described_class.create(file: __FILE__)
169
- end
170
-
171
- it "infers the file size, name, and date" do
172
- file = Tempfile.new("test")
173
- file.write "abc"
174
- file.close
175
- expected_values = {
176
- size: 3,
177
- name: File.basename(file.path),
178
- date: File.mtime(file.path),
179
- }
180
- expect(Tessa::Upload)
181
- .to receive(:create).with(hash_including(expected_values)).and_return(upload)
182
- described_class.create(file: file.path)
183
- end
184
-
185
- it "allows overriding inferred values" do
186
- expected_values = {
187
- size: 1,
188
- name: "my-file",
189
- date: "123",
190
- }
191
- expect(Tessa::Upload)
192
- .to receive(:create).with(hash_including(expected_values)).and_return(upload)
193
- described_class.create(file: __FILE__, **expected_values)
194
- end
195
- end
196
- end
@@ -1,70 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Tessa::Config do
4
- let(:cfg) { Tessa::Config.new }
5
-
6
- shared_examples_for "defaults to environment variable" do
7
- around { |ex| swap_environment_var(variable_name, 'from-env') { ex.run } }
8
-
9
- it { is_expected.to eq("from-env") }
10
-
11
- def swap_environment_var(var, new_val)
12
- old_val = ENV[var]
13
- ENV[var] = new_val
14
- yield
15
- ENV[var] = old_val
16
- end
17
- end
18
-
19
- describe "#username" do
20
- it_behaves_like "defaults to environment variable" do
21
- let(:variable_name) { 'TESSA_USERNAME' }
22
- subject { cfg.username }
23
- end
24
-
25
- it "behaves like a normal accessor" do
26
- cfg.username = "my-new-value"
27
- expect(cfg.username).to eq("my-new-value")
28
- end
29
- end
30
-
31
- describe "#password" do
32
- it_behaves_like "defaults to environment variable" do
33
- let(:variable_name) { 'TESSA_PASSWORD' }
34
- subject { cfg.password }
35
- end
36
-
37
- it "behaves like a normal accessor" do
38
- cfg.password = "my-new-value"
39
- expect(cfg.password).to eq("my-new-value")
40
- end
41
- end
42
-
43
- describe "#url" do
44
- it_behaves_like "defaults to environment variable" do
45
- let(:variable_name) { 'TESSA_URL' }
46
- subject { cfg.url }
47
- end
48
-
49
- it "behaves like a normal accessor" do
50
- cfg.url = "my-new-value"
51
- expect(cfg.url).to eq("my-new-value")
52
- end
53
- end
54
-
55
- describe "#strategy" do
56
- it_behaves_like "defaults to environment variable" do
57
- let(:variable_name) { 'TESSA_STRATEGY' }
58
- subject { cfg.strategy }
59
- end
60
-
61
- it "uses the string 'default' when no envvar passed" do
62
- expect(cfg.strategy).to eq("default")
63
- end
64
-
65
- it "behaves like a normal accessor" do
66
- cfg.strategy = "my-new-value"
67
- expect(cfg.strategy).to eq("my-new-value")
68
- end
69
- end
70
- end
@@ -1,55 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Tessa::ControllerHelpers do
4
- let(:controller) {
5
- Class.new do
6
- attr_writer :session
7
-
8
- def session
9
- @session ||= {}
10
- end
11
-
12
- end.tap { |c| c.send :include, described_class }
13
- }
14
- subject(:instance) { controller.new }
15
-
16
- describe "#tessa_upload_asset_ids" do
17
- it "returns the value of session[:tessa_upload_asset_ids]" do
18
- instance.session = { tessa_upload_asset_ids: :value }
19
- expect(instance.tessa_upload_asset_ids).to eq(:value)
20
- end
21
-
22
- it "sets and returns session[:tessa_upload_asset_ids] to an empty array if nil" do
23
- array = instance.tessa_upload_asset_ids
24
- expect(array).to eq([])
25
- array << 123
26
- expect(instance.tessa_upload_asset_ids).to eq(array)
27
- end
28
- end
29
-
30
- describe "#params_for_asset" do
31
- it "returns a Tessa::AssetChangeSet" do
32
- expect(instance.params_for_asset({})).to be_a(Tessa::AssetChangeSet)
33
- end
34
-
35
- it "sets the value of changes to the argument passed" do
36
- changes = [Tessa::AssetChange.new(id: 1)]
37
- expect(instance.params_for_asset(changes).changes).to eq(changes)
38
- end
39
-
40
- it "sets the scoped_ids to the value of tessa_upload_asset_ids" do
41
- scoped_ids = [1, 2, 3]
42
- instance.session[:tessa_upload_asset_ids] = scoped_ids
43
- expect(instance.params_for_asset([]).scoped_ids).to eq(scoped_ids)
44
- end
45
-
46
- it "ensures a unique array" do
47
- scoped_ids = [1]
48
- instance.session[:tessa_upload_asset_ids] = scoped_ids
49
- set = instance.params_for_asset([])
50
- set.scoped_ids << 2
51
- expect(scoped_ids).to eq([1])
52
- end
53
- end
54
-
55
- end
@@ -1,247 +0,0 @@
1
- require 'rails_helper'
2
-
3
- require 'tessa/jobs/migrate_assets_job'
4
-
5
- RSpec.describe Tessa::MigrateAssetsJob do
6
- it 'does nothing if no db rows' do
7
-
8
- expect {
9
- subject.perform
10
- }.to_not change { ActiveStorage::Attachment.count }
11
- end
12
-
13
- it 'creates attachments for old tessa assets' do
14
- allow(Tessa::Asset).to receive(:find)
15
- .with(1)
16
- .and_return(
17
- Tessa::Asset.new(id: 1,
18
- meta: { name: 'README.md' },
19
- private_download_url: 'https://test.com/README.md')
20
- )
21
-
22
- allow(Tessa::Asset).to receive(:find)
23
- .with(2)
24
- .and_return(
25
- Tessa::Asset.new(id: 2,
26
- meta: { name: 'LICENSE.txt' },
27
- private_download_url: 'https://test.com/LICENSE.txt'),
28
- )
29
-
30
- stub_request(:get, 'https://test.com/README.md')
31
- .to_return(body: File.new('README.md'))
32
- stub_request(:get, 'https://test.com/LICENSE.txt')
33
- .to_return(body: File.new('LICENSE.txt'))
34
-
35
- # DB models with those
36
- models = [
37
- SingleAssetModel.create!(avatar_id: 1),
38
- SingleAssetModel.create!(avatar_id: 2)
39
- ]
40
-
41
- expect {
42
- subject.perform
43
- }.to change { ActiveStorage::Attachment.count }.by(2)
44
-
45
- models.each do |m|
46
- expect(m.reload.avatar_id).to eq(m.avatar.key)
47
- # Now it's in activestorage
48
- expect(m.avatar.public_url).to start_with(
49
- 'https://www.example.com/rails/active_storage/blobs/')
50
- end
51
-
52
- expect(SingleAssetModel.where.not('avatar_id' => nil).count)
53
- .to eq(0)
54
- end
55
-
56
- it 'preserves ActiveStorage blobs' do
57
- allow(Tessa::Asset).to receive(:find)
58
- .with([1, 2])
59
- .and_return([
60
- Tessa::Asset.new(id: 1,
61
- meta: { name: 'README.md' },
62
- private_download_url: 'https://test.com/README.md'),
63
- Tessa::Asset.new(id: 2,
64
- meta: { name: 'LICENSE.txt' },
65
- private_download_url: 'https://test.com/LICENSE.txt'),
66
- ])
67
- stub_request(:get, 'https://test.com/README.md')
68
- .to_return(body: File.new('README.md'))
69
- stub_request(:get, 'https://test.com/LICENSE.txt')
70
- .to_return(body: File.new('LICENSE.txt'))
71
-
72
- file2 = Rack::Test::UploadedFile.new("LICENSE.txt")
73
-
74
- model = MultipleAssetModel.create!(
75
- # The Tessa DB column has the one asset
76
- another_place: [1, 2]
77
- )
78
- # But has already attached a second ActiveStorage blob
79
- ::ActiveStorage::Attached::Many.new("multiple_field", model, dependent: :purge_later)
80
- .attach(file2)
81
- model.save!
82
- attachment = model.multiple_field_attachments.first
83
-
84
- expect {
85
- subject.perform
86
- }.to change { ActiveStorage::Attachment.count }.by(2)
87
-
88
- model = model.reload
89
- # The IDs are now the keys of ActiveStorage objects
90
- expect(model.another_place).to eq(
91
- model.multiple_field.map(&:key))
92
- # preserves the existing attachment
93
- expect(model.multiple_field_attachments).to include(attachment)
94
- expect(model.another_place).to include(attachment.key)
95
-
96
- # all assets are in activestorage
97
- model.multiple_field.each do |blob|
98
- expect(blob.public_url).to start_with(
99
- 'https://www.example.com/rails/active_storage/blobs/')
100
- end
101
-
102
- # DB column is reset to nil
103
- expect(MultipleAssetModel.where.not('another_place' => nil).count)
104
- .to eq(0)
105
- end
106
-
107
- it 'Stops after hitting the batch size' do
108
- 1.upto(11).each do |i|
109
- asset = Tessa::Asset.new(id: i,
110
- meta: { name: 'README.md' },
111
- private_download_url: 'https://test.com/README.md')
112
- allow(Tessa::Asset).to receive(:find)
113
- .with(i)
114
- .and_return(asset)
115
- allow(Tessa::Asset).to receive(:find)
116
- .with([i])
117
- .and_return([asset])
118
- end
119
- stub_request(:get, 'https://test.com/README.md')
120
- .to_return(body: File.new('README.md'))
121
-
122
- # Mix of the two models...
123
- models =
124
- 1.upto(11).map do |i|
125
- if i % 2 == 0
126
- MultipleAssetModel.create!(another_place: [i])
127
- else
128
- SingleAssetModel.create!(avatar_id: i)
129
- end
130
- end
131
-
132
- final_state = nil
133
- final_options = nil
134
- dbl = double('set')
135
- expect(dbl).to receive(:perform_later) do |state, options|
136
- final_state = Marshal.load(state)
137
- final_options = options
138
- end
139
- expect(Tessa::MigrateAssetsJob).to receive(:set)
140
- .with(wait: 10.minutes)
141
- .and_return(dbl)
142
-
143
- expect {
144
- subject.perform
145
- }.to change { ActiveStorage::Attachment.count }.by(10)
146
-
147
- expect(final_state.fully_processed?).to be false
148
- expect(final_options).to eq({
149
- batch_size: 10,
150
- interval: 10.minutes.to_i
151
- })
152
- # One of the two models was fully processed
153
- expect(final_state.model_queue.count(&:fully_processed?))
154
- .to eq(1)
155
- end
156
-
157
- it 'Skips over Tessa errors' do
158
- 1.upto(11).each do |i|
159
- if i % 2 == 0
160
- allow(Tessa::Asset).to receive(:find)
161
- .with(i)
162
- .and_raise(Tessa::RequestFailed)
163
- next
164
- end
165
-
166
- allow(Tessa::Asset).to receive(:find)
167
- .with(i)
168
- .and_return(
169
- Tessa::Asset.new(id: i,
170
- meta: { name: 'README.md' },
171
- private_download_url: 'https://test.com/README.md')
172
- )
173
- end
174
- stub_request(:get, 'https://test.com/README.md')
175
- .to_return(body: File.new('README.md'))
176
-
177
- # Mix of the two models...
178
- models =
179
- 1.upto(11).map do |i|
180
- SingleAssetModel.create!(avatar_id: i)
181
- end
182
-
183
- final_state = nil
184
- final_options = nil
185
- dbl = double('set')
186
- expect(dbl).to receive(:perform_later) do |state, options|
187
- final_state = Marshal.load(state)
188
- final_options = options
189
- end
190
- expect(Tessa::MigrateAssetsJob).to receive(:set)
191
- .with(wait: 10.minutes)
192
- .and_return(dbl)
193
-
194
- expect {
195
- subject.perform
196
- }.to change { ActiveStorage::Attachment.count }.by(5)
197
-
198
- expect(final_state.fully_processed?).to be false
199
- field_state = final_state.next_model.next_field
200
- expect(field_state.offset).to eq(5)
201
- end
202
-
203
- it 'Resumes from marshalled state' do
204
-
205
- file = Rack::Test::UploadedFile.new('README.md')
206
-
207
- state = Tessa::MigrateAssetsJob::ProcessingState.initialize_from_models(
208
- [SingleAssetModel])
209
- field_state = state.model_queue
210
- .detect { |m| m.class_name == 'SingleAssetModel' }
211
- .field_queue
212
- .detect { |m| m.field_name == :avatar }
213
-
214
- 1.upto(10).each do |i|
215
- if i % 2 == 0
216
- # This one failed
217
- SingleAssetModel.create!(avatar_id: i).tap do |r|
218
- field_state.offset += 1
219
- end
220
- else
221
- # This one succeeded and is in ActiveStorage
222
- SingleAssetModel.create!(avatar: file).tap do |r|
223
- field_state.success_count += 1
224
- end
225
- end
226
- end
227
-
228
- # This one still needs to transition
229
- model = SingleAssetModel.create!(avatar_id: 11)
230
-
231
- asset = Tessa::Asset.new(id: 11,
232
- meta: { name: 'README.md' },
233
- private_download_url: 'https://test.com/README.md')
234
- allow(Tessa::Asset).to receive(:find)
235
- .with(11)
236
- .and_return(asset)
237
- stub_request(:get, 'https://test.com/README.md')
238
- .to_return(body: File.new('README.md'))
239
-
240
- # Doesn't reenqueue since we finished processing
241
- expect(Tessa::MigrateAssetsJob).to_not receive(:set)
242
-
243
- expect {
244
- subject.perform(Marshal.dump(state), { batch_size: 2, interval: 3 })
245
- }.to change { ActiveStorage::Attachment.count }.by(1)
246
- end
247
- end
@@ -1,72 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Tessa::Model::Field do
4
- subject(:field) { described_class.new(attrs) }
5
- let(:attrs) { {} }
6
-
7
- describe "#initialize" do
8
- context "with all fields set" do
9
- let(:attrs) {
10
- {
11
- model: :model,
12
- name: "name",
13
- multiple: true,
14
- id_field: "my_field",
15
- }
16
- }
17
-
18
- it "sets :model to attribute" do
19
- expect(field.model).to eq(:model)
20
- end
21
-
22
- it "sets :name to attribute" do
23
- expect(field.name).to eq("name")
24
- end
25
-
26
- it "sets :multiple to attribute" do
27
- expect(field.multiple).to eq(true)
28
- end
29
-
30
- it "sets :id_field to attribute" do
31
- expect(field.id_field).to eq("my_field")
32
- end
33
- end
34
-
35
- context "with no fields set" do
36
- it "defaults model to nil" do
37
- expect(field.model).to be_nil
38
- end
39
-
40
- it "defaults name to nil" do
41
- expect(field.name).to be_nil
42
- end
43
-
44
- it "defaults multiple to false" do
45
- expect(field.multiple).to eq(false)
46
- end
47
-
48
- context "when multiple true" do
49
- before do
50
- attrs[:name] = "my_name"
51
- attrs[:multiple] = false
52
- end
53
-
54
- it "defaults id_field to name + '_id'" do
55
- expect(field.id_field).to eq("my_name_id")
56
- end
57
- end
58
-
59
- context "when multiple false" do
60
- before do
61
- attrs[:name] = "my_name"
62
- attrs[:multiple] = true
63
- end
64
-
65
- it "defaults id_field to name + '_ids'" do
66
- expect(field.id_field).to eq("my_name_ids")
67
- end
68
- end
69
- end
70
- end
71
-
72
- end