brainstem 1.0.0.pre.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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