brainstem 0.2.6.1 → 1.0.0.pre.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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