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,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