brainstem 1.1.1 → 1.3.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.
- 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
|