brainstem 1.0.0.pre.1 → 1.0.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.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +383 -32
  5. data/bin/brainstem +6 -0
  6. data/brainstem.gemspec +2 -0
  7. data/docs/api_doc_generator.markdown +175 -0
  8. data/docs/brainstem_executable.markdown +32 -0
  9. data/docs/docgen.png +0 -0
  10. data/docs/docgen_ascii.txt +63 -0
  11. data/docs/executable.png +0 -0
  12. data/docs/executable_ascii.txt +10 -0
  13. data/lib/brainstem/api_docs.rb +146 -0
  14. data/lib/brainstem/api_docs/abstract_collection.rb +116 -0
  15. data/lib/brainstem/api_docs/atlas.rb +158 -0
  16. data/lib/brainstem/api_docs/builder.rb +167 -0
  17. data/lib/brainstem/api_docs/controller.rb +122 -0
  18. data/lib/brainstem/api_docs/controller_collection.rb +40 -0
  19. data/lib/brainstem/api_docs/endpoint.rb +234 -0
  20. data/lib/brainstem/api_docs/endpoint_collection.rb +58 -0
  21. data/lib/brainstem/api_docs/exceptions.rb +8 -0
  22. data/lib/brainstem/api_docs/formatters/abstract_formatter.rb +64 -0
  23. data/lib/brainstem/api_docs/formatters/markdown/controller_formatter.rb +76 -0
  24. data/lib/brainstem/api_docs/formatters/markdown/endpoint_collection_formatter.rb +73 -0
  25. data/lib/brainstem/api_docs/formatters/markdown/endpoint_formatter.rb +169 -0
  26. data/lib/brainstem/api_docs/formatters/markdown/helper.rb +76 -0
  27. data/lib/brainstem/api_docs/formatters/markdown/presenter_formatter.rb +200 -0
  28. data/lib/brainstem/api_docs/introspectors/abstract_introspector.rb +100 -0
  29. data/lib/brainstem/api_docs/introspectors/rails_introspector.rb +232 -0
  30. data/lib/brainstem/api_docs/presenter.rb +225 -0
  31. data/lib/brainstem/api_docs/presenter_collection.rb +97 -0
  32. data/lib/brainstem/api_docs/resolver.rb +73 -0
  33. data/lib/brainstem/api_docs/sinks/abstract_sink.rb +37 -0
  34. data/lib/brainstem/api_docs/sinks/controller_presenter_multifile_sink.rb +93 -0
  35. data/lib/brainstem/api_docs/sinks/stdout_sink.rb +44 -0
  36. data/lib/brainstem/cli.rb +146 -0
  37. data/lib/brainstem/cli/abstract_command.rb +97 -0
  38. data/lib/brainstem/cli/generate_api_docs_command.rb +169 -0
  39. data/lib/brainstem/concerns/controller_dsl.rb +300 -0
  40. data/lib/brainstem/concerns/controller_param_management.rb +30 -9
  41. data/lib/brainstem/concerns/formattable.rb +38 -0
  42. data/lib/brainstem/concerns/inheritable_configuration.rb +3 -2
  43. data/lib/brainstem/concerns/optional.rb +43 -0
  44. data/lib/brainstem/concerns/presenter_dsl.rb +76 -15
  45. data/lib/brainstem/controller_methods.rb +6 -3
  46. data/lib/brainstem/dsl/association.rb +6 -3
  47. data/lib/brainstem/dsl/associations_block.rb +6 -3
  48. data/lib/brainstem/dsl/base_block.rb +2 -4
  49. data/lib/brainstem/dsl/conditional.rb +7 -3
  50. data/lib/brainstem/dsl/conditionals_block.rb +4 -4
  51. data/lib/brainstem/dsl/configuration.rb +184 -8
  52. data/lib/brainstem/dsl/field.rb +6 -3
  53. data/lib/brainstem/dsl/fields_block.rb +2 -3
  54. data/lib/brainstem/help_text.txt +8 -0
  55. data/lib/brainstem/presenter.rb +27 -6
  56. data/lib/brainstem/presenter_validator.rb +5 -2
  57. data/lib/brainstem/time_classes.rb +1 -1
  58. data/lib/brainstem/version.rb +1 -1
  59. data/spec/brainstem/api_docs/abstract_collection_spec.rb +156 -0
  60. data/spec/brainstem/api_docs/atlas_spec.rb +353 -0
  61. data/spec/brainstem/api_docs/builder_spec.rb +100 -0
  62. data/spec/brainstem/api_docs/controller_collection_spec.rb +92 -0
  63. data/spec/brainstem/api_docs/controller_spec.rb +225 -0
  64. data/spec/brainstem/api_docs/endpoint_collection_spec.rb +144 -0
  65. data/spec/brainstem/api_docs/endpoint_spec.rb +346 -0
  66. data/spec/brainstem/api_docs/formatters/abstract_formatter_spec.rb +30 -0
  67. data/spec/brainstem/api_docs/formatters/markdown/controller_formatter_spec.rb +126 -0
  68. data/spec/brainstem/api_docs/formatters/markdown/endpoint_collection_formatter_spec.rb +85 -0
  69. data/spec/brainstem/api_docs/formatters/markdown/endpoint_formatter_spec.rb +261 -0
  70. data/spec/brainstem/api_docs/formatters/markdown/helper_spec.rb +100 -0
  71. data/spec/brainstem/api_docs/formatters/markdown/presenter_formatter_spec.rb +485 -0
  72. data/spec/brainstem/api_docs/introspectors/abstract_introspector_spec.rb +192 -0
  73. data/spec/brainstem/api_docs/introspectors/rails_introspector_spec.rb +170 -0
  74. data/spec/brainstem/api_docs/presenter_collection_spec.rb +84 -0
  75. data/spec/brainstem/api_docs/presenter_spec.rb +519 -0
  76. data/spec/brainstem/api_docs/resolver_spec.rb +72 -0
  77. data/spec/brainstem/api_docs/sinks/abstract_sink_spec.rb +16 -0
  78. data/spec/brainstem/api_docs/sinks/controller_presenter_multifile_sink_spec.rb +56 -0
  79. data/spec/brainstem/api_docs/sinks/stdout_sink_spec.rb +22 -0
  80. data/spec/brainstem/api_docs_spec.rb +58 -0
  81. data/spec/brainstem/cli/abstract_command_spec.rb +91 -0
  82. data/spec/brainstem/cli/generate_api_docs_command_spec.rb +125 -0
  83. data/spec/brainstem/cli_spec.rb +67 -0
  84. data/spec/brainstem/concerns/controller_dsl_spec.rb +471 -0
  85. data/spec/brainstem/concerns/controller_param_management_spec.rb +36 -16
  86. data/spec/brainstem/concerns/formattable_spec.rb +30 -0
  87. data/spec/brainstem/concerns/inheritable_configuration_spec.rb +104 -4
  88. data/spec/brainstem/concerns/optional_spec.rb +48 -0
  89. data/spec/brainstem/concerns/presenter_dsl_spec.rb +202 -31
  90. data/spec/brainstem/dsl/association_spec.rb +18 -2
  91. data/spec/brainstem/dsl/conditional_spec.rb +25 -2
  92. data/spec/brainstem/dsl/configuration_spec.rb +1 -1
  93. data/spec/brainstem/dsl/field_spec.rb +18 -2
  94. data/spec/brainstem/presenter_collection_spec.rb +10 -2
  95. data/spec/brainstem/presenter_spec.rb +32 -0
  96. data/spec/brainstem/presenter_validator_spec.rb +12 -7
  97. data/spec/dummy/rails.rb +49 -0
  98. data/spec/shared/atlas_taker.rb +18 -0
  99. data/spec/shared/formattable.rb +14 -0
  100. data/spec/spec_helper.rb +2 -0
  101. data/spec/spec_helpers/db.rb +1 -1
  102. data/spec/spec_helpers/presenters.rb +20 -14
  103. metadata +106 -6
@@ -2,41 +2,61 @@ require 'spec_helper'
2
2
  require 'brainstem/concerns/controller_param_management'
3
3
 
4
4
  describe Brainstem::Concerns::ControllerParamManagement do
5
- class TasksController
6
- include Brainstem::Concerns::ControllerParamManagement
5
+ subject do
6
+ Class.new do
7
+ include Brainstem::Concerns::ControllerParamManagement
7
8
 
8
- def controller_name
9
- 'tasks'
9
+ def controller_name
10
+ self.class.controller_name
11
+ end
12
+
13
+ def self.controller_name
14
+ 'tasks'
15
+ end
10
16
  end
11
17
  end
12
18
 
13
19
  before do
14
- TasksController.brainstem_model_name = nil
15
- TasksController.brainstem_plural_model_name = nil
20
+ subject.brainstem_model_name = nil
21
+ subject.brainstem_plural_model_name = nil
16
22
  end
17
23
 
18
24
  describe '.brainstem_model_name' do
19
25
  it 'is settable on the controller' do
20
- TasksController.brainstem_model_name = 'thingy'
21
- expect(TasksController.new.brainstem_model_name).to eq 'thingy'
26
+ subject.brainstem_model_name = 'thingy'
27
+ expect(subject.new.brainstem_model_name).to eq 'thingy'
22
28
  end
23
29
 
24
30
  it 'has good defaults' do
25
- expect(TasksController.new.brainstem_model_name).to eq 'task'
26
- expect(TasksController.new.brainstem_plural_model_name).to eq 'tasks'
31
+ expect(subject.new.brainstem_model_name).to eq 'task'
32
+ expect(subject.new.brainstem_plural_model_name).to eq 'tasks'
33
+ end
34
+
35
+ it "has good defaults on the class level" do
36
+ expect(subject.brainstem_model_name).to eq 'task'
37
+ expect(subject.brainstem_plural_model_name).to eq 'tasks'
27
38
  end
28
39
  end
29
40
 
30
41
  describe '.brainstem_plural_model_name' do
31
42
  it 'is infered from the singular model name' do
32
- TasksController.brainstem_model_name = 'thingy'
33
- expect(TasksController.new.brainstem_plural_model_name).to eq 'thingies'
43
+ subject.brainstem_model_name = 'thingy'
44
+ expect(subject.brainstem_plural_model_name).to eq 'thingies'
45
+ expect(subject.new.brainstem_plural_model_name).to eq 'thingies'
34
46
  end
35
47
 
36
48
  it 'can be overridden' do
37
- TasksController.brainstem_model_name = 'thingy'
38
- TasksController.brainstem_plural_model_name = 'thingzees'
39
- expect(TasksController.new.brainstem_plural_model_name).to eq 'thingzees'
49
+ subject.brainstem_model_name = 'thingy'
50
+ subject.brainstem_plural_model_name = 'thingzees'
51
+ expect(subject.brainstem_plural_model_name).to eq 'thingzees'
52
+ expect(subject.new.brainstem_plural_model_name).to eq 'thingzees'
53
+ end
54
+ end
55
+
56
+ describe '.brainstem_model_class' do
57
+ it "classifies and constantizes the brainstem_model_name" do
58
+ subject.brainstem_model_name = "object"
59
+ expect(subject.brainstem_model_class).to eq Object
40
60
  end
41
61
  end
42
- end
62
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/concerns/formattable'
3
+
4
+ module Brainstem
5
+ module Concerns
6
+ describe Formattable do
7
+ let(:formattable_class) do
8
+ Class.new do
9
+ include Brainstem::Concerns::Formattable
10
+ end
11
+ end
12
+
13
+ let(:subject) { formattable_class.new }
14
+
15
+ describe "#formatter_type" do
16
+ it "returns the class name underscored and symbolized" do
17
+ stub(formattable_class).to_s { "MyClass" }
18
+ expect(subject.formatter_type).to eq :my_class
19
+ end
20
+
21
+ it "returns only the last segment of a class name" do
22
+ stub(formattable_class).to_s { "Namespaced::MyClass" }
23
+ expect(subject.formatter_type).to eq :my_class
24
+
25
+ end
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -33,22 +33,51 @@ describe Brainstem::Concerns::InheritableConfiguration do
33
33
  expect(parent_class.configuration['ten']).to be_nil
34
34
  end
35
35
 
36
- describe '#keys' do
37
- it "returns the union of this class's keys with any parent keys" do
36
+ it "does not inherit nonheritable keys" do
37
+ expect(parent_class.configuration['nonheritable']).to be_nil
38
+ parent_class.configuration.nonheritable! :nonheritable
39
+ parent_class.configuration['nonheritable'] = "parent"
40
+ expect(parent_class.configuration['nonheritable']).to eq "parent"
41
+
42
+ subclass = Class.new(parent_class)
43
+ expect(subclass.configuration['nonheritable']).to be_nil
44
+
45
+ subclass.configuration['nonheritable'] = "child"
46
+
47
+ subsubclass = Class.new(subclass)
48
+ expect(subsubclass.configuration['nonheritable']).to be_nil
49
+ end
50
+
51
+ describe '#keys and #to_h' do
52
+ let(:subclass) { Class.new(parent_class) }
53
+ let(:subsubclass) { Class.new(subclass) }
54
+
55
+ before do
38
56
  parent_class.configuration['1'] = :a
39
57
  parent_class.configuration['2'] = :b
40
58
 
41
- subclass = Class.new(parent_class)
42
59
  subclass.configuration['2'] = :c
43
60
  subclass.configuration['3'] = :d
44
61
 
45
- subsubclass = Class.new(subclass)
46
62
  subsubclass.configuration['3'] = :e
47
63
  subsubclass.configuration['4'] = :f
64
+ end
48
65
 
66
+ it "returns the union of this class's keys with any parent keys" do
49
67
  expect(parent_class.configuration.keys).to eq ['1', '2']
68
+ expect(parent_class.configuration.to_h).to eq({ '1' => :a, '2' => :b })
50
69
  expect(subclass.configuration.keys).to eq ['1', '2', '3']
70
+ expect(subclass.configuration.to_h).to eq({ '1' => :a, '2' => :c, '3' => :d })
51
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 })
73
+
74
+ # it doesn't mutate storage
75
+ subclass.configuration.to_h['1'] = :new
76
+ subclass.configuration.to_h['2'] = :new
77
+ expect(subclass.configuration['1']).to eq :a
78
+ expect(subclass.configuration['2']).to eq :c
79
+ expect(parent_class.configuration['1']).to eq :a
80
+ expect(parent_class.configuration['2']).to eq :b
52
81
 
53
82
  expect(parent_class.configuration['1']).to eq :a
54
83
  expect(parent_class.configuration['2']).to eq :b
@@ -62,6 +91,64 @@ describe Brainstem::Concerns::InheritableConfiguration do
62
91
  expect(subsubclass.configuration['3']).to eq :e
63
92
  expect(subsubclass.configuration['4']).to eq :f
64
93
  end
94
+
95
+ it "does not return nonheritable keys in the parent" do
96
+ parent_class.configuration.nonheritable! :nonheritable
97
+ parent_class.configuration['nonheritable'] = "why yes, I am nonheritable"
98
+ expect(subclass.configuration.keys).not_to include 'nonheritable'
99
+
100
+ expect(subclass.configuration.to_h.keys).not_to include 'nonheritable'
101
+ expect(subclass.configuration.has_key?('nonheritable')).to eq false
102
+ end
103
+ end
104
+
105
+ describe "#fetch" do
106
+ let(:config) { parent_class.configuration }
107
+ let(:my_block) { Proc.new { nil } }
108
+
109
+ before do
110
+ parent_class.configuration["my_key"] = "yep"
111
+ end
112
+
113
+ context "when key is found" do
114
+ it "returns the key" do
115
+ expect(config.fetch("my_key")).to eq "yep"
116
+ end
117
+ end
118
+
119
+ context "when key is not found" do
120
+ context "when default or block not given" do
121
+ it "raises a KeyError exception" do
122
+ expect { config.fetch("fake_key") }.to raise_exception KeyError
123
+ end
124
+ end
125
+
126
+ context "when block given" do
127
+ before do
128
+ mock(my_block).call { "hey" }
129
+ end
130
+
131
+ it "evals and returns the block" do
132
+ expect(config.fetch("fake_key", &my_block)).to eq "hey"
133
+ end
134
+ end
135
+
136
+ context "when default given" do
137
+ it "returns the default" do
138
+ expect(config.fetch("fake_key", "hey")).to eq "hey"
139
+ end
140
+ end
141
+
142
+ context "when default and block given" do
143
+ before do
144
+ mock(my_block).call("hey") { "sup" }
145
+ end
146
+
147
+ it "evals and returns the block, passing it the default" do
148
+ expect(config.fetch("fake_key", "hey", &my_block)).to eq "sup"
149
+ end
150
+ end
151
+ end
65
152
  end
66
153
 
67
154
  describe '#nest!' do
@@ -199,6 +286,19 @@ describe Brainstem::Concerns::InheritableConfiguration do
199
286
  expect(lambda { subclass.configuration['list'] = 2 }).to raise_error('You cannot override an inheritable array once set')
200
287
  end
201
288
  end
289
+
290
+ describe "#nonheritable!" do
291
+ it "adds the key to the nonheritable attributes list" do
292
+ parent_class.configuration.nonheritable! :nonheritable
293
+ expect(parent_class.configuration.nonheritable_keys.to_a).to eq ["nonheritable"]
294
+ end
295
+
296
+ it "dedupes" do
297
+ parent_class.configuration.nonheritable! :nonheritable
298
+ parent_class.configuration.nonheritable! :nonheritable
299
+ expect(parent_class.configuration.nonheritable_keys.to_a).to eq ["nonheritable"]
300
+ end
301
+ end
202
302
  end
203
303
 
204
304
  describe '#configuration' do
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/concerns/optional'
3
+
4
+ describe Brainstem::Concerns::Optional do
5
+ let(:optional_class) do
6
+ Class.new do
7
+ include Brainstem::Concerns::Optional
8
+ end
9
+ end
10
+
11
+ context "when options are not passed" do
12
+ it "raises no error" do
13
+ expect { optional_class.new }.not_to raise_error
14
+ end
15
+ end
16
+
17
+ context "when options are passed" do
18
+ context "when option is whitelisted" do
19
+ before do
20
+ stub.any_instance_of(optional_class).valid_options { [:thing] }
21
+ end
22
+
23
+ context "when an accessor exists" do
24
+ before do
25
+ mock.any_instance_of(optional_class).thing=("blah")
26
+ end
27
+
28
+ it "passes the option to the accessor" do
29
+ optional_class.new(thing: "blah")
30
+ end
31
+ end
32
+
33
+ context "when no accessor exists" do
34
+ it "raises an error" do
35
+ expect { optional_class.new(thing: "blah") }.to \
36
+ raise_error NoMethodError
37
+ end
38
+ end
39
+ end
40
+
41
+ context "when option is not whitelisted" do
42
+ it "does not send the symbol" do
43
+ dont_allow.any_instance_of(optional_class).other_thing
44
+ expect { optional_class.new(other_thing: "nope") }.not_to raise_error
45
+ end
46
+ end
47
+ end
48
+ end
@@ -5,11 +5,17 @@ require 'brainstem/concerns/presenter_dsl'
5
5
  #
6
6
  # brainstem_key :projects
7
7
  #
8
+ # title "Project"
9
+ # description "It does stuff"
10
+ #
11
+ #
8
12
  # conditionals do
9
- # model :title_is_hello, lambda { workspace.title == 'hello' }, 'visible when the title is hello'
10
- # request :user_is_bob, lambda { current_user.username == 'bob' }, 'visible only to bob'
13
+ # model :title_is_hello, lambda { workspace.title == 'hello' }, info: 'visible when the title is hello'
14
+ # request :user_is_bob, lambda { current_user.username == 'bob' }, info: 'visible only to bob'
11
15
  # end
12
16
  #
17
+ # sort_order :created_at, ".created_at"
18
+ #
13
19
  # fields do
14
20
  # field :title, :string
15
21
  # field :description, :string
@@ -20,7 +26,8 @@ require 'brainstem/concerns/presenter_dsl'
20
26
  # if: [:user_is_bob, :title_is_hello]
21
27
  #
22
28
  # with_options if: :user_is_bob do
23
- # field :bob_title, :string, 'another name for the title, only for Bob',
29
+ # field :bob_title, :string,
30
+ # info: 'another name for the title, only for Bob',
24
31
  # via: :title
25
32
  # end
26
33
  # fields :nested_permissions do
@@ -30,10 +37,13 @@ require 'brainstem/concerns/presenter_dsl'
30
37
  # end
31
38
  #
32
39
  # associations do
33
- # association :tasks, Task, 'The Tasks in this Workspace',
40
+ # association :tasks, Task,
41
+ # info: 'The Tasks in this Workspace',
34
42
  # restrict_to_only: true
35
- # association :lead_user, User, 'The user who runs this Workspace'
36
- # association :subtasks, Task, 'Only Tasks in this Workspace that are subtasks',
43
+ # association :lead_user, User,
44
+ # info: 'The user who runs this Workspace'
45
+ # association :subtasks, Task,
46
+ # info: 'Only Tasks in this Workspace that are subtasks',
37
47
  # dynamic: lambda { |workspace| workspace.tasks.where('parent_id IS NOT NULL') }
38
48
  # association :something, :polymorphic
39
49
  # end
@@ -62,11 +72,99 @@ describe Brainstem::Concerns::PresenterDSL do
62
72
  end
63
73
  end
64
74
 
75
+ describe 'the title method' do
76
+ it "is stored in the configuration" do
77
+ presenter_class.title "Project"
78
+ expect(presenter_class.configuration[:title][:info]).to eq "Project"
79
+ end
80
+
81
+ it "stores options" do
82
+ presenter_class.title "Project", nodoc: true
83
+ expect(presenter_class.configuration[:title][:nodoc]).to be true
84
+ end
85
+
86
+ it "is not inherited" do
87
+ presenter_class.title "Project"
88
+ subclass = Class.new(presenter_class)
89
+ expect(subclass.configuration).not_to have_key :title
90
+ end
91
+ end
92
+
93
+ describe 'the description method' do
94
+ it "is stored in the configuration" do
95
+ presenter_class.description "desc 123"
96
+ expect(presenter_class.configuration[:description][:info]).to eq "desc 123"
97
+ end
98
+
99
+ it "stores options" do
100
+ presenter_class.description "Project", nodoc: true
101
+ expect(presenter_class.configuration[:description][:nodoc]).to be true
102
+ end
103
+
104
+ it "is not inherited" do
105
+ presenter_class.description "desc 123"
106
+ subclass = Class.new(presenter_class)
107
+ expect(subclass.configuration).not_to have_key :description
108
+ end
109
+ end
110
+
111
+ describe 'the nodoc! method' do
112
+ before do
113
+ presenter_class.nodoc!
114
+ end
115
+
116
+ it "is stored in the configuration" do
117
+ expect(presenter_class.configuration[:nodoc]).to be true
118
+ end
119
+ end
120
+
121
+
122
+ # sort_order :created_at, ".created_at"
123
+ describe 'the sort_order block' do
124
+ let(:value) { "widgets.created_at" }
125
+ let(:orders) { presenter_class.configuration[:sort_orders] }
126
+
127
+ context "when passed value is a column" do
128
+ before do
129
+ presenter_class.sort_order :created_at, value, info: "sorts by creation time"
130
+ end
131
+
132
+ it "is stored in the configuration by name" do
133
+ expect(orders).to have_key "created_at"
134
+ end
135
+
136
+ it "stores the value passed" do
137
+ expect(orders[:created_at][:value]).to eq "widgets.created_at"
138
+ end
139
+
140
+ it "stores the documentation" do
141
+ expect(orders[:created_at][:info]).to eq "sorts by creation time"
142
+ end
143
+ end
144
+
145
+ context "when passed value is a block" do
146
+ let(:value) { Proc.new { nil } }
147
+
148
+ before do
149
+ presenter_class.sort_order :created_at, info: "sorts by creation time", &value
150
+ end
151
+
152
+ it "stores the value passed" do
153
+ expect(orders[:created_at][:value]).to eq value
154
+ end
155
+
156
+ it "stores the documentation" do
157
+ expect(orders[:created_at][:info]).to eq "sorts by creation time"
158
+ end
159
+ end
160
+ end
161
+
162
+
65
163
  describe 'the conditional block' do
66
164
  before do
67
165
  presenter_class.conditionals do
68
- model :title_is_hello, lambda { |workspace| workspace.title == 'hello' }, 'visible when the title is hello'
69
- request :user_is_bob, lambda { current_user == 'bob' }, 'visible only to bob'
166
+ model :title_is_hello, lambda { |workspace| workspace.title == 'hello' }, info: 'visible when the title is hello', nodoc: true
167
+ request :user_is_bob, lambda { current_user == 'bob' }, info: 'visible only to bob'
70
168
  end
71
169
  end
72
170
 
@@ -75,6 +173,7 @@ describe Brainstem::Concerns::PresenterDSL do
75
173
  expect(presenter_class.configuration[:conditionals][:title_is_hello].action).to be_present
76
174
  expect(presenter_class.configuration[:conditionals][:title_is_hello].type).to eq :model
77
175
  expect(presenter_class.configuration[:conditionals][:title_is_hello].description).to eq 'visible when the title is hello'
176
+ expect(presenter_class.configuration[:conditionals][:title_is_hello].options).to eq({ nodoc: true, info: 'visible when the title is hello' })
78
177
  expect(presenter_class.configuration[:conditionals][:user_is_bob].action).to be_present
79
178
  expect(presenter_class.configuration[:conditionals][:user_is_bob].type).to eq :request
80
179
  expect(presenter_class.configuration[:conditionals][:user_is_bob].description).to eq 'visible only to bob'
@@ -83,13 +182,30 @@ describe Brainstem::Concerns::PresenterDSL do
83
182
  it 'is inherited and overridable' do
84
183
  subclass = Class.new(presenter_class)
85
184
  subclass.conditionals do
86
- model :silly_conditional, lambda { rand > 0.5 }, 'visible half the time'
87
- model :title_is_hello, lambda { |workspace| workspace.title == 'HELLO' }, 'visible when the title is hello (in all caps)'
185
+ model :silly_conditional, lambda { rand > 0.5 }, info: 'visible half the time'
186
+ model :title_is_hello, lambda { |workspace| workspace.title == 'HELLO' }, info: 'visible when the title is hello (in all caps)'
88
187
  end
89
188
  expect(presenter_class.configuration[:conditionals].keys).to eq %w[title_is_hello user_is_bob]
90
189
  expect(subclass.configuration[:conditionals].keys).to eq %w[title_is_hello user_is_bob silly_conditional]
91
190
  expect(presenter_class.configuration[:conditionals][:title_is_hello].description).to eq "visible when the title is hello"
191
+ expect(presenter_class.configuration[:conditionals][:title_is_hello].options).to eq({ nodoc: true, info: "visible when the title is hello" })
92
192
  expect(subclass.configuration[:conditionals][:title_is_hello].description).to eq "visible when the title is hello (in all caps)"
193
+ expect(subclass.configuration[:conditionals][:title_is_hello].options).to eq({ info: "visible when the title is hello (in all caps)" })
194
+ end
195
+
196
+ context 'when options hash is a hash with indifferent access' do
197
+ before do
198
+ presenter_class.conditionals do
199
+ request :user_is_jane, lambda { current_user == 'jane' }, { info: 'visible only to Jane' }.with_indifferent_access
200
+ end
201
+ end
202
+
203
+ it 'is stored in the configuration correctly' do
204
+ expect(presenter_class.configuration[:conditionals].keys).to include('user_is_jane')
205
+ expect(presenter_class.configuration[:conditionals][:user_is_jane].action).to be_present
206
+ expect(presenter_class.configuration[:conditionals][:user_is_jane].type).to eq :request
207
+ expect(presenter_class.configuration[:conditionals][:user_is_jane].description).to eq 'visible only to Jane'
208
+ end
93
209
  end
94
210
  end
95
211
 
@@ -103,7 +219,8 @@ describe Brainstem::Concerns::PresenterDSL do
103
219
  if: [:user_is_bob, :title_is_hello]
104
220
 
105
221
  with_options if: :user_is_bob do
106
- field :bob_title, :string, 'another name for the title, only for Bob',
222
+ field :bob_title, :string,
223
+ info: 'another name for the title, only for Bob',
107
224
  via: :title
108
225
  end
109
226
  fields :nested_permissions do
@@ -125,7 +242,11 @@ describe Brainstem::Concerns::PresenterDSL do
125
242
  expect(presenter_class.configuration[:fields][:secret].options).to eq({ via: :secret_info, if: [:user_is_bob, :title_is_hello] })
126
243
  expect(presenter_class.configuration[:fields][:bob_title].type).to eq :string
127
244
  expect(presenter_class.configuration[:fields][:bob_title].description).to eq 'another name for the title, only for Bob'
128
- expect(presenter_class.configuration[:fields][:bob_title].options).to eq({ via: :title, if: [:user_is_bob] })
245
+ expect(presenter_class.configuration[:fields][:bob_title].options).to eq(
246
+ via: :title,
247
+ if: [:user_is_bob],
248
+ info: 'another name for the title, only for Bob'
249
+ )
129
250
  end
130
251
 
131
252
  it 'handles nesting' do
@@ -139,7 +260,7 @@ describe Brainstem::Concerns::PresenterDSL do
139
260
  subclass.fields do
140
261
  field :title, :string
141
262
  with_options if: [:some_condition, :some_other_condition] do
142
- field :updated_at, :datetime, 'this time I have a description and condition'
263
+ field :updated_at, :datetime, info: 'this time I have a description and condition'
143
264
  end
144
265
  end
145
266
  expect(presenter_class.configuration[:fields].keys).to match_array %w[updated_at dynamic_title secret bob_title nested_permissions]
@@ -147,24 +268,32 @@ describe Brainstem::Concerns::PresenterDSL do
147
268
  expect(presenter_class.configuration[:fields][:updated_at].description).to be_nil
148
269
  expect(presenter_class.configuration[:fields][:updated_at].options).to eq({})
149
270
  expect(subclass.configuration[:fields][:updated_at].description).to eq 'this time I have a description and condition'
150
- expect(subclass.configuration[:fields][:updated_at].options).to eq({ if: [:some_condition, :some_other_condition] })
271
+ expect(subclass.configuration[:fields][:updated_at].options).to eq(
272
+ if: [:some_condition, :some_other_condition],
273
+ info: 'this time I have a description and condition'
274
+ )
151
275
  end
152
276
 
153
277
  it 'any :if options are combined and inherited using with_options' do
154
278
  presenter_class.fields do
155
279
  with_options if: :user_is_bob do
156
- field :bob_title, :string, 'another name for the title, only for Bob',
157
- via: :title, if: :another_condition
158
- field :bob_title2, :string, 'another name for the title, only for Bob',
280
+ field :bob_title, :string,
281
+ info: 'another name for the title, only for Bob',
282
+ via: :title,
283
+ if: :another_condition
284
+ field :bob_title2, :string,
285
+ info: 'another name for the title, only for Bob',
159
286
  via: :title, if: :another_condition
160
287
  end
161
288
  end
162
289
  subclass = Class.new(presenter_class)
163
290
  subclass.fields do
164
291
  with_options if: [:user_is_bob, :more_specific] do
165
- field :bob_title, :string, 'another name for the title, only for Bob',
292
+ field :bob_title, :string,
293
+ info: 'another name for the title, only for Bob',
166
294
  via: :title, if: [:another_condition]
167
- field :bob_title2, :string, 'another name for the title, only for Bob',
295
+ field :bob_title2, :string,
296
+ info: 'another name for the title, only for Bob',
168
297
  via: :title
169
298
  end
170
299
  end
@@ -206,14 +335,30 @@ describe Brainstem::Concerns::PresenterDSL do
206
335
  expect(subclass.configuration[:fields][:nested_permissions][:deeper][:something]).to be_present
207
336
  expect(subclass.configuration[:fields][:new_nested_permissions]).to be_present
208
337
  end
338
+
339
+ context "when options is a hash with indifferent access" do
340
+ before do
341
+ presenter_class.fields do
342
+ field :synced_at, :datetime, { info: "Last time the object was synced" }.with_indifferent_access
343
+ end
344
+ end
345
+
346
+ it "is stored in the configuration correctly" do
347
+ expect(presenter_class.configuration[:fields].keys).to include('synced_at')
348
+ expect(presenter_class.configuration[:fields][:synced_at].type).to eq :datetime
349
+ expect(presenter_class.configuration[:fields][:synced_at].description).to eq 'Last time the object was synced'
350
+ end
351
+ end
209
352
  end
210
353
 
211
354
  describe 'the associations block' do
212
355
  before do
213
356
  presenter_class.associations do
214
- association :tasks, Task, 'The Tasks in this Workspace',
357
+ association :tasks, Task,
358
+ info: 'The Tasks in this Workspace',
215
359
  restrict_to_only: true
216
- association :subtasks, Task, 'Only Tasks in this Workspace that are subtasks',
360
+ association :subtasks, Task,
361
+ info: 'Only Tasks in this Workspace that are subtasks',
217
362
  dynamic: lambda { |workspace| workspace.tasks.where('parent_id IS NOT NULL') }
218
363
  association :something, :polymorphic
219
364
  end
@@ -223,10 +368,10 @@ describe Brainstem::Concerns::PresenterDSL do
223
368
  expect(presenter_class.configuration[:associations].keys).to match_array %w[tasks subtasks something]
224
369
  expect(presenter_class.configuration[:associations][:tasks].target_class).to eq Task
225
370
  expect(presenter_class.configuration[:associations][:tasks].description).to eq 'The Tasks in this Workspace'
226
- expect(presenter_class.configuration[:associations][:tasks].options).to eq({ restrict_to_only: true })
371
+ expect(presenter_class.configuration[:associations][:tasks].options).to eq({ restrict_to_only: true, info: 'The Tasks in this Workspace' })
227
372
  expect(presenter_class.configuration[:associations][:subtasks].target_class).to eq Task
228
373
  expect(presenter_class.configuration[:associations][:subtasks].description).to eq 'Only Tasks in this Workspace that are subtasks'
229
- expect(presenter_class.configuration[:associations][:subtasks].options.keys).to eq [:dynamic]
374
+ expect(presenter_class.configuration[:associations][:subtasks].options.keys).to match_array [:dynamic, :info]
230
375
  expect(presenter_class.configuration[:associations][:something].target_class).to eq :polymorphic
231
376
  expect(presenter_class.configuration[:associations][:something].description).to be_nil
232
377
  end
@@ -234,20 +379,37 @@ describe Brainstem::Concerns::PresenterDSL do
234
379
  it 'is inherited and overridable' do
235
380
  subclass = Class.new(presenter_class)
236
381
  subclass.associations do
237
- association :tasks, Task, 'The Tasks in this Workspace'
238
- association :lead_user, User, 'The user who runs this Workspace'
382
+ association :tasks, Task, info: 'The Tasks in this Workspace'
383
+ association :lead_user, User, info: 'The user who runs this Workspace'
239
384
  end
240
385
 
241
386
  expect(presenter_class.configuration[:associations].keys).to match_array %w[tasks subtasks something]
242
387
  expect(subclass.configuration[:associations].keys).to match_array %w[tasks subtasks lead_user something]
243
388
 
244
- expect(presenter_class.configuration[:associations][:tasks].options).to eq({ restrict_to_only: true })
389
+ expect(presenter_class.configuration[:associations][:tasks].options).to eq({ restrict_to_only: true, info: 'The Tasks in this Workspace' })
245
390
  expect(presenter_class.configuration[:associations][:lead_user]).to be_nil
246
391
 
247
- expect(subclass.configuration[:associations][:tasks].options).to eq({})
392
+ expect(subclass.configuration[:associations][:tasks].options).to eq({ info: 'The Tasks in this Workspace' })
248
393
  expect(subclass.configuration[:associations][:lead_user].target_class).to eq User
249
394
  expect(subclass.configuration[:associations][:lead_user].description).to eq 'The user who runs this Workspace'
250
395
  end
396
+
397
+ context "when options is a hash with indifferent access" do
398
+ before do
399
+ presenter_class.associations do
400
+ association :something_else, :polymorphic, { info: 'The other things in this Workspace', restrict_to_only: true }.with_indifferent_access
401
+ end
402
+ end
403
+
404
+ it "is stored in the configuration correctly" do
405
+ expect(presenter_class.configuration[:associations].keys).to include('something_else')
406
+ expect(presenter_class.configuration[:associations][:something_else].description).to eq 'The other things in this Workspace'
407
+ expect(presenter_class.configuration[:associations][:something_else].options).to eq(
408
+ info: 'The other things in this Workspace',
409
+ restrict_to_only: true
410
+ )
411
+ end
412
+ end
251
413
  end
252
414
 
253
415
  describe ".helper" do
@@ -373,16 +535,25 @@ describe Brainstem::Concerns::PresenterDSL do
373
535
  end
374
536
 
375
537
  describe ".filter" do
538
+ let(:foo) { presenter_class.configuration[:filters][:foo] }
539
+
376
540
  it "creates an entry in the filters configuration" do
377
- presenter_class.filter(:foo, :default => true) { 1 }
378
- expect(presenter_class.configuration[:filters][:foo][0]).to eq({"default" => true})
379
- expect(presenter_class.configuration[:filters][:foo][1]).to be_a(Proc)
541
+ my_proc = Proc.new { 1 }
542
+ presenter_class.filter(:foo, :default => true, &my_proc)
543
+
544
+ expect(foo).to eq({ "default" => true, "value" => my_proc })
380
545
  end
381
546
 
382
547
  it "accepts names without blocks" do
383
548
  presenter_class.filter(:foo)
384
- expect(presenter_class.configuration[:filters][:foo][1]).to be_nil
549
+ expect(foo[:value]).to be_nil
550
+ end
551
+
552
+ it "records the info option" do
553
+ presenter_class.filter(:foo, :info => "This is documented.")
554
+ expect(foo[:info]).to eq "This is documented."
385
555
  end
556
+
386
557
  end
387
558
 
388
559
  describe ".search" do