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
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'brainstem/api_docs/introspectors/abstract_introspector'
|
|
3
|
+
|
|
4
|
+
module Brainstem
|
|
5
|
+
module ApiDocs
|
|
6
|
+
module Introspectors
|
|
7
|
+
describe AbstractIntrospector do
|
|
8
|
+
subject { AbstractIntrospector.send(:new) }
|
|
9
|
+
|
|
10
|
+
describe ".with_loaded_environment" do
|
|
11
|
+
it "passes along all options" do
|
|
12
|
+
any_instance_of(AbstractIntrospector) { |instance| stub(instance).one=(1) }
|
|
13
|
+
mock.proxy(AbstractIntrospector).new(one: 1) do |obj|
|
|
14
|
+
stub(obj) do |stub|
|
|
15
|
+
stub.load_environment!
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
AbstractIntrospector.with_loaded_environment(one: 1)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "invokes #load_environment! on the instance" do
|
|
23
|
+
stub.proxy(AbstractIntrospector).new do |obj|
|
|
24
|
+
mock(obj).load_environment!
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
AbstractIntrospector.with_loaded_environment
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "returns the instance" do
|
|
31
|
+
instance = Object.new
|
|
32
|
+
stub(instance).load_environment!
|
|
33
|
+
stub.proxy(AbstractIntrospector).new { |_| instance }
|
|
34
|
+
|
|
35
|
+
expect(AbstractIntrospector.with_loaded_environment).to eq instance
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "#initialize" do
|
|
40
|
+
it "is private" do
|
|
41
|
+
expect { AbstractIntrospector.new }.to raise_error NoMethodError
|
|
42
|
+
expect { AbstractIntrospector.send(:new) }.not_to raise_error
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
describe "#load_environment!" do
|
|
48
|
+
it "is not implemented" do
|
|
49
|
+
expect { subject.send(:load_environment!) }.to raise_error NotImplementedError
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe "#controllers" do
|
|
55
|
+
it "is not implemented" do
|
|
56
|
+
expect { subject.controllers }.to raise_error NotImplementedError
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe "#presenters" do
|
|
61
|
+
it "is not implemented" do
|
|
62
|
+
expect { subject.presenters }.to raise_error NotImplementedError
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe "#routes" do
|
|
67
|
+
it "is not implemented" do
|
|
68
|
+
expect { subject.routes }.to raise_error NotImplementedError
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe "#valid?" do
|
|
73
|
+
let! (:controllers_valid) { true }
|
|
74
|
+
let! (:presenters_valid) { true }
|
|
75
|
+
let! (:routes_valid) { true }
|
|
76
|
+
|
|
77
|
+
before do
|
|
78
|
+
stub(subject) do |s|
|
|
79
|
+
s.valid_controllers? { controllers_valid }
|
|
80
|
+
s.valid_presenters? { presenters_valid }
|
|
81
|
+
s.valid_routes? { routes_valid }
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
context "when controllers, presenters, and routes are valid" do
|
|
86
|
+
it "is valid" do
|
|
87
|
+
expect(subject.valid?).to eq true
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
context "when controllers are invalid" do
|
|
92
|
+
let(:controllers_valid) { false }
|
|
93
|
+
|
|
94
|
+
it "is not valid" do
|
|
95
|
+
expect(subject.valid?).to eq false
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
context "when presenters are invalid" do
|
|
101
|
+
let(:presenters_valid) { false }
|
|
102
|
+
|
|
103
|
+
it "is not valid" do
|
|
104
|
+
expect(subject.valid?).to eq false
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
context "when routes are invalid" do
|
|
109
|
+
let(:routes_valid) { false }
|
|
110
|
+
|
|
111
|
+
it "is not valid" do
|
|
112
|
+
expect(subject.valid?).to eq false
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe "#valid_controllers?" do
|
|
118
|
+
it "is valid when a collection of at least one class" do
|
|
119
|
+
stub(subject).controllers { [ Integer ] }
|
|
120
|
+
expect(subject.send(:valid_controllers?)).to eq true
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "is invalid when not a collection" do
|
|
124
|
+
stub(subject).controllers { { dog: "woof" } }
|
|
125
|
+
expect(subject.send(:valid_controllers?)).to eq false
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it "is invalid when empty" do
|
|
129
|
+
stub(subject).controllers { [] }
|
|
130
|
+
expect(subject.send(:valid_controllers?)).to eq false
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
describe "#valid_presenters?" do
|
|
136
|
+
it "is valid when a collection of zero or more classes" do
|
|
137
|
+
stub(subject).presenters { [ Integer ] }
|
|
138
|
+
expect(subject.send(:valid_presenters?)).to eq true
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "is valid when empty" do
|
|
142
|
+
stub(subject).presenters { [] }
|
|
143
|
+
expect(subject.send(:valid_presenters?)).to eq true
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "is invalid when not a collection" do
|
|
147
|
+
stub(subject).presenters { { dog: "woof" } }
|
|
148
|
+
expect(subject.send(:valid_presenters?)).to eq false
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
describe "#valid_routes?" do
|
|
154
|
+
it "is valid when a collection of hashes with specific keys" do
|
|
155
|
+
stub(subject).routes { [
|
|
156
|
+
{
|
|
157
|
+
path: "blah",
|
|
158
|
+
controller: "blah",
|
|
159
|
+
action: "blah",
|
|
160
|
+
http_methods: ['blah']
|
|
161
|
+
}
|
|
162
|
+
] }
|
|
163
|
+
|
|
164
|
+
expect(subject.send(:valid_routes?)).to eq true
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "is invalid when not a collection" do
|
|
168
|
+
stub(subject).routes { {} }
|
|
169
|
+
expect(subject.send(:valid_routes?)).to eq false
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it "is invalid when empty" do
|
|
173
|
+
stub(subject).routes { [] }
|
|
174
|
+
expect(subject.send(:valid_routes?)).to eq false
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "is invalid if any one hash is missing a required key" do
|
|
178
|
+
stub(subject).routes { [
|
|
179
|
+
{
|
|
180
|
+
path: "blah",
|
|
181
|
+
controller: "blah",
|
|
182
|
+
action: "blah"
|
|
183
|
+
}
|
|
184
|
+
] }
|
|
185
|
+
|
|
186
|
+
expect(subject.send(:valid_routes?)).to eq false
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'brainstem/api_docs/introspectors/rails_introspector'
|
|
3
|
+
|
|
4
|
+
module Brainstem
|
|
5
|
+
module ApiDocs
|
|
6
|
+
module Introspectors
|
|
7
|
+
describe RailsIntrospector do
|
|
8
|
+
let(:dummy_environment_file) do
|
|
9
|
+
File.expand_path('../../../../../spec/dummy/rails.rb', __FILE__)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
let(:described_klass) { RailsIntrospector }
|
|
13
|
+
let(:default_args) { { rails_environment_file: dummy_environment_file } }
|
|
14
|
+
|
|
15
|
+
subject do
|
|
16
|
+
RailsIntrospector.send(:new, default_args)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
context "when cannot find the environment file" do
|
|
21
|
+
describe "#load_environment!" do
|
|
22
|
+
subject { described_klass.send(:new) }
|
|
23
|
+
|
|
24
|
+
before do
|
|
25
|
+
# In the event that we've already loaded the environment through
|
|
26
|
+
# random ordering of specs, we want to force a reload.
|
|
27
|
+
#
|
|
28
|
+
# For some reason, this has stopped being needed.
|
|
29
|
+
Object.send(:remove_const, :Rails) if defined?(Rails)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "raises an error" do
|
|
33
|
+
# Testing this within Brainstem should fail.
|
|
34
|
+
|
|
35
|
+
expect { subject.send(:load_environment!) }
|
|
36
|
+
.to raise_error IncorrectIntrospectorForAppException
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context "when can find the entrypoint file" do
|
|
42
|
+
describe "#load_environment!" do
|
|
43
|
+
context "when introspector is invalid" do
|
|
44
|
+
before do
|
|
45
|
+
stub(subject).valid? { false }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "raises an error" do
|
|
49
|
+
expect { subject.send(:load_environment!) }.to \
|
|
50
|
+
raise_error InvalidIntrospectorError
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context "when introspector is valid" do
|
|
55
|
+
before do
|
|
56
|
+
stub(subject).valid? { true }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "does not raise an error" do
|
|
60
|
+
expect { subject.send(:load_environment!) }.not_to raise_error
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "does not load the file if Rails is defined" do
|
|
64
|
+
# Ensure that this has been sent already.
|
|
65
|
+
subject.send(:load_environment!)
|
|
66
|
+
|
|
67
|
+
dont_allow(subject).rails_environment_file
|
|
68
|
+
mock(subject).env_already_loaded? { true }
|
|
69
|
+
|
|
70
|
+
subject.send(:load_environment!)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
describe "#presenters" do
|
|
76
|
+
before do
|
|
77
|
+
stub.any_instance_of(described_klass).validate!
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
subject do
|
|
81
|
+
described_klass.with_loaded_environment(
|
|
82
|
+
default_args.merge(base_presenter_class: "::FakeBasePresenter")
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
it "allows the specification of a custom base_presenter_class" do
|
|
88
|
+
expect(subject.send(:base_presenter_class).to_s)
|
|
89
|
+
.to eq "::FakeBasePresenter"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "returns the descendants of the base presenter class" do
|
|
93
|
+
expect(subject.presenters).to eq [FakeDescendantPresenter]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe "#controllers" do
|
|
98
|
+
before do
|
|
99
|
+
stub.any_instance_of(described_klass).validate!
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
subject do
|
|
103
|
+
described_klass.with_loaded_environment(
|
|
104
|
+
default_args.merge(base_controller_class: "::FakeBaseController")
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
it "allows the specification of a custom base_controller_class" do
|
|
110
|
+
expect(subject.send(:base_controller_class).to_s)
|
|
111
|
+
.to eq "::FakeBaseController"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "returns the descendants of the base controller class" do
|
|
115
|
+
expect(subject.controllers).to eq [FakeDescendantController]
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe "#routes" do
|
|
120
|
+
let(:a_proc) { Object.new }
|
|
121
|
+
|
|
122
|
+
before do
|
|
123
|
+
stub.any_instance_of(described_klass).validate!
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
context "with dummy method" do
|
|
127
|
+
subject do
|
|
128
|
+
described_klass.with_loaded_environment(
|
|
129
|
+
default_args.merge(routes_method: a_proc)
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "allows the specification of a custom method to return the routes" do
|
|
134
|
+
expect(subject.send(:routes_method)).to eq a_proc
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "calls the routes method to return the routes" do
|
|
138
|
+
mock(a_proc).call
|
|
139
|
+
subject.routes
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
context "with fake (but realistic) data" do
|
|
144
|
+
subject do
|
|
145
|
+
described_klass.with_loaded_environment(default_args)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it "skips the entry if it does not have a valid controller" do
|
|
149
|
+
expect(subject.routes.count).to eq 1
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it "adds the controller constant" do
|
|
153
|
+
expect(subject.routes.first[:controller]).to eq FakeDescendantController
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "reports the controller name" do
|
|
157
|
+
expect(subject.routes.first[:controller_name]).to eq "fake_descendant"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "transforms the HTTP method regexp into a list of verbs" do
|
|
161
|
+
expect(subject.routes.first[:http_methods]).to eq %w(GET POST)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'brainstem/api_docs/presenter_collection'
|
|
3
|
+
|
|
4
|
+
module Brainstem
|
|
5
|
+
module ApiDocs
|
|
6
|
+
describe PresenterCollection do
|
|
7
|
+
let(:presenter) { Object.new }
|
|
8
|
+
let(:target_class) { Class.new }
|
|
9
|
+
let(:atlas) { Object.new }
|
|
10
|
+
let(:options) { {} }
|
|
11
|
+
|
|
12
|
+
subject { described_class.new(atlas, options) }
|
|
13
|
+
|
|
14
|
+
describe "#find_by_target_class" do
|
|
15
|
+
before do
|
|
16
|
+
stub(presenter) do |p|
|
|
17
|
+
p.target_class { target_class }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
subject << presenter
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context "when matches presenter" do
|
|
24
|
+
it "returns the matching presenter" do
|
|
25
|
+
expect(subject.find_by_target_class(target_class)).to eq presenter
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
context "when does not match presenter" do
|
|
30
|
+
it "returns nil" do
|
|
31
|
+
expect(subject.find_by_target_class(Class.new)).to eq nil
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
describe "#create_from_target_class" do
|
|
38
|
+
let(:pclm) { Object.new }
|
|
39
|
+
let(:options) { { presenter_constant_lookup_method: pclm } }
|
|
40
|
+
|
|
41
|
+
context "when can find constant" do
|
|
42
|
+
before do
|
|
43
|
+
stub(target_class).to_s { "TargetClass" }
|
|
44
|
+
stub(pclm).call("TargetClass") { Object }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "creates a new presenter, adding it to the members" do
|
|
48
|
+
presenter = subject.create_from_target_class(target_class)
|
|
49
|
+
expect(subject.first).to eq presenter
|
|
50
|
+
expect(presenter.const).to eq Object
|
|
51
|
+
expect(presenter.target_class).to eq target_class
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context "when cannot find constant" do
|
|
57
|
+
before do
|
|
58
|
+
stub(target_class).to_s { "TargetClass" }
|
|
59
|
+
stub(pclm).call("TargetClass") { raise KeyError }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "returns nil and does not append to the members" do
|
|
63
|
+
presenter = subject.create_from_target_class(target_class)
|
|
64
|
+
expect(presenter).to be_nil
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
describe "#create_from_presenter_collection" do
|
|
71
|
+
let(:const) { Class.new }
|
|
72
|
+
|
|
73
|
+
it "creates a new presenter, adding it to the members" do
|
|
74
|
+
presenter = subject.create_from_presenter_collection(target_class, const)
|
|
75
|
+
expect(subject.first).to eq presenter
|
|
76
|
+
expect(presenter.const).to eq const
|
|
77
|
+
expect(presenter.target_class).to eq target_class
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it_behaves_like "atlas taker"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'set'
|
|
3
|
+
require 'ostruct'
|
|
4
|
+
require 'brainstem/api_docs/presenter'
|
|
5
|
+
|
|
6
|
+
module Brainstem
|
|
7
|
+
module ApiDocs
|
|
8
|
+
describe Presenter do
|
|
9
|
+
subject { described_class.new(atlas, options) }
|
|
10
|
+
|
|
11
|
+
let(:atlas) { Object.new }
|
|
12
|
+
let(:target_class) { Class.new }
|
|
13
|
+
let(:options) { { } }
|
|
14
|
+
let(:nodoc) { false }
|
|
15
|
+
|
|
16
|
+
describe "#initialize" do
|
|
17
|
+
it "yields self if given a block" do
|
|
18
|
+
block = Proc.new { |s| s.target_class = target_class}
|
|
19
|
+
expect(described_class.new(atlas, &block).target_class).to eq target_class
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
describe "configured fields" do
|
|
25
|
+
let(:lorem) { "lorem ipsum dolor sit amet" }
|
|
26
|
+
let(:const) { Object.new }
|
|
27
|
+
let(:config) { {} }
|
|
28
|
+
let(:options) { { const: const } }
|
|
29
|
+
|
|
30
|
+
subject { described_class.new(atlas, options) }
|
|
31
|
+
|
|
32
|
+
before do
|
|
33
|
+
stub(const) do |constant|
|
|
34
|
+
constant.configuration { config }
|
|
35
|
+
constant.to_s { "Namespaced::ClassName" }
|
|
36
|
+
constant.possible_brainstem_keys { Set.new(%w(lorem ipsum)) }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
describe "#nodoc?" do
|
|
42
|
+
let(:config) { { nodoc: nodoc } }
|
|
43
|
+
|
|
44
|
+
context "when nodoc in default" do
|
|
45
|
+
let(:nodoc) { true }
|
|
46
|
+
|
|
47
|
+
it "is true" do
|
|
48
|
+
expect(subject.nodoc?).to eq true
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context "when not nodoc in default" do
|
|
53
|
+
it "is false" do
|
|
54
|
+
expect(subject.nodoc?).to eq false
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
describe "#title" do
|
|
61
|
+
let(:config) { { title: { info: lorem, nodoc: nodoc } } }
|
|
62
|
+
|
|
63
|
+
context "when nodoc" do
|
|
64
|
+
let(:nodoc) { true }
|
|
65
|
+
|
|
66
|
+
it "uses the last portion of the presenter's class" do
|
|
67
|
+
expect(subject.title).to eq "ClassName"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
context "when not nodoc" do
|
|
72
|
+
it "uses the presenter's title" do
|
|
73
|
+
expect(subject.title).to eq lorem
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
describe "#brainstem_keys" do
|
|
80
|
+
it "retrieves from the constant, array-izes, and sorts" do
|
|
81
|
+
expect(subject.brainstem_keys).to eq [ "ipsum", "lorem" ]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
describe "#description" do
|
|
87
|
+
context "with description" do
|
|
88
|
+
let(:config) { { description: { info: lorem, nodoc: nodoc } } }
|
|
89
|
+
|
|
90
|
+
context "when nodoc" do
|
|
91
|
+
let(:nodoc) { true }
|
|
92
|
+
|
|
93
|
+
it "returns empty" do
|
|
94
|
+
expect(subject.description).to eq ""
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context "when not nodoc" do
|
|
99
|
+
it "returns the description" do
|
|
100
|
+
expect(subject.description).to eq lorem
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
context "without description" do
|
|
106
|
+
it "returns empty" do
|
|
107
|
+
expect(subject.description).to eq ""
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
describe "#valid_fields" do
|
|
114
|
+
let(:field) { Object.new }
|
|
115
|
+
before { stub(field).options { { nodoc: nodoc } } }
|
|
116
|
+
|
|
117
|
+
describe "leafs" do
|
|
118
|
+
let(:config) { { fields: { a_field: field } } }
|
|
119
|
+
|
|
120
|
+
context "when nodoc" do
|
|
121
|
+
let(:nodoc) { true }
|
|
122
|
+
|
|
123
|
+
it "rejects the field" do
|
|
124
|
+
expect(subject.valid_fields.count).to eq 0
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context "when not nodoc" do
|
|
129
|
+
it "keeps the field" do
|
|
130
|
+
expect(subject.valid_fields.count).to eq 1
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
describe "branches" do
|
|
136
|
+
describe "single nesting" do
|
|
137
|
+
let(:config) { { fields: { nesting_one: { a_field: field } } } }
|
|
138
|
+
|
|
139
|
+
context "when all nodoc" do
|
|
140
|
+
let(:nodoc) { true }
|
|
141
|
+
|
|
142
|
+
it "rejects the nested field" do
|
|
143
|
+
expect(subject.valid_fields.count).to eq 0
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
context "when not all nodoc" do
|
|
148
|
+
it "keeps the nested field" do
|
|
149
|
+
expect(subject.valid_fields.count).to eq 1
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
describe "double nesting" do
|
|
156
|
+
let(:config) { { fields: { nesting_one: { nesting_two: { a_field: field } } } } }
|
|
157
|
+
|
|
158
|
+
context "when all nodoc" do
|
|
159
|
+
let(:nodoc) { true }
|
|
160
|
+
|
|
161
|
+
it "rejects the nested field" do
|
|
162
|
+
expect(subject.valid_fields.count).to eq 0
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
context "when not all nodoc" do
|
|
167
|
+
it "keeps the nested field" do
|
|
168
|
+
expect(subject.valid_fields.count).to eq 1
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
describe "#valid_filters" do
|
|
178
|
+
let(:info) { lorem }
|
|
179
|
+
let(:filter) { { info: info } }
|
|
180
|
+
let(:config) { { filters: { an_example: filter } } }
|
|
181
|
+
|
|
182
|
+
context "when valid" do
|
|
183
|
+
before do
|
|
184
|
+
stub(subject).documentable_filter?(:an_example, filter) { true }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
it "retrieves from configuration" do
|
|
188
|
+
expect(subject.valid_filters).to eq({ an_example: filter })
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
context "when invalid" do
|
|
193
|
+
before do
|
|
194
|
+
stub(subject).documentable_filter?(:an_example, filter) { false }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it "returns an empty hash" do
|
|
198
|
+
expect(subject.valid_filters).to be_empty
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
describe "#documentable_filter?" do
|
|
205
|
+
let(:info) { lorem }
|
|
206
|
+
let(:filter) { { nodoc: nodoc, info: info } }
|
|
207
|
+
|
|
208
|
+
context "when nodoc" do
|
|
209
|
+
let(:nodoc) { true }
|
|
210
|
+
|
|
211
|
+
it "is false" do
|
|
212
|
+
expect(subject.documentable_filter?(:filter, filter)).to eq false
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
context "when doc" do
|
|
217
|
+
context "when description present" do
|
|
218
|
+
it "is true" do
|
|
219
|
+
expect(subject.documentable_filter?(:filter, filter)).to eq true
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
context "when description absent" do
|
|
224
|
+
let(:info) { nil }
|
|
225
|
+
|
|
226
|
+
context "when documenting empty filters" do
|
|
227
|
+
let(:options) { { const: const, document_empty_filters: true } }
|
|
228
|
+
|
|
229
|
+
it "is true" do
|
|
230
|
+
expect(subject.documentable_filter?(:filter, filter)).to eq true
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
context "when not documenting empty filters" do
|
|
235
|
+
let(:options) { { const: const, document_empty_filters: false } }
|
|
236
|
+
|
|
237
|
+
it "is false" do
|
|
238
|
+
expect(subject.documentable_filter?(:filter, filter)).to eq false
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
describe "#valid_sort_orders" do
|
|
247
|
+
let(:config) { { sort_orders: { title: { nodoc: true }, date: {} } } }
|
|
248
|
+
|
|
249
|
+
it "returns all pairs not marked nodoc" do
|
|
250
|
+
expect(subject.valid_sort_orders).to have_key(:date)
|
|
251
|
+
expect(subject.valid_sort_orders).not_to have_key(:title)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
describe "#valid_associations" do
|
|
257
|
+
let(:info) { lorem }
|
|
258
|
+
let(:association) { Object.new }
|
|
259
|
+
let(:config) { { associations: { an_example: association } } }
|
|
260
|
+
|
|
261
|
+
context "when valid" do
|
|
262
|
+
before do
|
|
263
|
+
stub(subject).documentable_association?(:an_example, association) { true }
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it "retrieves from configuration" do
|
|
267
|
+
expect(subject.valid_associations).to eq({ an_example: association })
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
context "when invalid" do
|
|
272
|
+
before do
|
|
273
|
+
stub(subject).documentable_association?(:an_example, association) { false }
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it "returns an empty hash" do
|
|
277
|
+
expect(subject.valid_associations).to be_empty
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
describe "#documentable_association?" do
|
|
284
|
+
let(:desc) { lorem }
|
|
285
|
+
let(:association) { OpenStruct.new(options: { nodoc: nodoc }, description: desc ) }
|
|
286
|
+
|
|
287
|
+
context "when nodoc" do
|
|
288
|
+
let(:nodoc) { true }
|
|
289
|
+
|
|
290
|
+
it "is false" do
|
|
291
|
+
expect(subject.documentable_association?(:assoc, association)).to eq false
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
context "when doc" do
|
|
296
|
+
context "when description present" do
|
|
297
|
+
it "is true" do
|
|
298
|
+
expect(subject.documentable_association?(:assoc, association)).to eq true
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
context "when description absent" do
|
|
303
|
+
let(:desc) { nil }
|
|
304
|
+
|
|
305
|
+
context "when documenting empty filters" do
|
|
306
|
+
let(:options) { { const: const, document_empty_associations: true } }
|
|
307
|
+
|
|
308
|
+
it "is true" do
|
|
309
|
+
expect(subject.documentable_association?(:assoc, association)).to eq true
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
context "when not documenting empty filters" do
|
|
314
|
+
let(:options) { { const: const, document_empty_associations: false } }
|
|
315
|
+
|
|
316
|
+
it "is false" do
|
|
317
|
+
expect(subject.documentable_association?(:assoc, association)).to eq false
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
describe "#conditionals" do
|
|
326
|
+
let(:config) { { conditionals: { thing: :other_thing } } }
|
|
327
|
+
|
|
328
|
+
it "retrieves from configuration" do
|
|
329
|
+
expect(subject.conditionals).to eq({ thing: :other_thing })
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
describe "#default_sort_order" do
|
|
334
|
+
let(:config) { { default_sort_order: "alphabetical:asc" } }
|
|
335
|
+
|
|
336
|
+
it "retrieves from configuration" do
|
|
337
|
+
expect(subject.default_sort_order).to eq "alphabetical:asc"
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
describe "#default_sort_field" do
|
|
343
|
+
context "when has default sort order" do
|
|
344
|
+
let(:config) { { default_sort_order: "alphabetical:asc" } }
|
|
345
|
+
|
|
346
|
+
it "returns the first component" do
|
|
347
|
+
expect(subject.default_sort_field).to eq "alphabetical"
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
context "when no default sort order" do
|
|
352
|
+
it "returns nil" do
|
|
353
|
+
expect(subject.default_sort_field).to be_nil
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
describe "#default_sort_direction" do
|
|
360
|
+
context "when has default sort order" do
|
|
361
|
+
let(:config) { { default_sort_order: "alphabetical:asc" } }
|
|
362
|
+
|
|
363
|
+
it "returns the last component" do
|
|
364
|
+
expect(subject.default_sort_direction).to eq "asc"
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
context "when no default sort order" do
|
|
369
|
+
it "returns nil" do
|
|
370
|
+
expect(subject.default_sort_direction).to be_nil
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
describe "#contextual_documentation" do
|
|
376
|
+
let(:config) { { title: { info: info, nodoc: nodoc } } }
|
|
377
|
+
let(:info) { lorem }
|
|
378
|
+
|
|
379
|
+
context "when has the key" do
|
|
380
|
+
let(:key) { :title }
|
|
381
|
+
|
|
382
|
+
context "when not nodoc" do
|
|
383
|
+
context "when has info" do
|
|
384
|
+
it "is truthy" do
|
|
385
|
+
expect(subject.contextual_documentation(key)).to be_truthy
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
it "is the info" do
|
|
389
|
+
expect(subject.contextual_documentation(key)).to eq lorem
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
context "when has no info" do
|
|
394
|
+
let(:info) { nil }
|
|
395
|
+
|
|
396
|
+
it "is falsey" do
|
|
397
|
+
expect(subject.contextual_documentation(key)).to be_falsey
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
context "when nodoc" do
|
|
403
|
+
let(:nodoc) { true }
|
|
404
|
+
|
|
405
|
+
it "is falsey" do
|
|
406
|
+
expect(subject.contextual_documentation(key)).to be_falsey
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
context "when doesn't have the key" do
|
|
412
|
+
let(:key) { :herp }
|
|
413
|
+
|
|
414
|
+
it "is falsey" do
|
|
415
|
+
expect(subject.contextual_documentation(key)).to be_falsey
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
describe "#suggested_filename" do
|
|
423
|
+
before do
|
|
424
|
+
stub(target_class).to_s { "Abc" }
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
it "gsubs name and extension" do
|
|
428
|
+
|
|
429
|
+
instance = described_class.new(atlas,
|
|
430
|
+
filename_pattern: "presenters/{{name}}.{{extension}}",
|
|
431
|
+
target_class: target_class
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
stub(instance).extension { "xyz" }
|
|
435
|
+
|
|
436
|
+
expect(instance.suggested_filename(:xyz)).to eq "presenters/abc.xyz"
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
describe "#suggested_filename_link" do
|
|
442
|
+
before do
|
|
443
|
+
stub(target_class).to_s { "Abc" }
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
it "gsubs name and extension" do
|
|
447
|
+
|
|
448
|
+
instance = described_class.new(atlas,
|
|
449
|
+
filename_link_pattern: "presenters/{{name}}.{{extension}}.foo",
|
|
450
|
+
target_class: target_class
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
stub(instance).extension { "xyz" }
|
|
454
|
+
|
|
455
|
+
expect(instance.suggested_filename_link(:xyz)).to eq "presenters/abc.xyz.foo"
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
describe "#relative_path_to_presenter" do
|
|
461
|
+
let(:presenter) {
|
|
462
|
+
mock!
|
|
463
|
+
.suggested_filename_link(:markdown)
|
|
464
|
+
.returns("objects/sprocket_widget")
|
|
465
|
+
.subject
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
it "returns a relative path" do
|
|
469
|
+
expect(subject.relative_path_to_presenter(presenter, :markdown)).to \
|
|
470
|
+
eq "sprocket_widget"
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
describe "#link_for_association" do
|
|
476
|
+
let(:presenter) { Object.new }
|
|
477
|
+
let(:target_class) { Class.new }
|
|
478
|
+
let(:association) { OpenStruct.new(target_class: target_class) }
|
|
479
|
+
|
|
480
|
+
context "when can find presenter" do
|
|
481
|
+
before do
|
|
482
|
+
mock(subject).find_by_class(target_class) { presenter }
|
|
483
|
+
stub(subject).relative_path_to_presenter(presenter, :markdown) { "./path" }
|
|
484
|
+
stub(presenter).nodoc? { nodoc }
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
context "when nodoc" do
|
|
488
|
+
let(:nodoc) { true }
|
|
489
|
+
|
|
490
|
+
it "is nil" do
|
|
491
|
+
expect(subject.link_for_association(association)).to be_nil
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
context "when not nodoc" do
|
|
496
|
+
it "is the path" do
|
|
497
|
+
expect(subject.link_for_association(association)).to eq "./path"
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
context "when cannot find presenter" do
|
|
504
|
+
before do
|
|
505
|
+
mock(subject).find_by_class(target_class) { nil }
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
it "is nil" do
|
|
509
|
+
expect(subject.link_for_association(association)).to be_nil
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
it_behaves_like "formattable"
|
|
516
|
+
it_behaves_like "atlas taker"
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
end
|