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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +1 -1
- data/README.md +383 -32
- data/bin/brainstem +6 -0
- data/brainstem.gemspec +2 -0
- data/docs/api_doc_generator.markdown +175 -0
- data/docs/brainstem_executable.markdown +32 -0
- data/docs/docgen.png +0 -0
- data/docs/docgen_ascii.txt +63 -0
- data/docs/executable.png +0 -0
- data/docs/executable_ascii.txt +10 -0
- data/lib/brainstem/api_docs.rb +146 -0
- data/lib/brainstem/api_docs/abstract_collection.rb +116 -0
- data/lib/brainstem/api_docs/atlas.rb +158 -0
- data/lib/brainstem/api_docs/builder.rb +167 -0
- data/lib/brainstem/api_docs/controller.rb +122 -0
- data/lib/brainstem/api_docs/controller_collection.rb +40 -0
- data/lib/brainstem/api_docs/endpoint.rb +234 -0
- data/lib/brainstem/api_docs/endpoint_collection.rb +58 -0
- data/lib/brainstem/api_docs/exceptions.rb +8 -0
- data/lib/brainstem/api_docs/formatters/abstract_formatter.rb +64 -0
- data/lib/brainstem/api_docs/formatters/markdown/controller_formatter.rb +76 -0
- data/lib/brainstem/api_docs/formatters/markdown/endpoint_collection_formatter.rb +73 -0
- data/lib/brainstem/api_docs/formatters/markdown/endpoint_formatter.rb +169 -0
- data/lib/brainstem/api_docs/formatters/markdown/helper.rb +76 -0
- data/lib/brainstem/api_docs/formatters/markdown/presenter_formatter.rb +200 -0
- data/lib/brainstem/api_docs/introspectors/abstract_introspector.rb +100 -0
- data/lib/brainstem/api_docs/introspectors/rails_introspector.rb +232 -0
- data/lib/brainstem/api_docs/presenter.rb +225 -0
- data/lib/brainstem/api_docs/presenter_collection.rb +97 -0
- data/lib/brainstem/api_docs/resolver.rb +73 -0
- data/lib/brainstem/api_docs/sinks/abstract_sink.rb +37 -0
- data/lib/brainstem/api_docs/sinks/controller_presenter_multifile_sink.rb +93 -0
- data/lib/brainstem/api_docs/sinks/stdout_sink.rb +44 -0
- data/lib/brainstem/cli.rb +146 -0
- data/lib/brainstem/cli/abstract_command.rb +97 -0
- data/lib/brainstem/cli/generate_api_docs_command.rb +169 -0
- data/lib/brainstem/concerns/controller_dsl.rb +300 -0
- data/lib/brainstem/concerns/controller_param_management.rb +30 -9
- data/lib/brainstem/concerns/formattable.rb +38 -0
- data/lib/brainstem/concerns/inheritable_configuration.rb +3 -2
- data/lib/brainstem/concerns/optional.rb +43 -0
- data/lib/brainstem/concerns/presenter_dsl.rb +76 -15
- data/lib/brainstem/controller_methods.rb +6 -3
- data/lib/brainstem/dsl/association.rb +6 -3
- data/lib/brainstem/dsl/associations_block.rb +6 -3
- data/lib/brainstem/dsl/base_block.rb +2 -4
- data/lib/brainstem/dsl/conditional.rb +7 -3
- data/lib/brainstem/dsl/conditionals_block.rb +4 -4
- data/lib/brainstem/dsl/configuration.rb +184 -8
- data/lib/brainstem/dsl/field.rb +6 -3
- data/lib/brainstem/dsl/fields_block.rb +2 -3
- data/lib/brainstem/help_text.txt +8 -0
- data/lib/brainstem/presenter.rb +27 -6
- data/lib/brainstem/presenter_validator.rb +5 -2
- data/lib/brainstem/time_classes.rb +1 -1
- data/lib/brainstem/version.rb +1 -1
- data/spec/brainstem/api_docs/abstract_collection_spec.rb +156 -0
- data/spec/brainstem/api_docs/atlas_spec.rb +353 -0
- data/spec/brainstem/api_docs/builder_spec.rb +100 -0
- data/spec/brainstem/api_docs/controller_collection_spec.rb +92 -0
- data/spec/brainstem/api_docs/controller_spec.rb +225 -0
- data/spec/brainstem/api_docs/endpoint_collection_spec.rb +144 -0
- data/spec/brainstem/api_docs/endpoint_spec.rb +346 -0
- data/spec/brainstem/api_docs/formatters/abstract_formatter_spec.rb +30 -0
- data/spec/brainstem/api_docs/formatters/markdown/controller_formatter_spec.rb +126 -0
- data/spec/brainstem/api_docs/formatters/markdown/endpoint_collection_formatter_spec.rb +85 -0
- data/spec/brainstem/api_docs/formatters/markdown/endpoint_formatter_spec.rb +261 -0
- data/spec/brainstem/api_docs/formatters/markdown/helper_spec.rb +100 -0
- data/spec/brainstem/api_docs/formatters/markdown/presenter_formatter_spec.rb +485 -0
- data/spec/brainstem/api_docs/introspectors/abstract_introspector_spec.rb +192 -0
- data/spec/brainstem/api_docs/introspectors/rails_introspector_spec.rb +170 -0
- data/spec/brainstem/api_docs/presenter_collection_spec.rb +84 -0
- data/spec/brainstem/api_docs/presenter_spec.rb +519 -0
- data/spec/brainstem/api_docs/resolver_spec.rb +72 -0
- data/spec/brainstem/api_docs/sinks/abstract_sink_spec.rb +16 -0
- data/spec/brainstem/api_docs/sinks/controller_presenter_multifile_sink_spec.rb +56 -0
- data/spec/brainstem/api_docs/sinks/stdout_sink_spec.rb +22 -0
- data/spec/brainstem/api_docs_spec.rb +58 -0
- data/spec/brainstem/cli/abstract_command_spec.rb +91 -0
- data/spec/brainstem/cli/generate_api_docs_command_spec.rb +125 -0
- data/spec/brainstem/cli_spec.rb +67 -0
- data/spec/brainstem/concerns/controller_dsl_spec.rb +471 -0
- data/spec/brainstem/concerns/controller_param_management_spec.rb +36 -16
- data/spec/brainstem/concerns/formattable_spec.rb +30 -0
- data/spec/brainstem/concerns/inheritable_configuration_spec.rb +104 -4
- data/spec/brainstem/concerns/optional_spec.rb +48 -0
- data/spec/brainstem/concerns/presenter_dsl_spec.rb +202 -31
- data/spec/brainstem/dsl/association_spec.rb +18 -2
- data/spec/brainstem/dsl/conditional_spec.rb +25 -2
- data/spec/brainstem/dsl/configuration_spec.rb +1 -1
- data/spec/brainstem/dsl/field_spec.rb +18 -2
- data/spec/brainstem/presenter_collection_spec.rb +10 -2
- data/spec/brainstem/presenter_spec.rb +32 -0
- data/spec/brainstem/presenter_validator_spec.rb +12 -7
- data/spec/dummy/rails.rb +49 -0
- data/spec/shared/atlas_taker.rb +18 -0
- data/spec/shared/formattable.rb +14 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/spec_helpers/db.rb +1 -1
- data/spec/spec_helpers/presenters.rb +20 -14
- 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
|
-
|
|
6
|
-
|
|
5
|
+
subject do
|
|
6
|
+
Class.new do
|
|
7
|
+
include Brainstem::Concerns::ControllerParamManagement
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
21
|
-
expect(
|
|
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(
|
|
26
|
-
expect(
|
|
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
|
-
|
|
33
|
-
expect(
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
expect(
|
|
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
|
-
|
|
37
|
-
|
|
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,
|
|
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,
|
|
40
|
+
# association :tasks, Task,
|
|
41
|
+
# info: 'The Tasks in this Workspace',
|
|
34
42
|
# restrict_to_only: true
|
|
35
|
-
# association :lead_user, User,
|
|
36
|
-
#
|
|
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,
|
|
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(
|
|
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(
|
|
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,
|
|
157
|
-
|
|
158
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
357
|
+
association :tasks, Task,
|
|
358
|
+
info: 'The Tasks in this Workspace',
|
|
215
359
|
restrict_to_only: true
|
|
216
|
-
association :subtasks, Task,
|
|
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
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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(
|
|
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
|