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
@@ -0,0 +1,346 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/endpoint'
3
+
4
+ module Brainstem
5
+ module ApiDocs
6
+ describe Endpoint do
7
+ let(:lorem) { "lorem ipsum dolor sit amet" }
8
+ let(:atlas) { Object.new }
9
+ let(:options) { {} }
10
+ subject { described_class.new(atlas, options) }
11
+
12
+
13
+ describe "#initialize" do
14
+ it "yields self if given a block" do
15
+ block = Proc.new { |s| s.path = "bork bork" }
16
+ expect(described_class.new(atlas, &block).path).to eq "bork bork"
17
+ end
18
+ end
19
+
20
+
21
+ describe "#merge_http_methods!" do
22
+ let(:options) { { http_methods: %w(GET) } }
23
+
24
+ it "adds http methods that are not already present" do
25
+
26
+ expect(subject.http_methods).to eq %w(GET)
27
+ subject.merge_http_methods!(%w(POST PATCH GET))
28
+ expect(subject.http_methods).to eq %w(GET POST PATCH)
29
+ end
30
+ end
31
+
32
+
33
+ describe "configured fields" do
34
+ let(:const) do
35
+ Class.new do
36
+ def self.brainstem_model_name
37
+ :widget
38
+ end
39
+ end
40
+ end
41
+
42
+ let(:controller) { Object.new }
43
+ let(:action) { :show }
44
+
45
+ let(:lorem) { "lorem ipsum dolor sit amet" }
46
+ let(:default_config) { {} }
47
+ let(:show_config) { {} }
48
+ let(:nodoc) { false }
49
+
50
+ let(:configuration) {
51
+ {
52
+ :_default => default_config,
53
+ :show => show_config,
54
+ }
55
+ }
56
+
57
+ let(:options) { { controller: controller, action: action } }
58
+
59
+ before do
60
+ stub(controller).configuration { configuration }
61
+ stub(controller).const { const }
62
+ end
63
+
64
+
65
+ describe "#nodoc?" do
66
+ let(:show_config) { { nodoc: nodoc } }
67
+
68
+ context "when nodoc" do
69
+ let(:nodoc) { true }
70
+
71
+ it "is true" do
72
+ expect(subject.nodoc?).to eq true
73
+ end
74
+ end
75
+
76
+ context "when documentable" do
77
+ it "is false" do
78
+ expect(subject.nodoc?).to eq false
79
+ end
80
+ end
81
+ end
82
+
83
+
84
+ describe "#title" do
85
+ context "when present" do
86
+ let(:show_config) { { title: { info: lorem, nodoc: nodoc } } }
87
+
88
+ context "when nodoc" do
89
+ let(:nodoc) { true }
90
+
91
+ it "uses the action name" do
92
+ expect(subject.title).to eq "Show"
93
+ end
94
+ end
95
+
96
+ context "when documentable" do
97
+ it "formats the title as an h4" do
98
+ expect(subject.title).to eq lorem
99
+ end
100
+ end
101
+ end
102
+
103
+ context "when absent" do
104
+ it "falls back to the action name" do
105
+ expect(subject.title).to eq "Show"
106
+ end
107
+ end
108
+ end
109
+
110
+
111
+ describe "#description" do
112
+ context "when present" do
113
+ let(:show_config) { { description: { info: lorem, nodoc: nodoc } } }
114
+
115
+ context "when nodoc" do
116
+ let(:nodoc) { true }
117
+
118
+ it "shows nothing" do
119
+ expect(subject.description).to be_empty
120
+ end
121
+ end
122
+
123
+ context "when not nodoc" do
124
+ it "shows the description" do
125
+ expect(subject.description).to eq lorem
126
+ end
127
+ end
128
+ end
129
+
130
+ context "when not present" do
131
+ it "shows nothing" do
132
+ expect(subject.description).to be_empty
133
+ end
134
+ end
135
+ end
136
+
137
+
138
+ describe "#valid_params" do
139
+ it "returns the valid_params key from action or default" do
140
+ mock(subject).key_with_default_fallback(:valid_params)
141
+ subject.valid_params
142
+ end
143
+ end
144
+
145
+
146
+ describe "#root_param_keys" do
147
+ let(:nested_param) { { title: { nodoc: nodoc, root: :sprocket } } }
148
+ let(:proc_nested_param) { { title: { nodoc: nodoc, root: Proc.new { |klass| klass.brainstem_model_name } } } }
149
+ let(:root_param) { { title: { nodoc: nodoc } } }
150
+ let(:default_config) { { valid_params: which_param } }
151
+
152
+ context "non-nested params" do
153
+ let(:which_param) { root_param }
154
+
155
+ context "when nodoc" do
156
+ let(:nodoc) { true }
157
+
158
+ it "rejects the key" do
159
+ expect(subject.root_param_keys).to be_empty
160
+ end
161
+ end
162
+
163
+ context "when not nodoc" do
164
+ it "lists it as a root param" do
165
+ expect(subject.root_param_keys).to have_key(:title)
166
+ end
167
+ end
168
+ end
169
+
170
+
171
+ context "nested params" do
172
+ let(:which_param) { nested_param }
173
+
174
+ context "when nodoc" do
175
+ let(:nodoc) { true }
176
+
177
+ it "rejects the key" do
178
+ expect(subject.root_param_keys).to be_empty
179
+ end
180
+ end
181
+
182
+ context "when not nodoc" do
183
+ it "lists it as a nested param" do
184
+ expect(subject.root_param_keys).to eq({ sprocket: [ :title ] })
185
+ end
186
+ end
187
+ end
188
+
189
+
190
+ context "proc nested params" do
191
+ let(:which_param) { proc_nested_param }
192
+
193
+ context "when nodoc" do
194
+ let(:nodoc) { true }
195
+
196
+ it "rejects the key" do
197
+ expect(subject.root_param_keys).to be_empty
198
+ end
199
+ end
200
+
201
+ context "when not nodoc" do
202
+ it "evaluates the proc in the controller's context and lists it as a nested param" do
203
+ mock.proxy(const).brainstem_model_name
204
+ expect(subject.root_param_keys).to eq({ widget: [ :title ] })
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+
211
+ describe "#valid_presents" do
212
+ it "returns the presents key from action or default" do
213
+ mock(subject).key_with_default_fallback(:presents)
214
+ subject.valid_presents
215
+ end
216
+ end
217
+
218
+
219
+ describe "#contextual_documentation" do
220
+ let(:show_config) { { title: { info: info, nodoc: nodoc } } }
221
+ let(:info) { lorem }
222
+
223
+ context "when has the key" do
224
+ let(:key) { :title }
225
+
226
+ context "when not nodoc" do
227
+ context "when has info" do
228
+ it "is truthy" do
229
+ expect(subject.contextual_documentation(key)).to be_truthy
230
+ end
231
+
232
+ it "is the info" do
233
+ expect(subject.contextual_documentation(key)).to eq lorem
234
+ end
235
+ end
236
+
237
+ context "when has no info" do
238
+ let(:info) { nil }
239
+
240
+ it "is falsey" do
241
+ expect(subject.contextual_documentation(key)).to be_falsey
242
+ end
243
+ end
244
+ end
245
+
246
+ context "when nodoc" do
247
+ let(:nodoc) { true }
248
+
249
+ it "is falsey" do
250
+ expect(subject.contextual_documentation(key)).to be_falsey
251
+ end
252
+ end
253
+ end
254
+
255
+ context "when doesn't have the key" do
256
+ let(:key) { :herp }
257
+
258
+ it "is falsey" do
259
+ expect(subject.contextual_documentation(key)).to be_falsey
260
+ end
261
+ end
262
+ end
263
+
264
+
265
+ describe "#key_with_default_fallback" do
266
+ let(:default_config) { { info: "default" } }
267
+
268
+ context "when it has the key in the action config" do
269
+ let(:show_config) { { info: "show" } }
270
+
271
+ it "returns that" do
272
+ expect(subject.key_with_default_fallback(:info)).to eq "show"
273
+ end
274
+ end
275
+
276
+ context "when it has the key only in the default config" do
277
+ it "returns that" do
278
+ expect(subject.key_with_default_fallback(:info)).to eq "default"
279
+ end
280
+ end
281
+ end
282
+ end
283
+
284
+
285
+ describe "#sort" do
286
+ actions = %w(index show create update delete articuno zapdos moltres)
287
+
288
+ actions.each do |axn|
289
+ let(axn.to_sym) { described_class.new(atlas, action: axn.to_sym) }
290
+ end
291
+
292
+ let(:axns) { actions.map {|axn| send(axn.to_sym) } }
293
+
294
+ it "orders appropriately" do
295
+ sorted = axns.reverse.sort
296
+ expect(sorted[0]).to eq index
297
+ expect(sorted[1]).to eq show
298
+ expect(sorted[2]).to eq create
299
+ expect(sorted[3]).to eq update
300
+ expect(sorted[4]).to eq delete
301
+ expect(sorted[5]).to eq articuno
302
+ expect(sorted[6]).to eq moltres
303
+ expect(sorted[7]).to eq zapdos
304
+ end
305
+ end
306
+
307
+
308
+ describe "#presenter_title" do
309
+ let(:presenter) { mock!.title.returns(lorem).subject }
310
+ let(:options) { { presenter: presenter } }
311
+
312
+ it "returns the presenter's title" do
313
+ expect(subject.presenter_title).to eq lorem
314
+ end
315
+ end
316
+
317
+
318
+ describe "#relative_presenter_path_from_controller" do
319
+ let(:presenter) {
320
+ mock!
321
+ .suggested_filename_link(:markdown)
322
+ .returns("objects/sprocket_widget")
323
+ .subject
324
+ }
325
+
326
+ let(:controller) {
327
+ mock!
328
+ .suggested_filename_link(:markdown)
329
+ .returns("controllers/api/v1/sprocket_widgets_controller")
330
+ .subject
331
+ }
332
+
333
+ let(:options) { { presenter: presenter, controller: controller } }
334
+
335
+ it "returns a relative path" do
336
+ expect(subject.relative_presenter_path_from_controller(:markdown)).to \
337
+ eq "../../../objects/sprocket_widget"
338
+ end
339
+ end
340
+
341
+
342
+ it_behaves_like "formattable"
343
+ it_behaves_like "atlas taker"
344
+ end
345
+ end
346
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/formatters/abstract_formatter'
3
+
4
+ module Brainstem
5
+ module ApiDocs
6
+ module Formatters
7
+ describe AbstractFormatter do
8
+ subject { AbstractFormatter.new }
9
+
10
+ describe ".call" do
11
+ it "instantiates a new instance and calls it, passing the instance all args" do
12
+ mock(described_class).new(1, 2, {}) do |instance|
13
+ mock(Object.new).call
14
+ end
15
+
16
+ described_class.call(1, 2, {})
17
+ end
18
+ end
19
+
20
+
21
+ describe "#call" do
22
+ it "is not implemented" do
23
+ expect { subject.call }.to raise_error NotImplementedError
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/formatters/markdown/controller_formatter'
3
+ require 'brainstem/api_docs/controller'
4
+
5
+ module Brainstem
6
+ module ApiDocs
7
+ module Formatters
8
+ module Markdown
9
+ describe ControllerFormatter do
10
+ let(:const) { Object.new }
11
+ let(:atlas) { Object.new }
12
+ let(:controller) { Controller.new(atlas, const: const) }
13
+ let(:configuration) { {} }
14
+
15
+ let(:endpoint_1) { Object.new }
16
+ let(:endpoints) { [ endpoint_1 ] }
17
+ let(:nodoc) { false }
18
+ let(:options) { {} }
19
+
20
+ subject { described_class.new(controller, options) }
21
+
22
+ before do
23
+ stub(const).configuration { configuration }
24
+ end
25
+
26
+ describe "#call" do
27
+ let(:configuration) { { _default: { nodoc: nodoc } } }
28
+
29
+ context "when nodoc specified" do
30
+ let(:nodoc) { true }
31
+
32
+ before do
33
+ dont_allow(subject).format_title!
34
+ dont_allow(subject).format_description!
35
+ dont_allow(subject).format_actions!
36
+ end
37
+
38
+ it "returns a blank output" do
39
+ expect(subject.call).to eq ""
40
+ end
41
+ end
42
+
43
+ context "when nodoc not specified" do
44
+ it "formats title, description, actions, and presenters" do
45
+ mock(subject).format_title!
46
+ mock(subject).format_description!
47
+ mock(subject).format_actions!
48
+
49
+ subject.call
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "formatting" do
55
+ let(:lorem) { "lorem ipsum dolor sit amet" }
56
+ let(:default_config) { {} }
57
+ let(:configuration) { { _default: default_config } }
58
+
59
+
60
+ describe "#format_title!" do
61
+ it "outputs it as an h2" do
62
+ stub(controller).title { lorem }
63
+ mock(subject).md_h2(lorem) { lorem }
64
+ subject.send(:format_title!)
65
+ expect(subject.output).to eq lorem
66
+ end
67
+ end
68
+
69
+ describe "#format_description!" do
70
+ context "when present" do
71
+ before do
72
+ stub(controller).description { lorem }
73
+ end
74
+
75
+ it "prints it as a p" do
76
+ mock(subject).md_p(lorem) { lorem }
77
+ subject.send(:format_description!)
78
+ expect(subject.output).to eq lorem
79
+ end
80
+ end
81
+
82
+ context "when absent" do
83
+ before do
84
+ stub(controller).description { "" }
85
+ end
86
+
87
+ it "prints nothing" do
88
+ dont_allow(subject).md_p
89
+ subject.send(:format_description!)
90
+ expect(subject.output).to eq ""
91
+ end
92
+
93
+ end
94
+ end
95
+
96
+ describe "#format_actions!" do
97
+ context "if include actions" do
98
+ let(:options) { { include_actions: true } }
99
+
100
+ it "creates a subheading" do
101
+ stub(controller).valid_sorted_endpoints.stub!.formatted_as(:markdown, anything) { "" }
102
+ subject.send(:format_actions!)
103
+ expect(subject.output).to include "### Endpoints"
104
+ end
105
+
106
+ it "appends the formatted output of the endpoints" do
107
+ stub(controller).valid_sorted_endpoints.stub!.formatted_as(:markdown, anything) { "collection" }
108
+ subject.send(:format_actions!)
109
+ expect(subject.output).to include "collection"
110
+ end
111
+ end
112
+
113
+ context "if not include actions" do
114
+ it "shows nothing" do
115
+ subject.send(:format_actions!)
116
+ expect(subject.output).to eq ""
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end