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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 115ca6ee598304821711410e09788b194b4463d3
|
|
4
|
+
data.tar.gz: eb49966eafe33595f32048f087ee7148a0b4da36
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 45af28ac0b45a91167174b0809e79c22a20864cb2facb3c59c23fdad0d71e62dec0299db8f74748a0c0306169aa05eaf756f5564459f43d19edb2b0045bbb30c
|
|
7
|
+
data.tar.gz: 8050a810b6b1046d00d7aa961bebe48698604be562f28c73943c5160d4668b47c92ff9c4bb52a03e67eb67804d4f9d5a8391507cd5328b8138546e7febefcdc8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
+ **1.0.0 - _07/20/2017_
|
|
4
|
+
- Add the capability to generate the documentation extracted from your properly annotated
|
|
5
|
+
presenters and controllers using `bundle exec brainstem generate [ARGS]`.
|
|
6
|
+
- Update Brainstem to use Ruby version 2.3.3.
|
|
7
|
+
- Add support for Ruby versions 2.2.7, 2.3.4 & 2.4.1 and drop support for Ruby version 2.1.10.
|
|
8
|
+
- Drop support for specifying description param as a string and not with the `info` key in the options hash.
|
|
9
|
+
|
|
10
|
+
+ **1.0.0.pre.2** - _04/12/2017_
|
|
11
|
+
- Added support for specifying the description for conditionals, fields and associations with the `info` key in the options hash.
|
|
12
|
+
- Added a deprecation warning when description param is specified as a string and not with the `info` key in the options hash.
|
|
13
|
+
- Fixed: support for conditional, field and association options to be a hash with indifferent access.
|
|
14
|
+
|
|
3
15
|
+ **1.0.0.pre.1** - _03/07/2017_
|
|
4
16
|
- Implemented new presenter DSL.
|
|
5
17
|
- Added controller helpers for presenting errors.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -66,11 +66,11 @@ module Api
|
|
|
66
66
|
|
|
67
67
|
# Specify the fields to be present in the returned JSON.
|
|
68
68
|
fields do
|
|
69
|
-
field :name, :string, "the Widget's name"
|
|
70
|
-
field :legacy, :boolean, "true for legacy Widgets, false otherwise", via: :legacy?
|
|
71
|
-
field :longform_description, :string, "feature-length description of this Widget", optional: true
|
|
72
|
-
field :updated_at, :datetime, "the time of this Widget's last update"
|
|
73
|
-
field :created_at, :datetime, "the time at which this Widget was created"
|
|
69
|
+
field :name, :string, info: "the Widget's name"
|
|
70
|
+
field :legacy, :boolean, info: "true for legacy Widgets, false otherwise", via: :legacy?
|
|
71
|
+
field :longform_description, :string, info: "feature-length description of this Widget", optional: true
|
|
72
|
+
field :updated_at, :datetime, info: "the time of this Widget's last update"
|
|
73
|
+
field :created_at, :datetime, info: "the time at which this Widget was created"
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
# Associations can be included by providing include=association_name in the URL.
|
|
@@ -78,8 +78,8 @@ module Api
|
|
|
78
78
|
# columns on the model, otherwise the user must explicitly request associations
|
|
79
79
|
# to avoid unnecessary loads.
|
|
80
80
|
associations do
|
|
81
|
-
association :features, Feature, "features associated with this Widget"
|
|
82
|
-
association :location, Location, "the location of this Widget"
|
|
81
|
+
association :features, Feature, info: "features associated with this Widget"
|
|
82
|
+
association :location, Location, info: "the location of this Widget"
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
end
|
|
@@ -119,6 +119,7 @@ The `Brainstem::ControllerMethods` concern provides:
|
|
|
119
119
|
* `brainstem_model_name` which is inferred from your controller name or settable with `self.brainstem_model_name = :thing`.
|
|
120
120
|
* `brainstem_present` and `brainstem_present_object` for presenting a scope of models or a single model.
|
|
121
121
|
* `brainstem_model_error` and `brainstem_system_error` for presenting model and system error messages.
|
|
122
|
+
* Various methods for auto-documentation of your API.
|
|
122
123
|
|
|
123
124
|
### Controller Best Practices
|
|
124
125
|
|
|
@@ -164,7 +165,7 @@ module Api
|
|
|
164
165
|
end
|
|
165
166
|
```
|
|
166
167
|
|
|
167
|
-
### Setup Rails to
|
|
168
|
+
### Setup Rails to Load Brainstem
|
|
168
169
|
|
|
169
170
|
To configure Brainstem for development and production, we do the following:
|
|
170
171
|
|
|
@@ -173,8 +174,9 @@ To configure Brainstem for development and production, we do the following:
|
|
|
173
174
|
2) We setup an initializer in `config/initializers/brainstem.rb`, similar to the following:
|
|
174
175
|
|
|
175
176
|
```ruby
|
|
176
|
-
# In order to support live code reload in the development environment, we
|
|
177
|
-
# runs once in production (before the
|
|
177
|
+
# In order to support live code reload in the development environment, we
|
|
178
|
+
# register a `to_prepare` callback. This # runs once in production (before the
|
|
179
|
+
# first request) and whenever a file has changed in development.
|
|
178
180
|
Rails.application.config.to_prepare do
|
|
179
181
|
# Forget all Brainstem configuration.
|
|
180
182
|
Brainstem.reset!
|
|
@@ -182,9 +184,11 @@ Rails.application.config.to_prepare do
|
|
|
182
184
|
# Set the current default API namespace.
|
|
183
185
|
Brainstem.default_namespace = :v1
|
|
184
186
|
|
|
185
|
-
# (Optional) Load a default base helper into all presenters. You could use
|
|
186
|
-
#
|
|
187
|
-
#
|
|
187
|
+
# (Optional) Load a default base helper into all presenters. You could use
|
|
188
|
+
# this to bring in a concept like `current_user`. # While not necessarily the
|
|
189
|
+
# best approach, something like http://stackoverflow.com/a/11670283 can
|
|
190
|
+
# currently be used to # access the requesting user inside of a Brainstem
|
|
191
|
+
# presenter. We hope to clean this up by allowing a user to be passed in #
|
|
188
192
|
# when presenting in the future.
|
|
189
193
|
module ApiHelper
|
|
190
194
|
def current_user
|
|
@@ -269,6 +273,343 @@ Brainstem parses the request params and supports the following:
|
|
|
269
273
|
keywords, such as `search`, `page`, `per_page`, `limit`, `offset`, `order`, `only`, or `include`.
|
|
270
274
|
* Brainstem supports optional fields which will only be returned when requested, for example: `optional_fields=field1,field2`
|
|
271
275
|
|
|
276
|
+
--
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
## The `brainstem` executable
|
|
280
|
+
|
|
281
|
+
The `brainstem` executable provided with the gem is at the moment used only to
|
|
282
|
+
generate API docs from the command line, but you can verify which commands are
|
|
283
|
+
available simply by running:
|
|
284
|
+
|
|
285
|
+
```sh
|
|
286
|
+
bundle exec brainstem
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
This will give you a list of all available commands. Additional help is
|
|
290
|
+
available for each command, and can be found by passing the command
|
|
291
|
+
the `help` flag, i.e.:
|
|
292
|
+
|
|
293
|
+
```sh
|
|
294
|
+
bundle exec brainstem generate --help
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
--
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
## API Documentation
|
|
301
|
+
|
|
302
|
+
### The `generate` command
|
|
303
|
+
|
|
304
|
+
Running `bundle exec brainstem generate [ARGS]` will generate the documentation
|
|
305
|
+
extracted from your properly annotated presenters and controllers.
|
|
306
|
+
|
|
307
|
+
Note that this does not, at present, *remove* existing docs that may be present
|
|
308
|
+
from a previous generation, so it is recommended that you use this executable as
|
|
309
|
+
part of a large shell script that empties your directory and regenerates over
|
|
310
|
+
top of it if you expect much churn.
|
|
311
|
+
|
|
312
|
+
### Customizing behavior
|
|
313
|
+
|
|
314
|
+
While options can be passed on the command line, this can complicate the
|
|
315
|
+
invocation, especially when the desired settings are often specific to the
|
|
316
|
+
project and do not often change.
|
|
317
|
+
|
|
318
|
+
As a result, it is possible to specify options through an initializer in your
|
|
319
|
+
application that will be used in the absence of command-line flags. Thus,
|
|
320
|
+
configuration precedence is in the following order:
|
|
321
|
+
|
|
322
|
+
1. Command-line flags;
|
|
323
|
+
2. Initializer settings;
|
|
324
|
+
3. Built-in defaults.
|
|
325
|
+
|
|
326
|
+
To see a list of the available command-line options, run `bundle exec brainstem
|
|
327
|
+
generate --help`.
|
|
328
|
+
|
|
329
|
+
To see a list of the available initializer settings, view
|
|
330
|
+
[lib/brainstem/api_docs.rb](./lib/brainstem/api_docs.rb). You can configure
|
|
331
|
+
these in your initializers just by setting them:
|
|
332
|
+
|
|
333
|
+
```ruby
|
|
334
|
+
# config/initializers/brainstem.rb
|
|
335
|
+
|
|
336
|
+
Brainstem::ApiDocs.tap do |config|
|
|
337
|
+
config.write_path = "/path/to/output"
|
|
338
|
+
end
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Annotating an API
|
|
342
|
+
|
|
343
|
+
#### Presenters / Data Models
|
|
344
|
+
|
|
345
|
+
By and large, Presenters are self-documenting: simply using them as intended
|
|
346
|
+
will yield a panoply of data.
|
|
347
|
+
|
|
348
|
+
##### Docstrings
|
|
349
|
+
|
|
350
|
+
All common methods that do not explicitly take a description take an `:info`
|
|
351
|
+
option, which allows for the specification of an explanatory documentation
|
|
352
|
+
string.
|
|
353
|
+
|
|
354
|
+
As a general rule of thumb, methods that are not used within a block tend to
|
|
355
|
+
accept `:info` strings, and those used within a block tend to have their own
|
|
356
|
+
`description` argument.
|
|
357
|
+
|
|
358
|
+
For example:
|
|
359
|
+
|
|
360
|
+
```ruby
|
|
361
|
+
class MyPresenter < Brainstem::Presenter
|
|
362
|
+
sort_order :cost, info: "Sorts by cost" do |scope, direction|
|
|
363
|
+
scope.reorder("myobjects.cost #{direction}")
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
The methods that take an `:info` option include:
|
|
369
|
+
|
|
370
|
+
- `sort_order`
|
|
371
|
+
- `filter`
|
|
372
|
+
- `association`
|
|
373
|
+
- `request/model`
|
|
374
|
+
- `field` — also displays the documentation of any condition set in its
|
|
375
|
+
`:if` option.
|
|
376
|
+
|
|
377
|
+
The following do not accept documentation:
|
|
378
|
+
|
|
379
|
+
- `default_sort_order`
|
|
380
|
+
- `preload`
|
|
381
|
+
|
|
382
|
+
##### Nodoc
|
|
383
|
+
|
|
384
|
+
The following methods accept a `:nodoc` boolean option, which indicates that the
|
|
385
|
+
documentation should be suppressed for this particular entry:
|
|
386
|
+
|
|
387
|
+
- `association` — hides the association
|
|
388
|
+
- `field` — hides the field
|
|
389
|
+
- `sort_order` — hides the sort order
|
|
390
|
+
- `filter` — hides the filter
|
|
391
|
+
- `request` / `model` — causes the conditional not to be
|
|
392
|
+
listed on any field which specifies it
|
|
393
|
+
|
|
394
|
+
##### Additional Documentables
|
|
395
|
+
|
|
396
|
+
In addition to the above, there are three additional methods in the DSL designed
|
|
397
|
+
primarily for documentation:
|
|
398
|
+
|
|
399
|
+
- `nodoc!` — within a presenter or the `brainstem_params` block within a controller, skips generating the documentation entirely. Useful for hidden or non-public endpoints.
|
|
400
|
+
- `title(str, options)` — used to specify an alternate title for the
|
|
401
|
+
Presenter.
|
|
402
|
+
- `nodoc: true` — forces fallback to the Presenter's constant
|
|
403
|
+
- `description(str, options)` — used to specify a description for the
|
|
404
|
+
Presenter.
|
|
405
|
+
- `nodoc: true` — displays no description
|
|
406
|
+
|
|
407
|
+
##### Example
|
|
408
|
+
|
|
409
|
+
```ruby
|
|
410
|
+
class PostsPresenter < Brainstem::Presenter
|
|
411
|
+
presents Post
|
|
412
|
+
|
|
413
|
+
# Hide the entire presenter
|
|
414
|
+
#
|
|
415
|
+
# nodoc!
|
|
416
|
+
|
|
417
|
+
# If we temporarily want to disable the custom title, and just display
|
|
418
|
+
# 'Posts', we can add a 'nodoc' option set to true.
|
|
419
|
+
#
|
|
420
|
+
# title "Blog Posts", nodoc: true
|
|
421
|
+
|
|
422
|
+
title "Blog Posts"
|
|
423
|
+
|
|
424
|
+
description <<-MARKDOWN.strip_heredoc
|
|
425
|
+
The blog post is the primary entity in the blog, which represents a single
|
|
426
|
+
post by one of our authors.
|
|
427
|
+
MARKDOWN
|
|
428
|
+
|
|
429
|
+
associations do
|
|
430
|
+
association :author, User, info: "the author of the post"
|
|
431
|
+
|
|
432
|
+
# Temporarily disable documenting this relationship as we revamp the
|
|
433
|
+
# editorial system:
|
|
434
|
+
association :editor, User, info: "the editor of the post", nodoc: true
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### Controllers
|
|
440
|
+
|
|
441
|
+
The configuration for a controller takes place inside the `brainstem_params` block, e.g.:
|
|
442
|
+
|
|
443
|
+
```ruby
|
|
444
|
+
class PostsController < ApiController
|
|
445
|
+
include Brainstem::Concerns::ControllerDSL
|
|
446
|
+
|
|
447
|
+
brainstem_params do
|
|
448
|
+
title "Posts"
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
##### Action Contexts
|
|
454
|
+
|
|
455
|
+
Configuration that is specified within the root level of the `brainstem_params`
|
|
456
|
+
block is applied to the entire controller, and every action within the
|
|
457
|
+
controller. This is referred to as the 'default' context, because it is used as
|
|
458
|
+
the default for all actions. This lets you specify common defaults for all
|
|
459
|
+
actions, as well as a title and description for the controller, which, along
|
|
460
|
+
with an annotation of `nodoc!`, are not inherited by the actions.
|
|
461
|
+
|
|
462
|
+
Each action has its own action context, and the documentation is smart enough
|
|
463
|
+
to know that what you want to document for the `index` action is likely not
|
|
464
|
+
what you'd like to document for the `show` action, but you are also likely to
|
|
465
|
+
have your `create` and `update` methods be very similar.
|
|
466
|
+
|
|
467
|
+
You can define an action context and place any configuration inside this
|
|
468
|
+
context, and it will keep the documentation isolated to that specific action:
|
|
469
|
+
|
|
470
|
+
```ruby
|
|
471
|
+
brainstem_params do
|
|
472
|
+
valid :global_controller_param,
|
|
473
|
+
info: "A trivial example of a param that applies to all actions."
|
|
474
|
+
|
|
475
|
+
actions :index do
|
|
476
|
+
# This adds a `blog_id` param to just the `index` action.
|
|
477
|
+
valid :blog_id, info: "The id of the blog to which this post belongs"
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
actions :create, :update do
|
|
481
|
+
# This will add an `id` param to both `create` and `update` actions.
|
|
482
|
+
valid :id, info: "The id of the blog post"
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Action contexts, like the default context, are inherited from the parent
|
|
488
|
+
controller. So it is often possible to express common setup in the more
|
|
489
|
+
abstract controllers, like so:
|
|
490
|
+
|
|
491
|
+
```ruby
|
|
492
|
+
class ApiController
|
|
493
|
+
brainstem_params do
|
|
494
|
+
actions :destroy do
|
|
495
|
+
presents nil
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
class PostsController << ApiController; end
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
In this example, `PostsController` will list no presenter for its `destroy`
|
|
504
|
+
method as it inherits this from `ApiController`.
|
|
505
|
+
|
|
506
|
+
**It is important to specify everything at the most specific level possible.**
|
|
507
|
+
Action contexts have a higher priority than defaults, and will fall back to the
|
|
508
|
+
action context of the parent controller before they check the default of the
|
|
509
|
+
child controller. It's therefore recommended that your documentation be kept
|
|
510
|
+
in action contexts as much as possible.
|
|
511
|
+
|
|
512
|
+
##### `title` / `description` / `nodoc!`
|
|
513
|
+
|
|
514
|
+
Any of these can be used inside an action context as well.
|
|
515
|
+
|
|
516
|
+
```ruby
|
|
517
|
+
class BlogPostsController < ApiController
|
|
518
|
+
brainstem_params do
|
|
519
|
+
|
|
520
|
+
# Make the displayed title of this controller "Posts"
|
|
521
|
+
title "Posts"
|
|
522
|
+
|
|
523
|
+
# Fall back to 'BlogPostsController' for a title
|
|
524
|
+
title "Posts", nodoc: true
|
|
525
|
+
|
|
526
|
+
# Show description
|
|
527
|
+
description "Access blog posts through these endpoints."
|
|
528
|
+
|
|
529
|
+
# Hide description
|
|
530
|
+
description "...", nodoc: true
|
|
531
|
+
|
|
532
|
+
# Do not document this controller or any of its endpoints!
|
|
533
|
+
nodoc!
|
|
534
|
+
|
|
535
|
+
actions :index do
|
|
536
|
+
# Set the title of this action
|
|
537
|
+
title "Listing blog posts"
|
|
538
|
+
description "..."
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
actions :show do
|
|
542
|
+
# Do not display this action.
|
|
543
|
+
nodoc!
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
##### `valid` / `model_params`
|
|
552
|
+
|
|
553
|
+
```ruby
|
|
554
|
+
class BlogPostsController < ApiController
|
|
555
|
+
brainstem_params do
|
|
556
|
+
|
|
557
|
+
# Add an `:category_id` param to all actions in this controller / children:
|
|
558
|
+
valid :category_id, info: "(required) the category's ID"
|
|
559
|
+
|
|
560
|
+
# Do not document this additional field.
|
|
561
|
+
valid :lang,
|
|
562
|
+
info: "(optional) the language of the requested post",
|
|
563
|
+
nodoc: true
|
|
564
|
+
|
|
565
|
+
actions :show do
|
|
566
|
+
# Declare a nested param under the `brainstem_model_name` root key,
|
|
567
|
+
# i.e. `params[:blog_post][:id]`):
|
|
568
|
+
model_params do |post|
|
|
569
|
+
post.valid :id, info: "(required) the id of the post"
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
actions :share do
|
|
574
|
+
# Declare a nested param with an explicit root key:, i.e. `params[:share][...]`
|
|
575
|
+
model_param :share do
|
|
576
|
+
# ...
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
def self.param_root
|
|
582
|
+
:widgets
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
actions :update do
|
|
587
|
+
# Declare a dynamic root key, i.e. `params[:widgets][:id]`
|
|
588
|
+
model_params(-> (controller_klass) { controller_class.param_root } do |p|
|
|
589
|
+
p.valid :id #, ...
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
end
|
|
593
|
+
end
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
##### `presents`
|
|
597
|
+
|
|
598
|
+
```ruby
|
|
599
|
+
class BlogPostsController < ApiController
|
|
600
|
+
brainstem_params do
|
|
601
|
+
# Includes a link to the presenter for `BlogPost` in each action.
|
|
602
|
+
presents BlogPost
|
|
603
|
+
end
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
### Extending and Customizing the API Documentation
|
|
607
|
+
|
|
608
|
+
For more information on extending and customizing the API documentation, please
|
|
609
|
+
see the
|
|
610
|
+
[API Doc Generator developer documentation](./docs/api_doc_generator.markdown).
|
|
611
|
+
|
|
612
|
+
|
|
272
613
|
--
|
|
273
614
|
|
|
274
615
|
For more detailed examples, please see the rest of this README and our detailed
|
|
@@ -524,12 +865,15 @@ Brainstem provides a rich DSL for building presenters. This section details the
|
|
|
524
865
|
|
|
525
866
|
```ruby
|
|
526
867
|
fields do
|
|
527
|
-
field :name, :string, "the Widget's name"
|
|
528
|
-
field :legacy, :boolean,
|
|
868
|
+
field :name, :string, info: "the Widget's name"
|
|
869
|
+
field :legacy, :boolean,
|
|
870
|
+
info: "true for legacy Widgets, false otherwise",
|
|
529
871
|
via: :legacy?
|
|
530
|
-
field :dynamic_name, :string,
|
|
872
|
+
field :dynamic_name, :string,
|
|
873
|
+
info: "a formatted name for this Widget",
|
|
531
874
|
dynamic: lambda { |widget| "This Widget's name is #{widget.name}" }
|
|
532
|
-
field :longform_description, :string,
|
|
875
|
+
field :longform_description, :string,
|
|
876
|
+
info: "feature-length description of this Widget",
|
|
533
877
|
optional: true
|
|
534
878
|
|
|
535
879
|
# Fields can be nested
|
|
@@ -561,14 +905,16 @@ Brainstem provides a rich DSL for building presenters. This section details the
|
|
|
561
905
|
|
|
562
906
|
```ruby
|
|
563
907
|
associations do
|
|
564
|
-
association :features, Feature, "features associated with this Widget"
|
|
565
|
-
association :location, Location, "the location of this Widget"
|
|
566
|
-
association :previous_location, Location,
|
|
908
|
+
association :features, Feature, info: "features associated with this Widget"
|
|
909
|
+
association :location, Location, info: "the location of this Widget"
|
|
910
|
+
association :previous_location, Location,
|
|
911
|
+
info: "the Widget's previous location",
|
|
567
912
|
dynamic: lambda { |widget| widget.previous_locations.first }
|
|
568
|
-
association :associated_objects, :polymorphic,
|
|
913
|
+
association :associated_objects, :polymorphic,
|
|
914
|
+
info: "a mixture of objects related to this Widget"
|
|
569
915
|
end
|
|
570
916
|
```
|
|
571
|
-
|
|
917
|
+
|
|
572
918
|
* `lookup` - Use this option to avoid N + 1 queries for Fields and Associations. The `lookup` lambda runs once when
|
|
573
919
|
presenting and every presented model gets its assocation or value from the cache the `lookup` lambda generates. The
|
|
574
920
|
`lookup` lambda takes in the presented models and should generate a cache containing the models' coresponding assocations
|
|
@@ -579,7 +925,8 @@ the `lookup` will be used.
|
|
|
579
925
|
|
|
580
926
|
```ruby
|
|
581
927
|
associations do
|
|
582
|
-
association :current_user_groups, Group,
|
|
928
|
+
association :current_user_groups, Group,
|
|
929
|
+
info: "the Groups for the current user",
|
|
583
930
|
lookup: lambda { |models|
|
|
584
931
|
Group.where(subject_id: models.map(&:id)
|
|
585
932
|
.where(user_id: current_user.id)
|
|
@@ -595,12 +942,13 @@ the `lookup` will be used.
|
|
|
595
942
|
|
|
596
943
|
```ruby
|
|
597
944
|
fields do
|
|
598
|
-
field :current_user_post_count, Post,
|
|
599
|
-
|
|
945
|
+
field :current_user_post_count, Post,
|
|
946
|
+
info: "count of Posts the current_user has for this model",
|
|
947
|
+
lookup: lambda { |models|
|
|
600
948
|
lookup = Post.where(subject_id: models.map(&:id)
|
|
601
949
|
.where(user_id: current_user.id)
|
|
602
|
-
.group_by { |post| post.subject_id }
|
|
603
|
-
|
|
950
|
+
.group_by { |post| post.subject_id }
|
|
951
|
+
|
|
604
952
|
lookup
|
|
605
953
|
},
|
|
606
954
|
lookup_fetch: lambda { |lookup, model| lookup[model.id] }
|
|
@@ -617,24 +965,27 @@ the `lookup` will be used.
|
|
|
617
965
|
conditionals do
|
|
618
966
|
model :title_is_hello,
|
|
619
967
|
lambda { |model| model.title == 'hello' },
|
|
620
|
-
'visible when the title is hello'
|
|
968
|
+
info: 'visible when the title is hello'
|
|
621
969
|
|
|
622
970
|
request :user_is_bob,
|
|
623
971
|
lambda { current_user == 'bob' }, # Assuming some sort of `helper` that provides `current_user`
|
|
624
|
-
'visible only to bob'
|
|
972
|
+
info: 'visible only to bob'
|
|
625
973
|
end
|
|
626
974
|
|
|
627
975
|
fields do
|
|
628
|
-
field :hello_title, :string,
|
|
976
|
+
field :hello_title, :string,
|
|
977
|
+
info: 'the title, when it is exactly the word "hello"',
|
|
629
978
|
dynamic: lambda { |model| model.title + " is the title" },
|
|
630
979
|
if: :title_is_hello
|
|
631
980
|
|
|
632
|
-
field :secret, :string,
|
|
981
|
+
field :secret, :string,
|
|
982
|
+
info: "a secret, via the secret_info model method, only visible to bob and when the model's title is hello",
|
|
633
983
|
via: :secret_info,
|
|
634
984
|
if: [:user_is_bob, :title_is_hello]
|
|
635
985
|
|
|
636
986
|
with_options if: :user_is_bob do
|
|
637
|
-
field :bob_title, :string,
|
|
987
|
+
field :bob_title, :string,
|
|
988
|
+
info: 'another name for the title, only visible to Bob',
|
|
638
989
|
via: :title
|
|
639
990
|
end
|
|
640
991
|
end
|