brainstem 0.2.6.1 → 1.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +5 -13
  2. data/CHANGELOG.md +16 -2
  3. data/Gemfile.lock +51 -36
  4. data/README.md +531 -110
  5. data/brainstem.gemspec +6 -2
  6. data/lib/brainstem.rb +25 -9
  7. data/lib/brainstem/concerns/controller_param_management.rb +22 -0
  8. data/lib/brainstem/concerns/error_presentation.rb +58 -0
  9. data/lib/brainstem/concerns/inheritable_configuration.rb +29 -0
  10. data/lib/brainstem/concerns/lookup.rb +30 -0
  11. data/lib/brainstem/concerns/presenter_dsl.rb +111 -0
  12. data/lib/brainstem/controller_methods.rb +17 -8
  13. data/lib/brainstem/dsl/association.rb +55 -0
  14. data/lib/brainstem/dsl/associations_block.rb +12 -0
  15. data/lib/brainstem/dsl/base_block.rb +31 -0
  16. data/lib/brainstem/dsl/conditional.rb +25 -0
  17. data/lib/brainstem/dsl/conditionals_block.rb +15 -0
  18. data/lib/brainstem/dsl/configuration.rb +112 -0
  19. data/lib/brainstem/dsl/field.rb +68 -0
  20. data/lib/brainstem/dsl/fields_block.rb +25 -0
  21. data/lib/brainstem/preloader.rb +98 -0
  22. data/lib/brainstem/presenter.rb +325 -134
  23. data/lib/brainstem/presenter_collection.rb +82 -286
  24. data/lib/brainstem/presenter_validator.rb +96 -0
  25. data/lib/brainstem/query_strategies/README.md +107 -0
  26. data/lib/brainstem/query_strategies/base_strategy.rb +62 -0
  27. data/lib/brainstem/query_strategies/filter_and_search.rb +50 -0
  28. data/lib/brainstem/query_strategies/filter_or_search.rb +103 -0
  29. data/lib/brainstem/test_helpers.rb +5 -1
  30. data/lib/brainstem/version.rb +1 -1
  31. data/spec/brainstem/concerns/controller_param_management_spec.rb +42 -0
  32. data/spec/brainstem/concerns/error_presentation_spec.rb +113 -0
  33. data/spec/brainstem/concerns/inheritable_configuration_spec.rb +210 -0
  34. data/spec/brainstem/concerns/presenter_dsl_spec.rb +412 -0
  35. data/spec/brainstem/controller_methods_spec.rb +15 -27
  36. data/spec/brainstem/dsl/association_spec.rb +123 -0
  37. data/spec/brainstem/dsl/conditional_spec.rb +93 -0
  38. data/spec/brainstem/dsl/configuration_spec.rb +1 -0
  39. data/spec/brainstem/dsl/field_spec.rb +212 -0
  40. data/spec/brainstem/preloader_spec.rb +137 -0
  41. data/spec/brainstem/presenter_collection_spec.rb +565 -244
  42. data/spec/brainstem/presenter_spec.rb +726 -167
  43. data/spec/brainstem/presenter_validator_spec.rb +209 -0
  44. data/spec/brainstem/query_strategies/filter_and_search_spec.rb +46 -0
  45. data/spec/brainstem/query_strategies/filter_or_search_spec.rb +45 -0
  46. data/spec/spec_helper.rb +11 -3
  47. data/spec/spec_helpers/db.rb +32 -65
  48. data/spec/spec_helpers/presenters.rb +124 -29
  49. data/spec/spec_helpers/rr.rb +11 -0
  50. data/spec/spec_helpers/schema.rb +115 -0
  51. metadata +126 -30
  52. data/lib/brainstem/association_field.rb +0 -53
  53. data/lib/brainstem/engine.rb +0 -4
  54. data/pkg/brainstem-0.2.5.gem +0 -0
  55. data/pkg/brainstem-0.2.6.gem +0 -0
  56. data/spec/spec_helpers/cleanup.rb +0 -23
@@ -1,8 +1,7 @@
1
1
  require 'spec_helper'
2
- require 'spec_helpers/presenters'
3
2
 
4
3
  describe Brainstem::ControllerMethods do
5
- class FakeController
4
+ class TasksController
6
5
  include Brainstem::ControllerMethods
7
6
 
8
7
  attr_accessor :call_results
@@ -12,73 +11,62 @@ describe Brainstem::ControllerMethods do
12
11
  end
13
12
  end
14
13
 
15
- before do
16
- UserPresenter.presents User
17
- TaskPresenter.presents Task
18
- WorkspacePresenter.presents Workspace
19
- PostPresenter.presents Post
20
- end
21
-
22
14
  describe "#present_object" do
23
15
  before do
24
- @controller = FakeController.new
16
+ @controller = TasksController.new
25
17
  end
26
18
 
27
19
  describe "calling #present with sensible params" do
28
20
  before do
29
- def @controller.present(klass, options)
21
+ def @controller.brainstem_present(klass, options)
30
22
  @call_results = { :klass => klass, :options => options, :block_result => yield }
31
23
  end
32
24
  end
33
25
 
34
26
  it "works with arrays of ActiveRecord objects" do
35
- @controller.present_object([Workspace.find(1), Workspace.find(3)])
27
+ @controller.brainstem_present_object([Workspace.find(1), Workspace.find(3)])
36
28
  expect(@controller.call_results[:klass]).to eq(Workspace)
37
- expect(@controller.call_results[:options][:as]).to eq("workspaces")
38
29
  expect(@controller.call_results[:block_result].pluck(:id)).to eq([1, 3])
39
30
  end
40
31
 
41
32
  it "works with a Relation" do
42
- @controller.present_object(Workspace.owned_by(1))
33
+ @controller.brainstem_present_object(Workspace.owned_by(1))
43
34
  expect(@controller.call_results[:klass]).to eq(Workspace)
44
- expect(@controller.call_results[:options][:as]).to eq("workspaces")
45
35
  expect(@controller.call_results[:block_result].pluck(:id)).to eq([1, 2, 3, 4])
46
36
  end
47
37
 
48
38
  it "works with singleton objects" do
49
- @controller.present_object(Workspace.find(1))
39
+ @controller.brainstem_present_object(Workspace.find(1))
50
40
  expect(@controller.call_results[:klass]).to eq(Workspace)
51
- expect(@controller.call_results[:options][:as]).to eq("workspaces")
52
41
  expect(@controller.call_results[:block_result].pluck(:id)).to eq([1])
53
42
  end
54
43
 
55
- it "accepts a key map" do
56
- @controller.present_object(Workspace.find(1), :key_map => { "Workspace" => "your_workspaces" })
57
- expect(@controller.call_results[:klass]).to eq(Workspace)
58
- expect(@controller.call_results[:options][:as]).to eq("your_workspaces")
59
- expect(@controller.call_results[:block_result].pluck(:id)).to eq([1])
44
+ it "raises an error when given a key_map" do
45
+ expect {
46
+ @controller.brainstem_present_object(Workspace.find(1), :key_map => { "Workspace" => "your_workspaces" })
47
+ }.to raise_error(/brainstem_key annotation/)
60
48
  end
61
49
 
62
50
  it "passes through the controller params" do
63
- @controller.present_object(Workspace.find(1), :key_map => { "Workspace" => "your_workspaces" })
51
+ @controller.brainstem_present_object(Workspace.find(1))
64
52
  expect(@controller.call_results[:options][:params]).to eq(@controller.params.merge(:only => '1'))
65
53
  end
66
54
 
67
55
  it "passes through supplied options" do
68
- @controller.present_object(Workspace.find(1), :foo => :bar)
56
+ @controller.brainstem_present_object(Workspace.find(1), :foo => :bar)
69
57
  expect(@controller.call_results[:options][:foo]).to eq(:bar)
70
58
  end
71
59
 
72
60
  it "adds an only param if there is only one object to present" do
73
- @controller.present_object(Workspace.find(1))
61
+ @controller.brainstem_present_object(Workspace.find(1))
74
62
  expect(@controller.call_results[:options][:params][:only]).to eq("1")
75
63
 
76
- @controller.present_object(Workspace.all)
64
+ @controller.brainstem_present_object(Workspace.all)
77
65
  expect(@controller.call_results[:options][:params][:only]).to be_nil
78
66
  end
79
67
 
80
68
  it "passes :apply_default_filters => false to the PresenterCollection so that filters are not applied by default" do
81
- @controller.present_object(Workspace.find(1))
69
+ @controller.brainstem_present_object(Workspace.find(1))
82
70
  expect(@controller.call_results[:options][:apply_default_filters]).to eq(false)
83
71
  end
84
72
  end
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/dsl/association'
3
+
4
+ describe Brainstem::DSL::Association do
5
+ let(:name) { :user }
6
+ let(:target_class) { User }
7
+ let(:description) { "This object's user" }
8
+ let(:options) { { } }
9
+ let(:association) { Brainstem::DSL::Association.new(name, target_class, description, options) }
10
+
11
+ describe "#run_on" do
12
+ let(:context) { { } }
13
+
14
+ context 'with no special options' do
15
+ it 'calls the method by name on the model' do
16
+ object = Object.new
17
+ mock(object).user
18
+ association.run_on(object, context)
19
+ end
20
+ end
21
+
22
+ context 'when given a via' do
23
+ let(:options) { { via: :user2 } }
24
+
25
+ it 'calls the method named in :via on the model' do
26
+ object = Object.new
27
+ mock(object).user2
28
+ association.run_on(object, context)
29
+ end
30
+ end
31
+
32
+ context 'when given a dynamic lambda' do
33
+ let(:options) { { dynamic: lambda { |model| some_instance_method; :return_value } } }
34
+
35
+ it 'calls the lambda in the context of the given instance' do
36
+ instance = Object.new
37
+ mock(instance).some_instance_method
38
+ expect(association.run_on(:anything, context, instance)).to eq :return_value
39
+ end
40
+ end
41
+
42
+ context 'when given a lookup lambda' do
43
+ let(:options) { { lookup: lambda { |models| some_instance_method; Hash[models.map { |model| [model.id, model.username] }] } } }
44
+ let(:first_model) { target_class.create(username: 'Ben') }
45
+ let(:second_model) { target_class.create(username: 'Nate') }
46
+ let(:models) { [first_model, second_model] }
47
+ let(:context) {
48
+ {
49
+ lookup: Brainstem::Presenter.new.send(:empty_lookup_cache, [], [name.to_s]),
50
+ models: models
51
+ }
52
+ }
53
+ # {:lookup=>{:fields=>{}, :associations=>{"user"=>nil}}}
54
+
55
+ context 'The first model is ran' do
56
+ it 'builds lookup cache and returns the value for the first model' do
57
+ expect(context[:lookup][:associations][name.to_s]).to eq(nil)
58
+ instance = Object.new
59
+ mock(instance).some_instance_method
60
+ expect(association.run_on(first_model, context, instance)).to eq('Ben')
61
+ expect(context[:lookup][:associations][name.to_s]).to eq({ first_model.id => 'Ben', second_model.id => 'Nate' })
62
+ end
63
+ end
64
+
65
+ context 'The second model is ran after the first' do
66
+ it 'returns the value from the lookup cache and does not run the lookup' do
67
+ instance = Object.new
68
+ mock(instance).some_instance_method
69
+ association.run_on(first_model, context, instance)
70
+ expect(context[:lookup][:associations][name.to_s]).to eq({ first_model.id => 'Ben', second_model.id => 'Nate' })
71
+
72
+ mock(instance).some_instance_method.never
73
+ expect(association.run_on(second_model, context, instance)).to eq('Nate')
74
+ end
75
+ end
76
+
77
+ context 'with no lookup_fetch' do
78
+ context 'when the lookup returns on object which does not respond to []' do
79
+ let(:options) { { lookup: lambda { |models| nil } } }
80
+
81
+ it 'should raise error explaining the default lookup fetch relies on [] to access the model\'s value from the lookup' do
82
+ expect {
83
+ association.run_on(first_model, context)
84
+ }.to raise_error(StandardError, 'Brainstem expects the return result of the `lookup` to be a Hash since it must respond to [] in order to access the model\'s assocation(s). Default: lookup_fetch: lambda { |lookup, model| lookup[model.id] }`')
85
+ end
86
+ end
87
+ end
88
+
89
+ context 'with a dynamic lambda' do
90
+ let(:options) {
91
+ {
92
+ lookup: lambda { |models| lookup_instance_method; Hash[models.map { |model| [model.id, model.username] }] },
93
+ dynamic: lambda { |model| dynamic_instance_method; model.username }
94
+ }
95
+ }
96
+
97
+ it 'calls the lookup lambda and not the dynamic lambda' do
98
+ instance = Object.new
99
+ mock(instance).dynamic_instance_method.never
100
+ mock(instance).lookup_instance_method
101
+ expect(association.run_on(first_model, context, instance)).to eq 'Ben'
102
+ end
103
+ end
104
+
105
+ context 'when given a lookup_fetch' do
106
+ let(:options) {
107
+ {
108
+ lookup: lambda { |models| Hash[models.map { |model| [model.id, model.username] }] },
109
+ lookup_fetch: lambda { |lookup, model| some_instance_method; lookup[model.id] }
110
+ }
111
+ }
112
+
113
+ it 'returns the value from the lookup using the lookup_fetch lambda' do
114
+ context[:lookup][:associations][name.to_s] = {}
115
+ context[:lookup][:associations][name.to_s][first_model.id] = "Ben's stubbed out Name"
116
+ instance = Object.new
117
+ mock(instance).some_instance_method
118
+ expect(association.run_on(first_model, context, instance)).to eq("Ben's stubbed out Name")
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/dsl/conditional'
3
+
4
+ describe Brainstem::DSL::Conditional do
5
+ let(:conditional) { Brainstem::DSL::Conditional.new(name, type, action, description) }
6
+ let(:model) { Workspace.first }
7
+
8
+ describe '.matches?' do
9
+ context 'as a :model conditional' do
10
+ let(:name) { :title_is_hello }
11
+ let(:type) { :model }
12
+ let(:action) { lambda { |model| model.title == 'hello' } }
13
+ let(:description) { 'visible when the title is hello' }
14
+
15
+ it 'calls the action and passes in the given model' do
16
+ model.title = 'not hello'
17
+ expect(conditional.matches?(model)).to be false
18
+ model.title = 'hello'
19
+ expect(conditional.matches?(model)).to be true
20
+ end
21
+
22
+ describe 'when given a helper instance' do
23
+ let(:action) { lambda { |model| some_method == 5 } }
24
+
25
+ it 'calls the action in the context of the given helper' do
26
+ helper_class = Class.new do
27
+ def some_method
28
+ 5
29
+ end
30
+ end
31
+ expect(conditional.matches?(model, helper_class.new)).to be true
32
+ end
33
+ end
34
+
35
+ it 'caches in the model conditional cache' do
36
+ hash = { model: {}, request: {} }
37
+ expect(conditional.matches?(model, Object.new, hash)).to be false
38
+ expect(hash).to eq({ model: { title_is_hello: false }, request: {} })
39
+
40
+ model.title = 'hello'
41
+ expect(conditional.matches?(model, Object.new, hash)).to be false
42
+
43
+ hash = { model: {}, request: {} }
44
+ expect(conditional.matches?(model, Object.new, hash)).to be true
45
+ end
46
+ end
47
+
48
+ context 'as a :request conditional' do
49
+ let(:name) { :user_is_bob }
50
+ let(:type) { :request }
51
+ let(:action) { lambda { current_user == 'bob' } }
52
+ let(:description) { 'visible only to bob' }
53
+
54
+ it 'calls the action in the helper context, without passing in any arguments' do
55
+ helper_class = Class.new do
56
+ def current_user
57
+ 'jane'
58
+ end
59
+ end
60
+ expect(conditional.matches?(model, helper_class.new)).to be false
61
+
62
+ helper_class = Class.new do
63
+ def current_user
64
+ 'bob'
65
+ end
66
+ end
67
+ expect(conditional.matches?(model, helper_class.new)).to be true
68
+ end
69
+
70
+ it 'performs caching' do
71
+ cache = { model: {}, request: {} }
72
+
73
+ helper_class = Class.new do
74
+ def current_user
75
+ 'jane'
76
+ end
77
+ end
78
+ expect(conditional.matches?(model, helper_class.new, cache)).to be false
79
+ expect(cache[:request][:user_is_bob]).to be false
80
+
81
+ helper_class = Class.new do
82
+ def current_user
83
+ 'bob'
84
+ end
85
+ end
86
+ expect(conditional.matches?(model, helper_class.new, cache)).to be false
87
+
88
+ cache = { model: {}, request: {} }
89
+ expect(conditional.matches?(model, helper_class.new, cache)).to be true
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1 @@
1
+ # This is tested in concerns/inheritable_configuration_spec.rb
@@ -0,0 +1,212 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/dsl/field'
3
+
4
+ describe Brainstem::DSL::Field do
5
+ let(:name) { :title }
6
+ let(:type) { :string }
7
+ let(:description) { 'the title of this model' }
8
+ let(:options) { { } }
9
+ let(:field) { Brainstem::DSL::Field.new(name, type, description, options) }
10
+ let(:model) { Workspace.first }
11
+
12
+ describe '#method_name' do
13
+ describe 'by default' do
14
+ it 'returns the name' do
15
+ expect(field.method_name).to eq 'title'
16
+ end
17
+ end
18
+
19
+ describe 'on dynamic fields' do
20
+ let(:options) { { dynamic: lambda { 2 } } }
21
+
22
+ it 'returns nil' do
23
+ expect(field.method_name).to be_nil
24
+ end
25
+ end
26
+
27
+ describe 'when :via is present' do
28
+ let(:options) { { via: :description } }
29
+
30
+ it 'uses the :via method name' do
31
+ expect(field.method_name).to eq 'description'
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '#run_on' do
37
+ let(:context) { { } }
38
+
39
+ context 'on :dynamic fields' do
40
+ let(:options) { { dynamic: lambda { some_instance_method } } }
41
+
42
+ it 'calls the :dynamic lambda in the context of the given instance' do
43
+ do_not_allow(model).title
44
+ instance = Object.new
45
+ mock(instance).some_instance_method
46
+ field.run_on(model, context, instance)
47
+ end
48
+ end
49
+
50
+ context 'on :lookup fields' do
51
+ let(:options) { { lookup: lambda { |models| some_instance_method; Hash[models.map { |model| [model.id, model.title] }] } } }
52
+ let(:first_model) { Workspace.create(title: "Ben's Project") }
53
+ let(:second_model) { Workspace.create(title: "Nate's Project") }
54
+ let(:models) { [first_model, second_model] }
55
+ let(:context) {
56
+ {
57
+ lookup: Brainstem::Presenter.new.send(:empty_lookup_cache, [name.to_s], []),
58
+ models: models
59
+ }
60
+ }
61
+ # {:lookup=>{:fields=>{"title"=>nil}, :associations=>{}}
62
+
63
+ context 'The first model is ran' do
64
+ it 'builds lookup cache and returns the value for the first model' do
65
+ expect(context[:lookup][:fields][name.to_s]).to eq(nil)
66
+ instance = Object.new
67
+ mock(instance).some_instance_method
68
+ expect(field.run_on(first_model, context, instance)).to eq("Ben's Project")
69
+ expect(context[:lookup][:fields][name.to_s]).to eq({ first_model.id => "Ben's Project", second_model.id => "Nate's Project" })
70
+ end
71
+ end
72
+
73
+ context 'The second model is ran after the first' do
74
+ it 'returns the value from the lookup cache and does not run the lookup' do
75
+ instance = Object.new
76
+ mock(instance).some_instance_method
77
+ field.run_on(first_model, context, instance)
78
+ expect(context[:lookup][:fields][name.to_s]).to eq({ first_model.id => "Ben's Project", second_model.id => "Nate's Project" })
79
+
80
+ mock(instance).some_instance_method.never
81
+ expect(field.run_on(second_model, context, instance)).to eq("Nate's Project")
82
+ end
83
+ end
84
+
85
+ context 'with no lookup_fetch' do
86
+ context 'when the lookup returns on object which does not respond to []' do
87
+ let(:options) { { lookup: lambda { |models| nil } } }
88
+
89
+ it 'should raise error explaining the default lookup fetch relies on [] to access the model\'s value from the lookup' do
90
+ expect {
91
+ field.run_on(first_model, context)
92
+ }.to raise_error(StandardError, 'Brainstem expects the return result of the `lookup` to be a Hash since it must respond to [] in order to access the model\'s assocation(s). Default: lookup_fetch: lambda { |lookup, model| lookup[model.id] }`')
93
+ end
94
+ end
95
+ end
96
+
97
+ context 'with a dynamic lambda' do
98
+ let(:options) {
99
+ {
100
+ lookup: lambda { |models| lookup_instance_method; Hash[models.map { |model| [model.id, model.title] }] },
101
+ dynamic: lambda { |model| dynamic_instance_method; model.title }
102
+ }
103
+ }
104
+
105
+ it 'does not use the dynamic lambda' do
106
+ instance = Object.new
107
+ mock(instance).dynamic_instance_method.never
108
+ mock(instance).lookup_instance_method
109
+ expect(field.run_on(first_model, context, instance)).to eq "Ben's Project"
110
+ end
111
+ end
112
+
113
+ context 'on :lookup_fetch' do
114
+ let(:options) {
115
+ {
116
+ lookup: lambda { |models| Hash[models.map { |model| [model.id, model.title] }] },
117
+ lookup_fetch: lambda { |lookup, model| some_instance_method; lookup[model.id] }
118
+ }
119
+ }
120
+
121
+ it 'returns the value from the lookup using the lookup_fetch lambda' do
122
+ context[:lookup][:fields][name.to_s] = {}
123
+ context[:lookup][:fields][name.to_s][first_model.id] = "Ben's stubbed out Project"
124
+ instance = Object.new
125
+ mock(instance).some_instance_method
126
+ expect(field.run_on(first_model, context, instance)).to eq("Ben's stubbed out Project")
127
+ end
128
+ end
129
+ end
130
+
131
+ context 'on non-:dynamic fields' do
132
+ it 'calls method_name on the model' do
133
+ mock(model).foo
134
+ mock(field).method_name { 'foo' }
135
+ field.run_on(model, context)
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '#conditionals_match?' do
141
+ let(:fake_conditional) do
142
+ Class.new do
143
+ def initialize(result)
144
+ @result = result
145
+ end
146
+
147
+ def matches?(model, helper_instance, conditional_cache)
148
+ @result
149
+ end
150
+ end
151
+ end
152
+
153
+ context 'when no :if option has been set on the field' do
154
+ it 'returns true when there are no conditions' do
155
+ expect(field.conditionals_match?(model, WorkspacePresenter.configuration[:conditionals])).to eq true
156
+ end
157
+ end
158
+
159
+ context 'when a single :if has been set' do
160
+ let(:options) { { if: :title_is_hello } }
161
+
162
+ it 'returns true if the conditional matches' do
163
+ expect(field.conditionals_match?(model, title_is_hello: fake_conditional.new(true))).to eq true
164
+ end
165
+
166
+ it 'returns false if the conditional does not match' do
167
+ expect(field.conditionals_match?(model, title_is_hello: fake_conditional.new(false))).to eq false
168
+ end
169
+ end
170
+
171
+ context 'when multiple :if options have been passed' do
172
+ let(:options) { { if: [:title_is_hello, :user_is_bob] } }
173
+
174
+ it 'returns true if all of the conditionals match' do
175
+ expect(field.conditionals_match?(model, title_is_hello: fake_conditional.new(true), user_is_bob: fake_conditional.new(false))).to eq false
176
+ expect(field.conditionals_match?(model, title_is_hello: fake_conditional.new(false), user_is_bob: fake_conditional.new(true))).to eq false
177
+ expect(field.conditionals_match?(model, title_is_hello: fake_conditional.new(true), user_is_bob: fake_conditional.new(true))).to eq true
178
+ expect(field.conditionals_match?(model,
179
+ unknown: fake_conditional.new(false),
180
+ title_is_hello: fake_conditional.new(true),
181
+ user_is_bob: fake_conditional.new(true))).to eq true
182
+ end
183
+ end
184
+ end
185
+
186
+ describe "#optioned?" do
187
+ context "when is not an optional field" do
188
+ it 'returns true' do
189
+ expect(field.optioned?(['some_optional_field', 'some_other_optional_field'])).to eq true
190
+ end
191
+ end
192
+
193
+ context "when is an optional field" do
194
+ let(:name) { :expensive_title }
195
+ let(:type) { :string }
196
+ let(:options) { { optional: true } }
197
+ let(:description) { 'the optional expensive title field' }
198
+
199
+ context "when not requested" do
200
+ it 'returns false' do
201
+ expect(field.optioned?([])).to eq false
202
+ end
203
+ end
204
+
205
+ context "when requested" do
206
+ it 'returns true' do
207
+ expect(field.optioned?(['expensive_title', 'some_other_optional_field'])).to eq true
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end