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,67 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/cli'
3
+
4
+ describe "Command line" do
5
+ let(:dummy_puts) { Object.new }
6
+ let(:options) { { log_method: dummy_puts } }
7
+ let(:args) { [] }
8
+
9
+ subject { Brainstem::Cli.new(args, options) }
10
+
11
+ describe "#initialize" do
12
+ it "accepts options and sets them" do
13
+ expect(subject.send(:log_method)).to eq dummy_puts
14
+ end
15
+
16
+ it "saves the raw args to _args and freezes them" do
17
+ expect(subject._args).to eq []
18
+ expect(subject._args).to be_frozen
19
+ end
20
+
21
+ end
22
+
23
+ context "when called with no commands" do
24
+ it "produces response text" do
25
+ mock(dummy_puts).call(anything) do |text|
26
+ expect(text).to include "Commonly used commands"
27
+ end
28
+
29
+ subject.call
30
+ end
31
+ end
32
+
33
+ context "when called with a command" do
34
+ let(:args) { [ 'fake' ] }
35
+
36
+ it "sets the requested command" do
37
+ expect(subject.requested_command).to eq 'fake'
38
+ end
39
+
40
+ context "when the command exists" do
41
+ let(:fake_command_class) { Proc.new { false } }
42
+ let(:args) { %w(fake --silent) }
43
+
44
+ before do
45
+ stub(subject).commands { { 'fake' => fake_command_class } }
46
+ end
47
+
48
+ it "calls the command, passing it the args less one" do
49
+ mock(fake_command_class).call(['--silent'])
50
+ subject.call
51
+ end
52
+ end
53
+
54
+ context "when the command does not exist" do
55
+ let(:args) { [ 'fake' ] }
56
+
57
+ it "produces response text" do
58
+ mock(dummy_puts).call(anything) do |text|
59
+ expect(text).to include "Commonly used commands"
60
+ end
61
+
62
+ subject.call
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,471 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/concerns/controller_dsl'
3
+
4
+ module Brainstem
5
+ module Concerns
6
+ describe ControllerDSL do
7
+ subject { Class.new { include Brainstem::Concerns::ControllerDSL } }
8
+
9
+ describe ".configuration" do
10
+ it "returns a configuration object" do
11
+ expect(subject.configuration).to be_a Brainstem::DSL::Configuration
12
+ end
13
+ end
14
+
15
+ describe ".nodoc!" do
16
+ it "sets the config nodoc to true" do
17
+ subject.brainstem_params do
18
+ nodoc!
19
+ end
20
+
21
+ expect(subject.configuration[:_default][:nodoc]).to eq true
22
+ end
23
+ end
24
+
25
+ describe ".brainstem_params" do
26
+ it "evaluates the given block in the class context" do
27
+ mock(subject).configuration
28
+ subject.brainstem_params do
29
+ self.configuration
30
+ end
31
+ end
32
+
33
+ it "sets the brainstem_params_context to default when it opens" do
34
+ context = nil
35
+ subject.brainstem_params do
36
+ context = brainstem_params_context
37
+ end
38
+
39
+ expect(context).to eq :_default
40
+ end
41
+
42
+ it "sets the brainstem_params_context to nil when it closes" do
43
+ subject.brainstem_params do
44
+ # No-op
45
+ end
46
+
47
+ expect(subject.brainstem_params_context).to eq nil
48
+ end
49
+ end
50
+
51
+ describe ".description" do
52
+ it "sets the description for the context" do
53
+ subject.brainstem_params do
54
+ description "Main description"
55
+
56
+ actions :show do
57
+ description "Action description"
58
+ end
59
+ end
60
+
61
+ expect(subject.configuration[:_default][:description][:info]).to \
62
+ eq "Main description"
63
+
64
+ expect(subject.configuration[:show][:description][:info]).to \
65
+ eq "Action description"
66
+ end
67
+
68
+ it "allows setting options" do
69
+ subject.brainstem_params do
70
+ description "Main description", nodoc: true
71
+ end
72
+
73
+ expect(subject.configuration[:_default][:description][:nodoc]).to \
74
+ be true
75
+ end
76
+ end
77
+
78
+ describe ".title" do
79
+ it "sets the title for the context" do
80
+ subject.brainstem_params do
81
+ title "Class title"
82
+
83
+ actions :show do
84
+ title "Action title"
85
+ end
86
+ end
87
+
88
+ expect(subject.configuration[:_default][:title][:info]).to \
89
+ eq "Class title"
90
+
91
+ expect(subject.configuration[:show][:title][:info]).to \
92
+ eq "Action title"
93
+ end
94
+
95
+ it "allows passing options" do
96
+ subject.brainstem_params do
97
+ title "Class title", nodoc: true
98
+ end
99
+
100
+ expect(subject.configuration[:_default][:title][:nodoc]).to be true
101
+ end
102
+ end
103
+
104
+ describe ".model_params" do
105
+ before do
106
+ stub(subject).brainstem_model_name { "widgets" }
107
+ end
108
+
109
+ it "evaluates the block given to it" do
110
+ mock(subject).valid(:thing, root: "widgets")
111
+
112
+ subject.model_params :widgets do |param|
113
+ param.valid :thing
114
+ end
115
+ end
116
+
117
+ it "merges options" do
118
+ mock(subject).valid(:thing, root: "widgets", nodoc: true)
119
+
120
+ subject.model_params :widgets do |param|
121
+ param.valid :thing, nodoc: true
122
+ end
123
+ end
124
+ end
125
+
126
+ describe ".valid" do
127
+ context "when given a name and an options hash" do
128
+ it "appends to the valid params hash" do
129
+ subject.brainstem_params do
130
+ valid :sprocket_name,
131
+ info: "sprockets[sprocket_name] is required"
132
+ end
133
+
134
+ expect(subject.configuration[:_default][:valid_params][:sprocket_name][:info]).to \
135
+ eq "sprockets[sprocket_name] is required"
136
+ end
137
+ end
138
+
139
+ context "when given a name and an options hash" do
140
+ it "appends to the valid params hash" do
141
+ # This is HWIA, so all keys are stringified
142
+ data = {
143
+ "recursive" => true,
144
+ "info" => "sprockets[sub_sprockets] is recursive and an array"
145
+ }
146
+
147
+ subject.brainstem_params do
148
+ valid :sub_sprockets, data
149
+ end
150
+
151
+ expect(subject.configuration[:_default][:valid_params][:sub_sprockets]).to \
152
+ eq data
153
+ end
154
+ end
155
+ end
156
+
157
+ describe ".transform" do
158
+ it "appends to the list of transforms" do
159
+ subject.brainstem_params do
160
+ transform :a_key => :another_key
161
+ end
162
+
163
+ expect(subject.configuration[:_default][:transforms]).to be_a Brainstem::DSL::Configuration
164
+ expect(subject.configuration[:_default][:transforms][:a_key]).to eq(:another_key)
165
+ end
166
+ end
167
+
168
+ describe ".presents" do
169
+ context "when class given" do
170
+ it "sets the presenter" do
171
+ klass = Class.new
172
+ subject.brainstem_params do
173
+ presents klass
174
+ end
175
+
176
+ expect(subject.configuration[:_default][:presents][:target_class]).to \
177
+ eq klass
178
+ end
179
+
180
+ it "allows options" do
181
+ klass = Class.new
182
+ subject.brainstem_params do
183
+ presents klass, nodoc: true
184
+ end
185
+
186
+ expect(subject.configuration[:_default][:presents][:nodoc]).to be true
187
+ end
188
+ end
189
+
190
+ context "when no name given" do
191
+ it "falls back to the brainstem_model_class" do
192
+ klass = Class.new
193
+ mock(subject).brainstem_model_class { klass }
194
+
195
+ subject.brainstem_params do
196
+ presents
197
+ end
198
+
199
+ expect(subject.configuration[:_default][:presents][:target_class]).to \
200
+ eq klass
201
+ end
202
+ end
203
+
204
+ context "when symbol given" do
205
+ it "raises an error" do
206
+ expect { subject.brainstem_params { presents :thing } }.to raise_error RuntimeError
207
+ end
208
+ end
209
+
210
+ context "when nil given" do
211
+ it "is explicitly nil" do
212
+ subject.brainstem_params do
213
+ presents nil
214
+ end
215
+
216
+ expect(subject.configuration[:_default][:presents][:target_class]).to \
217
+ eq nil
218
+ end
219
+ end
220
+ end
221
+
222
+ describe ".action_context" do
223
+ it "changes context to a specific action" do
224
+ context = nil
225
+ subject.brainstem_params do
226
+ action_context :show do
227
+ context = brainstem_params_context
228
+ end
229
+ end
230
+
231
+ expect(context).to eq :show
232
+ end
233
+
234
+ it "changes context back to the original context when done" do
235
+ before_context = nil
236
+ inner_context = nil
237
+ after_context = nil
238
+
239
+ subject.brainstem_params do
240
+ before_context = brainstem_params_context
241
+ action_context :show do
242
+ inner_context = brainstem_params_context
243
+ end
244
+ after_context = brainstem_params_context
245
+ end
246
+
247
+ expect(before_context).to eq after_context
248
+ expect(inner_context).to eq :show
249
+ end
250
+
251
+ context "when that context does not exist" do
252
+ before do
253
+ subject.brainstem_params do
254
+ title "My Controller"
255
+ description "This is off the hook."
256
+ action_context :show do
257
+ # No-op
258
+ end
259
+ end
260
+ end
261
+
262
+ it "creates a context that inherits from the default context" do
263
+ expect(subject.configuration.keys).to include "show"
264
+ end
265
+
266
+ it "does not inherit the title" do
267
+ expect(subject.configuration[:show]).not_to have_key(:title)
268
+ end
269
+
270
+ it "does not inherit the description" do
271
+ expect(subject.configuration[:show]).not_to have_key(:description)
272
+ end
273
+ end
274
+
275
+ context "when that context exists" do
276
+ it "uses the existing context" do
277
+ subject.brainstem_params do
278
+ action_context :show do
279
+ valid :param_1, info: "something"
280
+ end
281
+
282
+ action_context :show do
283
+ valid :param_2, info: "something else"
284
+ end
285
+ end
286
+
287
+ expect(subject.configuration[:show][:valid_params].keys).to \
288
+ eq ["param_1", "param_2"]
289
+
290
+ end
291
+ end
292
+ end
293
+
294
+ describe ".actions" do
295
+ it "changes context to multiple actions" do
296
+ mock(subject).action_context(is_a(Symbol)).twice
297
+
298
+ subject.brainstem_params do
299
+ actions [:show, :index] do
300
+ valid :param_1, info: "something"
301
+ end
302
+ end
303
+ end
304
+
305
+ it "allows passing an array" do
306
+ subject.brainstem_params do
307
+ actions [:show, :index] do
308
+ valid :param_1, info: "something"
309
+ end
310
+ end
311
+
312
+ %w(index show).each do |meth|
313
+ expect(subject.configuration[meth.to_sym][:valid_params].keys).to \
314
+ include "param_1"
315
+ end
316
+ end
317
+
318
+ it "allows passing multiple symbols" do
319
+ subject.brainstem_params do
320
+ actions :show, :index do
321
+ valid :param_1, "something"
322
+ end
323
+ end
324
+
325
+ %w(index show).each do |meth|
326
+ expect(subject.configuration[meth.to_sym][:valid_params].keys).to \
327
+ include "param_1"
328
+ end
329
+ end
330
+ end
331
+
332
+ describe "#brainstem_valid_params" do
333
+ let(:brainstem_model_name) { "widget" }
334
+
335
+ before do
336
+ stub(subject).brainstem_model_name { brainstem_model_name }
337
+ stub.any_instance_of(subject).brainstem_model_name { brainstem_model_name }
338
+
339
+ subject.brainstem_params do
340
+ valid :unrelated_root_key,
341
+ info: "it's unrelated."
342
+
343
+ model_params(brainstem_model_name) do |params|
344
+ params.valid :sprocket_parent_id,
345
+ info: "sprockets[sprocket_parent_id] is required"
346
+ end
347
+
348
+ actions :show do
349
+ model_params(brainstem_model_name) do |params|
350
+ params.valid :sprocket_name,
351
+ info: "sprockets[sprocket_name] is required"
352
+ end
353
+ end
354
+ end
355
+ end
356
+
357
+ it "evaluates any param root key with the controller constant if it is callable" do
358
+ stub.any_instance_of(subject).action_name { "show" }
359
+
360
+ subject.brainstem_params do
361
+ model_params Proc.new { |k| k.arbitrary_method } do |params|
362
+ params.valid :nested_key, info: "it's nested!"
363
+ end
364
+ end
365
+
366
+ mock(subject).arbitrary_method { :widget }
367
+ expect(subject.new.brainstem_valid_params).to have_key("nested_key")
368
+ end
369
+
370
+ it "returns the brainstem_model_name children as a hash" do
371
+ stub.any_instance_of(subject).action_name { "show" }
372
+
373
+ expect(subject.new.brainstem_valid_params).to eq({
374
+ "sprocket_name" => { "info" => "sprockets[sprocket_name] is required", "root" => "widget" },
375
+ "sprocket_parent_id" => { "info" => "sprockets[sprocket_parent_id] is required", "root" => "widget" }
376
+ })
377
+ end
378
+
379
+ context "when has context for the current action" do
380
+ before do
381
+ stub.any_instance_of(subject).action_name { "show" }
382
+ end
383
+
384
+ it "returns the valid params for the current action merged with default" do
385
+ expect(subject.new.brainstem_valid_params.keys.sort).to eq ["sprocket_name", "sprocket_parent_id"]
386
+ end
387
+ end
388
+
389
+ context "when has no context for the current action" do
390
+ before do
391
+ any_instance_of(subject) do |instance|
392
+ stub(instance).action_name { "index" }
393
+ end
394
+ end
395
+
396
+ it "falls back to the valid params for the default context" do
397
+ expect(subject.new.brainstem_valid_params.keys.sort).to eq ["sprocket_parent_id"]
398
+ end
399
+ end
400
+ end
401
+
402
+ describe "#transforms" do
403
+ before do
404
+ subject.brainstem_params do
405
+ transform :parent_id => :sprocket_parent_id
406
+
407
+ actions :show do
408
+ transform :name => :sprocket_name
409
+ end
410
+ end
411
+ end
412
+
413
+ it "returns as a symbolized hash" do
414
+ stub.any_instance_of(subject).action_name { "show" }
415
+
416
+ expect(subject.new.transforms).to eq({
417
+ :parent_id => :sprocket_parent_id,
418
+ :name => :sprocket_name
419
+ })
420
+ end
421
+
422
+ context "when has context for the current action" do
423
+ before do
424
+ any_instance_of(subject) do |instance|
425
+ stub(instance).action_name { "show" }
426
+ end
427
+ end
428
+
429
+ it "returns the transforms for the current action merged with default" do
430
+ expect(subject.new.transforms.keys.sort).to eq [:name, :parent_id]
431
+ end
432
+ end
433
+
434
+ context "when has no context for the current action" do
435
+ before do
436
+ any_instance_of(subject) do |instance|
437
+ stub(instance).action_name { "index" }
438
+ end
439
+ end
440
+
441
+ it "falls back to the valid params for the default context" do
442
+ expect(subject.new.transforms.keys.sort).to eq [:parent_id]
443
+ end
444
+ end
445
+ end
446
+
447
+ describe "#contextual_key" do
448
+ it "looks up the key for a given context" do
449
+ subject.brainstem_params do
450
+ description "blah"
451
+
452
+ actions :show do
453
+ description "less blah"
454
+ end
455
+ end
456
+
457
+ expect(subject.new.send(:contextual_key, :show, :description)[:info]).to \
458
+ eq "less blah"
459
+ end
460
+
461
+ it "returns the parent key if the given key is not found in context" do
462
+ subject.brainstem_params do
463
+ description "blah"
464
+ end
465
+
466
+ expect(subject.new.send(:contextual_key, :show, :description)[:info]).to eq "blah"
467
+ end
468
+ end
469
+ end
470
+ end
471
+ end