brainstem 1.1.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +81 -4
- data/Gemfile.lock +9 -9
- data/README.md +134 -37
- data/brainstem.gemspec +1 -1
- data/lib/brainstem/api_docs/endpoint.rb +40 -18
- data/lib/brainstem/api_docs/formatters/markdown/endpoint_formatter.rb +27 -22
- data/lib/brainstem/api_docs/formatters/markdown/helper.rb +9 -0
- data/lib/brainstem/api_docs/formatters/markdown/presenter_formatter.rb +14 -6
- data/lib/brainstem/api_docs/presenter.rb +3 -7
- data/lib/brainstem/concerns/controller_dsl.rb +138 -14
- data/lib/brainstem/concerns/presenter_dsl.rb +39 -6
- data/lib/brainstem/dsl/array_block_field.rb +25 -0
- data/lib/brainstem/dsl/block_field.rb +69 -0
- data/lib/brainstem/dsl/configuration.rb +13 -5
- data/lib/brainstem/dsl/field.rb +15 -1
- data/lib/brainstem/dsl/fields_block.rb +20 -2
- data/lib/brainstem/dsl/hash_block_field.rb +30 -0
- data/lib/brainstem/presenter.rb +10 -6
- data/lib/brainstem/presenter_validator.rb +20 -11
- data/lib/brainstem/version.rb +1 -1
- data/spec/brainstem/api_docs/endpoint_spec.rb +347 -14
- data/spec/brainstem/api_docs/formatters/markdown/endpoint_formatter_spec.rb +106 -13
- data/spec/brainstem/api_docs/formatters/markdown/helper_spec.rb +19 -0
- data/spec/brainstem/api_docs/formatters/markdown/presenter_formatter_spec.rb +150 -37
- data/spec/brainstem/api_docs/presenter_spec.rb +85 -18
- data/spec/brainstem/concerns/controller_dsl_spec.rb +615 -31
- data/spec/brainstem/concerns/inheritable_configuration_spec.rb +32 -9
- data/spec/brainstem/concerns/presenter_dsl_spec.rb +99 -25
- data/spec/brainstem/dsl/array_block_field_spec.rb +43 -0
- data/spec/brainstem/dsl/block_field_spec.rb +188 -0
- data/spec/brainstem/dsl/field_spec.rb +86 -20
- data/spec/brainstem/dsl/hash_block_field_spec.rb +166 -0
- data/spec/brainstem/presenter_collection_spec.rb +24 -24
- data/spec/brainstem/presenter_spec.rb +233 -9
- data/spec/brainstem/query_strategies/filter_and_search_spec.rb +1 -1
- data/spec/spec_helpers/presenters.rb +8 -0
- data/spec/spec_helpers/schema.rb +13 -0
- metadata +15 -6
@@ -49,12 +49,14 @@ describe Brainstem::Concerns::InheritableConfiguration do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
describe '#keys and #to_h' do
|
52
|
-
let(:subclass)
|
52
|
+
let(:subclass) { Class.new(parent_class) }
|
53
53
|
let(:subsubclass) { Class.new(subclass) }
|
54
|
+
let!(:proc_key) { Proc.new {} }
|
54
55
|
|
55
56
|
before do
|
56
57
|
parent_class.configuration['1'] = :a
|
57
58
|
parent_class.configuration['2'] = :b
|
59
|
+
parent_class.configuration[proc_key] = :foo
|
58
60
|
|
59
61
|
subclass.configuration['2'] = :c
|
60
62
|
subclass.configuration['3'] = :d
|
@@ -64,12 +66,9 @@ describe Brainstem::Concerns::InheritableConfiguration do
|
|
64
66
|
end
|
65
67
|
|
66
68
|
it "returns the union of this class's keys with any parent keys" do
|
67
|
-
expect(parent_class.configuration.
|
68
|
-
expect(
|
69
|
-
expect(
|
70
|
-
expect(subclass.configuration.to_h).to eq({ '1' => :a, '2' => :c, '3' => :d })
|
71
|
-
expect(subsubclass.configuration.keys).to eq ['1', '2', '3', '4']
|
72
|
-
expect(subsubclass.configuration.to_h).to eq({ '1' => :a, '2' => :c, '3' => :e, '4' => :f })
|
69
|
+
expect(parent_class.configuration.to_h).to eq({ '1' => :a, '2' => :b, proc_key => :foo })
|
70
|
+
expect(subclass.configuration.to_h).to eq({ '1' => :a, '2' => :c, '3' => :d, proc_key => :foo })
|
71
|
+
expect(subsubclass.configuration.to_h).to eq({ '1' => :a, '2' => :c, '3' => :e, '4' => :f, proc_key => :foo })
|
73
72
|
|
74
73
|
# it doesn't mutate storage
|
75
74
|
subclass.configuration.to_h['1'] = :new
|
@@ -82,14 +81,19 @@ describe Brainstem::Concerns::InheritableConfiguration do
|
|
82
81
|
expect(parent_class.configuration['1']).to eq :a
|
83
82
|
expect(parent_class.configuration['2']).to eq :b
|
84
83
|
expect(parent_class.configuration['3']).to be_nil
|
84
|
+
expect(parent_class.configuration[proc_key]).to eq :foo
|
85
|
+
|
85
86
|
expect(subclass.configuration['1']).to eq :a
|
86
87
|
expect(subclass.configuration['2']).to eq :c
|
87
88
|
expect(subclass.configuration['3']).to eq :d
|
88
89
|
expect(subclass.configuration['4']).to be_nil
|
90
|
+
expect(subclass.configuration[proc_key]).to eq :foo
|
91
|
+
|
89
92
|
expect(subsubclass.configuration['1']).to eq :a
|
90
93
|
expect(subsubclass.configuration['2']).to eq :c
|
91
94
|
expect(subsubclass.configuration['3']).to eq :e
|
92
95
|
expect(subsubclass.configuration['4']).to eq :f
|
96
|
+
expect(subsubclass.configuration[proc_key]).to eq :foo
|
93
97
|
end
|
94
98
|
|
95
99
|
it "does not return nonheritable keys in the parent" do
|
@@ -97,8 +101,12 @@ describe Brainstem::Concerns::InheritableConfiguration do
|
|
97
101
|
parent_class.configuration['nonheritable'] = "why yes, I am nonheritable"
|
98
102
|
expect(subclass.configuration.keys).not_to include 'nonheritable'
|
99
103
|
|
100
|
-
|
101
|
-
|
104
|
+
parent_class.configuration.nonheritable! proc_key
|
105
|
+
parent_class.configuration[proc_key] = "Not Inheritable!"
|
106
|
+
expect(subclass.configuration.keys).not_to include proc_key
|
107
|
+
|
108
|
+
expect(subclass.configuration.to_h.keys).not_to include proc_key
|
109
|
+
expect(subclass.configuration.has_key?(proc_key)).to eq false
|
102
110
|
end
|
103
111
|
end
|
104
112
|
|
@@ -298,6 +306,21 @@ describe Brainstem::Concerns::InheritableConfiguration do
|
|
298
306
|
parent_class.configuration.nonheritable! :nonheritable
|
299
307
|
expect(parent_class.configuration.nonheritable_keys.to_a).to eq ["nonheritable"]
|
300
308
|
end
|
309
|
+
|
310
|
+
context "when key is a proc" do
|
311
|
+
let!(:proc_key) { Proc.new {} }
|
312
|
+
|
313
|
+
it "adds the key to the nonheritable attributes list" do
|
314
|
+
parent_class.configuration.nonheritable! proc_key
|
315
|
+
expect(parent_class.configuration.nonheritable_keys.to_a).to eq [proc_key]
|
316
|
+
end
|
317
|
+
|
318
|
+
it "dedupes" do
|
319
|
+
parent_class.configuration.nonheritable! proc_key
|
320
|
+
parent_class.configuration.nonheritable! proc_key
|
321
|
+
expect(parent_class.configuration.nonheritable_keys.to_a).to eq [proc_key]
|
322
|
+
end
|
323
|
+
end
|
301
324
|
end
|
302
325
|
end
|
303
326
|
|
@@ -21,16 +21,32 @@ require 'brainstem/concerns/presenter_dsl'
|
|
21
21
|
# field :description, :string
|
22
22
|
# field :updated_at, :datetime
|
23
23
|
# field :dynamic_title, :string, dynamic: lambda { |model| model.title }
|
24
|
-
# field :secret, :string,
|
24
|
+
# field :secret, :string,
|
25
|
+
# info: 'a secret, via secret_info',
|
25
26
|
# via: :secret_info,
|
26
27
|
# if: [:user_is_bob, :title_is_hello]
|
27
28
|
#
|
29
|
+
# field :member_ids, :array,
|
30
|
+
# item_type: :integer,
|
31
|
+
# dynamic: lambda { |model| model.members.pluck(:id) }
|
32
|
+
#
|
28
33
|
# with_options if: :user_is_bob do
|
29
34
|
# field :bob_title, :string,
|
30
35
|
# info: 'another name for the title, only for Bob',
|
31
36
|
# via: :title
|
32
37
|
# end
|
33
|
-
#
|
38
|
+
#
|
39
|
+
# fields :members, :array,
|
40
|
+
# if: :user_is_bob,
|
41
|
+
# dynamic: lambda { |project| project.members } do
|
42
|
+
# field :name, :string,
|
43
|
+
# dynamic: lambda { |user| user.username }
|
44
|
+
# field :project_klass, :number,
|
45
|
+
# use_parent_value: false,
|
46
|
+
# dynamic: lambda { |project| project.class }
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# fields :nested_permissions, if: :user_is_bob do
|
34
50
|
# field :something_title, :string, via: :title
|
35
51
|
# field :random, :number, dynamic: lambda { rand }
|
36
52
|
# end
|
@@ -118,7 +134,6 @@ describe Brainstem::Concerns::PresenterDSL do
|
|
118
134
|
end
|
119
135
|
end
|
120
136
|
|
121
|
-
|
122
137
|
# sort_order :created_at, ".created_at"
|
123
138
|
describe 'the sort_order block' do
|
124
139
|
let(:value) { "widgets.created_at" }
|
@@ -159,7 +174,6 @@ describe Brainstem::Concerns::PresenterDSL do
|
|
159
174
|
end
|
160
175
|
end
|
161
176
|
|
162
|
-
|
163
177
|
describe 'the conditional block' do
|
164
178
|
before do
|
165
179
|
presenter_class.conditionals do
|
@@ -218,11 +232,16 @@ describe Brainstem::Concerns::PresenterDSL do
|
|
218
232
|
via: :secret_info,
|
219
233
|
if: [:user_is_bob, :title_is_hello]
|
220
234
|
|
235
|
+
field :member_ids, :array,
|
236
|
+
item_type: :integer,
|
237
|
+
dynamic: lambda { |model| model.members.pluck(:id) }
|
238
|
+
|
221
239
|
with_options if: :user_is_bob do
|
222
240
|
field :bob_title, :string,
|
223
241
|
info: 'another name for the title, only for Bob',
|
224
242
|
via: :title
|
225
243
|
end
|
244
|
+
|
226
245
|
fields :nested_permissions do
|
227
246
|
field :something_title, :string, via: :title
|
228
247
|
field :random, :number, dynamic: lambda { rand }
|
@@ -231,16 +250,25 @@ describe Brainstem::Concerns::PresenterDSL do
|
|
231
250
|
end
|
232
251
|
|
233
252
|
it 'is stored in the configuration' do
|
234
|
-
expect(presenter_class.configuration[:fields].keys)
|
235
|
-
|
253
|
+
expect(presenter_class.configuration[:fields].keys)
|
254
|
+
.to match_array %w[updated_at dynamic_title secret member_ids bob_title nested_permissions]
|
255
|
+
|
256
|
+
expect(presenter_class.configuration[:fields][:updated_at].type).to eq 'datetime'
|
236
257
|
expect(presenter_class.configuration[:fields][:updated_at].description).to be_nil
|
237
|
-
|
258
|
+
|
259
|
+
expect(presenter_class.configuration[:fields][:dynamic_title].type).to eq 'string'
|
238
260
|
expect(presenter_class.configuration[:fields][:dynamic_title].description).to be_nil
|
239
261
|
expect(presenter_class.configuration[:fields][:dynamic_title].options[:dynamic]).to be_a(Proc)
|
240
|
-
|
262
|
+
|
263
|
+
expect(presenter_class.configuration[:fields][:secret].type).to eq 'string'
|
241
264
|
expect(presenter_class.configuration[:fields][:secret].description).to be_nil
|
242
265
|
expect(presenter_class.configuration[:fields][:secret].options).to eq({ via: :secret_info, if: [:user_is_bob, :title_is_hello] })
|
243
|
-
|
266
|
+
|
267
|
+
expect(presenter_class.configuration[:fields][:member_ids].type).to eq 'array'
|
268
|
+
expect(presenter_class.configuration[:fields][:member_ids].options[:item_type]).to eq 'integer'
|
269
|
+
expect(presenter_class.configuration[:fields][:member_ids].options[:dynamic]).to be_a(Proc)
|
270
|
+
|
271
|
+
expect(presenter_class.configuration[:fields][:bob_title].type).to eq 'string'
|
244
272
|
expect(presenter_class.configuration[:fields][:bob_title].description).to eq 'another name for the title, only for Bob'
|
245
273
|
expect(presenter_class.configuration[:fields][:bob_title].options).to eq(
|
246
274
|
via: :title,
|
@@ -250,7 +278,7 @@ describe Brainstem::Concerns::PresenterDSL do
|
|
250
278
|
end
|
251
279
|
|
252
280
|
it 'handles nesting' do
|
253
|
-
expect(presenter_class.configuration[:fields][:nested_permissions][:something_title].type).to eq
|
281
|
+
expect(presenter_class.configuration[:fields][:nested_permissions][:something_title].type).to eq 'string'
|
254
282
|
expect(presenter_class.configuration[:fields][:nested_permissions][:something_title].options[:via]).to eq :title
|
255
283
|
expect(presenter_class.configuration[:fields][:nested_permissions][:random].options[:dynamic]).to be_a(Proc)
|
256
284
|
end
|
@@ -263,11 +291,17 @@ describe Brainstem::Concerns::PresenterDSL do
|
|
263
291
|
field :updated_at, :datetime, info: 'this time I have a description and condition'
|
264
292
|
end
|
265
293
|
end
|
266
|
-
|
267
|
-
expect(
|
294
|
+
|
295
|
+
expect(presenter_class.configuration[:fields].keys).
|
296
|
+
to match_array %w[updated_at dynamic_title secret member_ids bob_title nested_permissions]
|
297
|
+
expect(subclass.configuration[:fields].keys).
|
298
|
+
to match_array %w[updated_at dynamic_title secret member_ids bob_title title nested_permissions]
|
299
|
+
|
268
300
|
expect(presenter_class.configuration[:fields][:updated_at].description).to be_nil
|
269
301
|
expect(presenter_class.configuration[:fields][:updated_at].options).to eq({})
|
270
|
-
|
302
|
+
|
303
|
+
expect(subclass.configuration[:fields][:updated_at].description).
|
304
|
+
to eq 'this time I have a description and condition'
|
271
305
|
expect(subclass.configuration[:fields][:updated_at].options).to eq(
|
272
306
|
if: [:some_condition, :some_other_condition],
|
273
307
|
info: 'this time I have a description and condition'
|
@@ -320,18 +354,21 @@ describe Brainstem::Concerns::PresenterDSL do
|
|
320
354
|
field :something, :string, via: :title
|
321
355
|
end
|
322
356
|
end
|
323
|
-
expect(presenter_class.configuration[:fields].keys).to match_array %w[updated_at dynamic_title secret bob_title nested_permissions]
|
324
|
-
expect(subclass.configuration[:fields].keys).to match_array %w[updated_at dynamic_title secret bob_title nested_permissions new_nested_permissions]
|
325
357
|
|
326
|
-
expect(presenter_class.configuration[:fields]
|
327
|
-
|
358
|
+
expect(presenter_class.configuration[:fields].keys)
|
359
|
+
.to match_array %w[updated_at dynamic_title secret member_ids bob_title nested_permissions]
|
360
|
+
expect(subclass.configuration[:fields].keys)
|
361
|
+
.to match_array %w[updated_at dynamic_title secret member_ids bob_title nested_permissions new_nested_permissions]
|
362
|
+
|
363
|
+
expect(presenter_class.configuration[:fields][:nested_permissions][:something_title].type).to eq 'string'
|
364
|
+
expect(presenter_class.configuration[:fields][:nested_permissions][:random].type).to eq 'number'
|
328
365
|
expect(presenter_class.configuration[:fields][:nested_permissions][:new]).to be_nil
|
329
366
|
expect(presenter_class.configuration[:fields][:nested_permissions][:deeper]).to be_nil
|
330
367
|
expect(presenter_class.configuration[:fields][:new_nested_permissions]).to be_nil
|
331
368
|
|
332
|
-
expect(subclass.configuration[:fields][:nested_permissions][:something_title].type).to eq
|
333
|
-
expect(subclass.configuration[:fields][:nested_permissions][:random].type).to eq
|
334
|
-
expect(subclass.configuration[:fields][:nested_permissions][:new].type).to eq
|
369
|
+
expect(subclass.configuration[:fields][:nested_permissions][:something_title].type).to eq 'number' # changed this
|
370
|
+
expect(subclass.configuration[:fields][:nested_permissions][:random].type).to eq 'number'
|
371
|
+
expect(subclass.configuration[:fields][:nested_permissions][:new].type).to eq 'string'
|
335
372
|
expect(subclass.configuration[:fields][:nested_permissions][:deeper][:something]).to be_present
|
336
373
|
expect(subclass.configuration[:fields][:new_nested_permissions]).to be_present
|
337
374
|
end
|
@@ -345,7 +382,7 @@ describe Brainstem::Concerns::PresenterDSL do
|
|
345
382
|
|
346
383
|
it "is stored in the configuration correctly" do
|
347
384
|
expect(presenter_class.configuration[:fields].keys).to include('synced_at')
|
348
|
-
expect(presenter_class.configuration[:fields][:synced_at].type).to eq
|
385
|
+
expect(presenter_class.configuration[:fields][:synced_at].type).to eq 'datetime'
|
349
386
|
expect(presenter_class.configuration[:fields][:synced_at].description).to eq 'Last time the object was synced'
|
350
387
|
end
|
351
388
|
end
|
@@ -539,21 +576,58 @@ describe Brainstem::Concerns::PresenterDSL do
|
|
539
576
|
|
540
577
|
it "creates an entry in the filters configuration" do
|
541
578
|
my_proc = Proc.new { 1 }
|
542
|
-
presenter_class.filter(:foo, :default => true, &my_proc)
|
579
|
+
presenter_class.filter(:foo, :string, :default => true, :items => [:a, :b], &my_proc)
|
543
580
|
|
544
|
-
expect(foo).to eq({ "default" => true, "value" => my_proc })
|
581
|
+
expect(foo).to eq({ "default" => true, "value" => my_proc, "type" => "string", "items" => [:a, :b] })
|
545
582
|
end
|
546
583
|
|
547
584
|
it "accepts names without blocks" do
|
548
|
-
presenter_class.filter(:foo)
|
585
|
+
presenter_class.filter(:foo, :string, :items => [:a, :b])
|
549
586
|
expect(foo[:value]).to be_nil
|
587
|
+
expect(foo[:type]).to eq("string")
|
588
|
+
expect(foo[:items]).to eq([:a, :b])
|
550
589
|
end
|
551
590
|
|
552
591
|
it "records the info option" do
|
553
|
-
presenter_class.filter(:foo, :info => "This is documented.")
|
592
|
+
presenter_class.filter(:foo, :integer, :info => "This is documented.", :items => [:a, :b])
|
554
593
|
expect(foo[:info]).to eq "This is documented."
|
594
|
+
expect(foo[:type]).to eq "integer"
|
595
|
+
expect(foo[:items]).to eq([:a, :b])
|
596
|
+
end
|
597
|
+
|
598
|
+
context "when filter is of array type" do
|
599
|
+
it "records the item_type option" do
|
600
|
+
presenter_class.filter(:foo, :array, :item_type => :integer)
|
601
|
+
expect(foo[:type]).to eq "array"
|
602
|
+
expect(foo[:item_type]).to eq("integer")
|
603
|
+
end
|
604
|
+
|
605
|
+
it "defaults item_type to string if not specified" do
|
606
|
+
presenter_class.filter(:foo, :array)
|
607
|
+
expect(foo[:type]).to eq "array"
|
608
|
+
expect(foo[:item_type]).to eq("string")
|
609
|
+
end
|
555
610
|
end
|
556
611
|
|
612
|
+
|
613
|
+
context "when type is not specified" do
|
614
|
+
before do
|
615
|
+
mock(presenter_class).deprecated_type_warning
|
616
|
+
end
|
617
|
+
|
618
|
+
it "adds a deprecation warning and creates an entry in the filters configuration" do
|
619
|
+
my_proc = Proc.new { 1 }
|
620
|
+
presenter_class.filter(:foo, :default => true, &my_proc)
|
621
|
+
|
622
|
+
expect(foo).to eq({ "default" => true, "value" => my_proc, "type" => "string" })
|
623
|
+
end
|
624
|
+
|
625
|
+
it "adds a deprecation warning and records the info option" do
|
626
|
+
presenter_class.filter(:foo, :info => "This is documented.")
|
627
|
+
expect(foo[:info]).to eq "This is documented."
|
628
|
+
expect(foo[:type]).to eq "string"
|
629
|
+
end
|
630
|
+
end
|
557
631
|
end
|
558
632
|
|
559
633
|
describe ".search" do
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'brainstem/dsl/array_block_field'
|
3
|
+
|
4
|
+
describe Brainstem::DSL::ArrayBlockField do
|
5
|
+
let(:name) { :tasks }
|
6
|
+
let(:type) { :array }
|
7
|
+
let(:description) { 'the title of this model' }
|
8
|
+
let(:options) { { info: description, via: :tasks } }
|
9
|
+
let(:nested_field) { Brainstem::DSL::ArrayBlockField.new(name, type, options) }
|
10
|
+
let(:model) { Workspace.find(1) }
|
11
|
+
let(:tasks) { Task.where(workspace_id: model.id).order(:id).to_a }
|
12
|
+
|
13
|
+
before do
|
14
|
+
expect(tasks).to_not be_empty
|
15
|
+
expect(nested_field.configuration.keys).to be_empty
|
16
|
+
|
17
|
+
# Add sub fields to the array block field.
|
18
|
+
nested_field.configuration[:name] = Brainstem::DSL::Field.new(:name, type, {})
|
19
|
+
nested_field.configuration[:parent_name] = Brainstem::DSL::Field.new(:parent_name, type,
|
20
|
+
dynamic: -> (model) { model.parent.try(:name) }
|
21
|
+
)
|
22
|
+
nested_field.configuration[:secret] = Brainstem::DSL::Field.new(:secret, type,
|
23
|
+
via: :secret_info,
|
24
|
+
use_parent_value: false
|
25
|
+
)
|
26
|
+
|
27
|
+
expect(nested_field.configuration.keys).to eq(%w(name parent_name secret))
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#run_on' do
|
31
|
+
let(:context) { { } }
|
32
|
+
let(:helper_instance) { Object.new }
|
33
|
+
let(:presented_field_data) {
|
34
|
+
tasks.map do |task|
|
35
|
+
{ 'name' => task.name, 'parent_name' => task.parent.try(:name), 'secret' => model.secret_info }
|
36
|
+
end
|
37
|
+
}
|
38
|
+
|
39
|
+
it 'returns an array of hashes with sub nested properties' do
|
40
|
+
expect(nested_field.run_on(model, context)).to eq(presented_field_data)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'brainstem/dsl/block_field'
|
3
|
+
|
4
|
+
describe Brainstem::DSL::BlockField do
|
5
|
+
let(:name) { :tasks }
|
6
|
+
let(:type) { :hash }
|
7
|
+
let(:description) { 'the title of this model' }
|
8
|
+
let(:options) { { info: description } }
|
9
|
+
let(:nested_field) { Brainstem::DSL::BlockField.new(name, type, options) }
|
10
|
+
let(:model) { Workspace.find(1) }
|
11
|
+
|
12
|
+
describe 'configuration' do
|
13
|
+
context 'when parent field specified' do
|
14
|
+
let(:parent_field) { Brainstem::DSL::BlockField.new(name, type, options) }
|
15
|
+
let(:nested_field) { Brainstem::DSL::BlockField.new(name, type, options, parent_field) }
|
16
|
+
let(:inherited_field) do
|
17
|
+
Brainstem::DSL::Field.new(:parent_name, type, dynamic: -> (model) { model.parent.try(:name) })
|
18
|
+
end
|
19
|
+
let(:overriden_field) do
|
20
|
+
Brainstem::DSL::Field.new(:name, type, dynamic: -> (model) { "Formatted #{model.name}" })
|
21
|
+
end
|
22
|
+
|
23
|
+
before do
|
24
|
+
parent_field.configuration[:name] = Brainstem::DSL::Field.new(:name, type, {})
|
25
|
+
parent_field.configuration[:parent_name] = inherited_field
|
26
|
+
|
27
|
+
nested_field.configuration[:name] = overriden_field
|
28
|
+
nested_field.configuration[:parent_title] = Brainstem::DSL::Field.new(:parent_title, type,
|
29
|
+
dynamic: -> (model) { model.parent.try(:name) }
|
30
|
+
)
|
31
|
+
|
32
|
+
expect(nested_field.configuration.keys).to include('name', 'parent_title')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'inherits sub fields from the parent field' do
|
36
|
+
expect(nested_field.configuration.keys).to include('parent_name')
|
37
|
+
expect(nested_field.configuration[:parent_name]).to eq(inherited_field)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'override keys inherits from the parent' do
|
41
|
+
expect(nested_field.configuration.keys).to include('name')
|
42
|
+
expect(nested_field.configuration[:name]).to eq(overriden_field)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when parent field is not specified' do
|
47
|
+
let(:nested_field) { Brainstem::DSL::BlockField.new(name, type, options) }
|
48
|
+
|
49
|
+
before do
|
50
|
+
nested_field.configuration[:name] = Brainstem::DSL::Field.new(:name, type, {})
|
51
|
+
nested_field.configuration[:parent_title] = Brainstem::DSL::Field.new(:parent_title, type,
|
52
|
+
dynamic: -> (model) { model.parent.try(:name) }
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'only has sub fields defined on itself' do
|
57
|
+
expect(nested_field.configuration.keys).to match_array(['name', 'parent_title'])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'self.for' do
|
63
|
+
subject { described_class.for(name, type, options) }
|
64
|
+
|
65
|
+
context 'when given an array type' do
|
66
|
+
let(:type) { :array }
|
67
|
+
|
68
|
+
it 'returns a new ArrayBlockField' do
|
69
|
+
expect(subject).to be_instance_of(Brainstem::DSL::ArrayBlockField)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'when given a hash type' do
|
74
|
+
let(:type) { :hash }
|
75
|
+
|
76
|
+
it 'returns a new HashBlockField' do
|
77
|
+
expect(subject).to be_instance_of(Brainstem::DSL::HashBlockField)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when given an unknown type' do
|
82
|
+
let(:type) { :unknown }
|
83
|
+
|
84
|
+
it 'raises an error' do
|
85
|
+
expect { subject }.to raise_error(StandardError)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'evaluate_value_on' do
|
91
|
+
let(:context) { {} }
|
92
|
+
let(:helper_instance) { Object.new }
|
93
|
+
|
94
|
+
context 'when lookup option is specifed' do
|
95
|
+
let(:context) {
|
96
|
+
{
|
97
|
+
lookup: Brainstem::Presenter.new.send(:empty_lookup_cache, [name.to_s], []),
|
98
|
+
models: [model]
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
context 'when lookup_fetch option is not specified' do
|
103
|
+
let(:options) do
|
104
|
+
{ lookup: lambda { |models| Hash[models.map { |model| [model.id, model.tasks.to_a] }] } }
|
105
|
+
end
|
106
|
+
|
107
|
+
before do
|
108
|
+
expect(options).to_not have_key(:lookup_fetch)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'returns the value from the lookup cache' do
|
112
|
+
expect(nested_field.evaluate_value_on(model, context, helper_instance)).to eq(model.tasks.to_a)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when lookup_fetch option is specified' do
|
117
|
+
let(:options) do
|
118
|
+
{
|
119
|
+
lookup: lambda { |models| Hash[models.map { |model| [model.id + 10, model.tasks.to_a] }] },
|
120
|
+
lookup_fetch: lambda { |lookup, model| lookup[model.id + 10] }
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
before do
|
125
|
+
expect(options).to have_key(:lookup_fetch)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'returns the value from the lookup cache using the lookup fetch' do
|
129
|
+
expect(nested_field.evaluate_value_on(model, context, helper_instance)).to eq(model.tasks.to_a)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'when dynamic option is specified' do
|
135
|
+
let(:options) { { dynamic: lambda { |model| model.tasks.to_a } } }
|
136
|
+
|
137
|
+
it 'calls the :dynamic lambda in the context of the given instance' do
|
138
|
+
expect(nested_field.evaluate_value_on(model, context, helper_instance)).to eq(model.tasks.to_a)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'when via option is specified' do
|
143
|
+
let(:options) { { via: :tasks } }
|
144
|
+
|
145
|
+
it 'calls the method name in the :via option in the context of the given instance' do
|
146
|
+
expect(nested_field.evaluate_value_on(model, context, helper_instance)).to eq(model.tasks.to_a)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'when none of the options are specified' do
|
151
|
+
let(:options) { {} }
|
152
|
+
|
153
|
+
it 'should raise error' do
|
154
|
+
expect {
|
155
|
+
nested_field.evaluate_value_on(model, context, helper_instance)
|
156
|
+
}.to raise_error(StandardError)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe 'use_parent_value?' do
|
162
|
+
let(:field) { Brainstem::DSL::Field.new(:type, type, options) }
|
163
|
+
|
164
|
+
subject { nested_field.use_parent_value?(field) }
|
165
|
+
|
166
|
+
context 'when sub field does not specify use_parent_value option' do
|
167
|
+
let(:options) { { info: description } }
|
168
|
+
|
169
|
+
it { is_expected.to be_truthy }
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'when sub field specifies use_parent_value option' do
|
173
|
+
let(:options) { { use_parent_value: use_parent_value } }
|
174
|
+
|
175
|
+
context 'when set to true' do
|
176
|
+
let(:use_parent_value) { true }
|
177
|
+
|
178
|
+
it { is_expected.to be_truthy }
|
179
|
+
end
|
180
|
+
|
181
|
+
context 'when set to false' do
|
182
|
+
let(:use_parent_value) { false }
|
183
|
+
|
184
|
+
it { is_expected.to be_falsey }
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|