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
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# A Command is a callable object that acts as an entrypoint into the
|
|
5
|
+
# application logic. It is responsible for translating given options into
|
|
6
|
+
# specific enquiries.
|
|
7
|
+
#
|
|
8
|
+
# Internally, it wraps an OptionParser instance returned through its
|
|
9
|
+
# +option_parser` method. The evaluation of this parser mutates the +options+
|
|
10
|
+
# hash. This is available to the +call+ method on the instance, which is the
|
|
11
|
+
# primary point of application logic execution.
|
|
12
|
+
#
|
|
13
|
+
module Brainstem
|
|
14
|
+
module CLI
|
|
15
|
+
class AbstractCommand
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# Convenience method for instantiating the command and calling it.
|
|
19
|
+
#
|
|
20
|
+
# @return [Brainstem::CLI::AbstractCommand] the instance
|
|
21
|
+
#
|
|
22
|
+
def self.call(args = [])
|
|
23
|
+
instance = new(args)
|
|
24
|
+
instance.call
|
|
25
|
+
instance
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# Returns a new instance of the command with options set.
|
|
31
|
+
#
|
|
32
|
+
def initialize(args = [])
|
|
33
|
+
self.args = args
|
|
34
|
+
self.options = default_options
|
|
35
|
+
extract_options!
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
#
|
|
40
|
+
# Returns the hash of default options used as a base into which cli args
|
|
41
|
+
# are merged.
|
|
42
|
+
#
|
|
43
|
+
def default_options
|
|
44
|
+
{}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# Kicks off execution of app-level code. Has available to it +options+,
|
|
50
|
+
# which contains the options extracted from the command line.
|
|
51
|
+
#
|
|
52
|
+
def call
|
|
53
|
+
raise NotImplementedError,
|
|
54
|
+
"Override #call and implement your application logic."
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
#
|
|
59
|
+
# Storage for given options.
|
|
60
|
+
#
|
|
61
|
+
attr_accessor :options
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
#
|
|
65
|
+
# Storage for passed, unparsed args.
|
|
66
|
+
#
|
|
67
|
+
attr_accessor :args
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
#
|
|
71
|
+
# Extracts command-line options for this specific command based on the
|
|
72
|
+
# +OptionParser+ specified in +self.option_parser+.
|
|
73
|
+
#
|
|
74
|
+
# @return [Hash] the extracted command-line options
|
|
75
|
+
#
|
|
76
|
+
def extract_options!
|
|
77
|
+
option_parser.order!(args)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
#
|
|
82
|
+
# An +OptionParser+ instance that specifies how options should be
|
|
83
|
+
# extracted specific to this command.
|
|
84
|
+
#
|
|
85
|
+
# Available to this method is the +options+ method, which is the primary
|
|
86
|
+
# method of communicating options to the +call+ method.
|
|
87
|
+
#
|
|
88
|
+
# @return [OptionParser] an instance of OptionParser
|
|
89
|
+
# @see http://ruby-doc.org/stdlib-2.2.2/libdoc/optparse/rdoc/OptionParser.html
|
|
90
|
+
#
|
|
91
|
+
def option_parser
|
|
92
|
+
raise NotImplementedError,
|
|
93
|
+
"Must return an instance of OptionParser."
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
require 'brainstem/cli/abstract_command'
|
|
2
|
+
require 'brainstem/api_docs'
|
|
3
|
+
require 'brainstem/api_docs/exceptions'
|
|
4
|
+
require 'brainstem/api_docs/builder'
|
|
5
|
+
|
|
6
|
+
#
|
|
7
|
+
# Require all sinks and formatters.
|
|
8
|
+
#
|
|
9
|
+
sinks_path = File.expand_path('../../api_docs/sinks/**/*.rb', __FILE__)
|
|
10
|
+
formatters_path = File.expand_path('../../api_docs/formatters/**/*.rb', __FILE__)
|
|
11
|
+
Dir.glob(sinks_path).each { |f| require f }
|
|
12
|
+
Dir.glob(formatters_path).each { |f| require f }
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
# The GenerateApiDocsCommand is responsible for the construction and
|
|
16
|
+
# intermediation of the two primary components of automatic API doc generation.
|
|
17
|
+
#
|
|
18
|
+
# It instantiates both a +Builder+, which is responsible for introspecting and
|
|
19
|
+
# validation of the host application, and also passes the +Builder+-generated
|
|
20
|
+
# data structure to the +Sink+, which is responsible for serializing the data
|
|
21
|
+
# structure to some store (likely transforming the data along the way).
|
|
22
|
+
#
|
|
23
|
+
module Brainstem
|
|
24
|
+
module CLI
|
|
25
|
+
class GenerateApiDocsCommand < AbstractCommand
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def call
|
|
29
|
+
ensure_sink_specified!
|
|
30
|
+
construct_builder!
|
|
31
|
+
present_atlas!
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def default_sink_method
|
|
36
|
+
Brainstem::ApiDocs::Sinks::ControllerPresenterMultifileSink.method(:new)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def default_options
|
|
41
|
+
{
|
|
42
|
+
sink: {
|
|
43
|
+
method: default_sink_method,
|
|
44
|
+
options: {}
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
builder: {
|
|
48
|
+
args_for_atlas: { controller_matches: [] },
|
|
49
|
+
args_for_introspector: {
|
|
50
|
+
base_presenter_class: ::Brainstem::ApiDocs.method(:base_presenter_class),
|
|
51
|
+
base_controller_class: ::Brainstem::ApiDocs.method(:base_controller_class),
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
attr_accessor :builder
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
#########################################################################
|
|
62
|
+
private
|
|
63
|
+
#########################################################################
|
|
64
|
+
|
|
65
|
+
#
|
|
66
|
+
# Instantiates a builder, passing the relevant options to it.
|
|
67
|
+
#
|
|
68
|
+
def construct_builder!
|
|
69
|
+
@builder = Brainstem::ApiDocs::Builder.new(builder_options)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
#
|
|
74
|
+
# Hands the atlas over to the sink.
|
|
75
|
+
#
|
|
76
|
+
def present_atlas!
|
|
77
|
+
sink_method.call(sink_options) << builder.atlas
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
#
|
|
82
|
+
# Raises an error unless the user specified a destination for the output.
|
|
83
|
+
#
|
|
84
|
+
def ensure_sink_specified!
|
|
85
|
+
raise Brainstem::ApiDocs::NoSinkSpecifiedException unless sink_method
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
#
|
|
90
|
+
# Utility method for retrieving the sink.
|
|
91
|
+
#
|
|
92
|
+
def sink_method
|
|
93
|
+
@sink_method ||= options[:sink][:method]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
#
|
|
98
|
+
# Utility method for retrieving builder options.
|
|
99
|
+
#
|
|
100
|
+
def builder_options
|
|
101
|
+
@builder_options ||= options[:builder]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
#
|
|
106
|
+
# Utility method for retrieving sink options.
|
|
107
|
+
#
|
|
108
|
+
def sink_options
|
|
109
|
+
@sink_options ||= options[:sink][:options]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
#
|
|
114
|
+
# Defines the option parser for this command.
|
|
115
|
+
#
|
|
116
|
+
# @return [OptionParser] the option parser that should mutate the
|
|
117
|
+
# +options+ hash.
|
|
118
|
+
#
|
|
119
|
+
def option_parser
|
|
120
|
+
OptionParser.new do |opts|
|
|
121
|
+
opts.banner = "Usage: generate [options]"
|
|
122
|
+
|
|
123
|
+
opts.on('-m', '--multifile-presenters-and-controllers',
|
|
124
|
+
'dumps presenters and controllers to separate files (default)') do |o|
|
|
125
|
+
options[:sink][:method] = \
|
|
126
|
+
Brainstem::ApiDocs::Sinks::ControllerPresenterMultifileSink.method(:new)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
opts.on('--host-env-file=PATH', "path to host app's entry file") do |o|
|
|
131
|
+
options[:builder][:args_for_introspector][:rails_environment_file] = o
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
opts.on('-o RELATIVE_DIR', '--output-dir=RELATIVE_DIR',
|
|
136
|
+
'specifies directory which to output if relevant') do |o|
|
|
137
|
+
options[:sink][:options][:write_path] = o
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
opts.on('--base-presenter-class=CLASS', "which class to look up presenters on") do |o|
|
|
142
|
+
options[:builder][:args_for_introspector][:base_presenter_class] = o
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
opts.on('--base-controller-class=CLASS', "which class to look up controllers on") do |o|
|
|
147
|
+
options[:builder][:args_for_introspector][:base_controller_class] = o
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
opts.on('--controller-matches=MATCH',
|
|
152
|
+
'a case-sensitive regexp used to winnow the list of '\
|
|
153
|
+
'controllers. It is matched against the constant, not '\
|
|
154
|
+
'underscored name of the controller. Specifying multiple '\
|
|
155
|
+
'performs a logical AND between Regexen.') do |o|
|
|
156
|
+
# Trim slashes on passed MATCH.
|
|
157
|
+
matcher = Regexp.new(o.gsub(/(\A\/)|(\/\z)/, ''), 'i')
|
|
158
|
+
options[:builder][:args_for_atlas][:controller_matches].push(matcher)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
opts.on('--markdown', 'use markdown format') do |o|
|
|
163
|
+
options[:sink][:options][:format] = :markdown
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
require 'brainstem/concerns/inheritable_configuration'
|
|
2
|
+
require 'active_support/core_ext/object/with_options'
|
|
3
|
+
|
|
4
|
+
module Brainstem
|
|
5
|
+
module Concerns
|
|
6
|
+
module ControllerDSL
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
include Brainstem::Concerns::InheritableConfiguration
|
|
9
|
+
|
|
10
|
+
DEFAULT_BRAINSTEM_PARAMS_CONTEXT = :_default
|
|
11
|
+
|
|
12
|
+
included do
|
|
13
|
+
reset_configuration!
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
module ClassMethods
|
|
18
|
+
def reset_configuration!
|
|
19
|
+
configuration.nest! :_default
|
|
20
|
+
configuration[:_default].tap do |default|
|
|
21
|
+
default.nest! :valid_params
|
|
22
|
+
default.nest! :transforms
|
|
23
|
+
default.nonheritable! :title
|
|
24
|
+
default.nonheritable! :description
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# In order to correctly scope the DSL, we must have a context under
|
|
31
|
+
# which keys are stored. The default context is _default (to avoid
|
|
32
|
+
# any potential collisions with methods named 'default'), and this
|
|
33
|
+
# context is used as the parent context for all other contexts.
|
|
34
|
+
#
|
|
35
|
+
# The context will change, for example, when we are adding keys to the
|
|
36
|
+
# configuration for actions. In those cases, the context becomes the
|
|
37
|
+
# +action_name+.
|
|
38
|
+
#
|
|
39
|
+
# Any methods that change the context should change it back upon
|
|
40
|
+
# conclusion so that the assumption of consistent scope inside a block
|
|
41
|
+
# is possible.
|
|
42
|
+
#
|
|
43
|
+
attr_accessor :brainstem_params_context
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
#
|
|
47
|
+
# Container method that sets up base scoping for the configuration.
|
|
48
|
+
#
|
|
49
|
+
def brainstem_params(&block)
|
|
50
|
+
self.brainstem_params_context = DEFAULT_BRAINSTEM_PARAMS_CONTEXT
|
|
51
|
+
class_eval(&block)
|
|
52
|
+
self.brainstem_params_context = nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
#
|
|
57
|
+
# Specifies that the scope should not be documented. Setting this on
|
|
58
|
+
# the default context will force the controller to be undocumented,
|
|
59
|
+
# whereas setting it within an action context will force that action to
|
|
60
|
+
# be undocumented.
|
|
61
|
+
#
|
|
62
|
+
def nodoc!
|
|
63
|
+
configuration[brainstem_params_context][:nodoc] = true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
#
|
|
68
|
+
# Changes context to a specific action context. Allows specification
|
|
69
|
+
# of per-action configuration.
|
|
70
|
+
#
|
|
71
|
+
# Instead of using this method, it's advised simply to use +actions+
|
|
72
|
+
# with a single method name. While marked as private, since it is
|
|
73
|
+
# usually used within a +class_eval+ block thanks to
|
|
74
|
+
# +brainstem_params+, this has little effect.
|
|
75
|
+
#
|
|
76
|
+
# Originally, this method was named +action+ for parity with the plural
|
|
77
|
+
# version. However, this conflicts in multiple ways with Rails, so it
|
|
78
|
+
# has been renamed.
|
|
79
|
+
#
|
|
80
|
+
# @private
|
|
81
|
+
#
|
|
82
|
+
# @param [Symbol] name the name of the context
|
|
83
|
+
# @param [Proc] block the proc to be evaluated in the context
|
|
84
|
+
#
|
|
85
|
+
def action_context(name, &block)
|
|
86
|
+
new_context = name.to_sym
|
|
87
|
+
old_context = self.brainstem_params_context
|
|
88
|
+
self.brainstem_params_context = new_context
|
|
89
|
+
|
|
90
|
+
self.configuration[new_context] ||= Brainstem::DSL::Configuration.new(
|
|
91
|
+
self.configuration[DEFAULT_BRAINSTEM_PARAMS_CONTEXT]
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
class_eval(&block)
|
|
95
|
+
self.brainstem_params_context = old_context
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private :action_context
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
#
|
|
102
|
+
# Invokes +action+ for each symbol in the argument list. Used to
|
|
103
|
+
# specify shared configuration.
|
|
104
|
+
#
|
|
105
|
+
def actions(*axns, &block)
|
|
106
|
+
axns.flatten.each { |name| action_context name, &block }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
#
|
|
111
|
+
# Allows the bulk specification of +:root+ options. Useful for
|
|
112
|
+
# denoting parameters which are nested under a resource.
|
|
113
|
+
#
|
|
114
|
+
# +root+ may be specified as a string or symbol, which will represent
|
|
115
|
+
# the final root key.
|
|
116
|
+
#
|
|
117
|
+
# However, +root+ can also be specified as a Proc / callable object, in
|
|
118
|
+
# which case it is evaluated at format time, passed the controller
|
|
119
|
+
# constant. By default, if no argument is passed, it will return the
|
|
120
|
+
# controller's +brainstem_model_name+ dynamically.
|
|
121
|
+
#
|
|
122
|
+
# We provide this functionality as a way to handle parameter inheritance
|
|
123
|
+
# in subclasses where the brainstem_model_name may not be the same as
|
|
124
|
+
# the parent class.
|
|
125
|
+
#
|
|
126
|
+
# @params root [Symbol,String,Proc] the brainstem model name or a
|
|
127
|
+
# method accepting the controller constant and returning one
|
|
128
|
+
#
|
|
129
|
+
def model_params(root = Proc.new { |klass| klass.brainstem_model_name }, &block)
|
|
130
|
+
with_options({ root: root.is_a?(Symbol) ? root.to_s : root }, &block)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
#
|
|
135
|
+
# Adds a param to the list of valid params, storing
|
|
136
|
+
# the info sent with it.
|
|
137
|
+
#
|
|
138
|
+
# @param [Symbol] field_name the name of the param
|
|
139
|
+
# @param [Hash] options
|
|
140
|
+
# @option options [String] :info the documentation for the param
|
|
141
|
+
# @option options [String,Symbol] :root if this is a nested param,
|
|
142
|
+
# under which param should it be nested?
|
|
143
|
+
# @option options [Boolean] :nodoc should this param appear in the
|
|
144
|
+
# documentation?
|
|
145
|
+
#
|
|
146
|
+
def valid(field_name, options = { nodoc: false })
|
|
147
|
+
valid_params = configuration[brainstem_params_context][:valid_params]
|
|
148
|
+
valid_params[field_name.to_sym] = options
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
#
|
|
153
|
+
# Adds a transform to the list of transforms. Used to rename incoming
|
|
154
|
+
# params to their internal names for usage.
|
|
155
|
+
#
|
|
156
|
+
# @example
|
|
157
|
+
#
|
|
158
|
+
# brainstem_params do
|
|
159
|
+
# transform :param_from_frontend => :param_for_backend
|
|
160
|
+
# end
|
|
161
|
+
#
|
|
162
|
+
# @param [Hash] transformations An old_param => new_param mapping.
|
|
163
|
+
#
|
|
164
|
+
def transform(transformations)
|
|
165
|
+
transformations.each_pair do |k, v|
|
|
166
|
+
transforms = configuration[brainstem_params_context][:transforms]
|
|
167
|
+
transforms[k.to_sym] = v.to_sym
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
alias_method :transforms, :transform
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
#
|
|
174
|
+
# Specifies which presenter is used for the controller / action.
|
|
175
|
+
# By default, expects presentation on all methods, and falls back to the
|
|
176
|
+
# class derived from +brainstem_model_name+ if a name is not
|
|
177
|
+
# given.
|
|
178
|
+
#
|
|
179
|
+
# Setting the +:nodoc+ option marks this presenter as 'internal use only',
|
|
180
|
+
# and causes formatters to display this as not indicated.
|
|
181
|
+
#
|
|
182
|
+
# @param [Class] target_class the target class of the presenter (i.e
|
|
183
|
+
# the model it presents)
|
|
184
|
+
# @param [Hash] options options to record with the presenter
|
|
185
|
+
# @option [Boolean] options :nodoc whether this presenter should not
|
|
186
|
+
# be output in the documentation.
|
|
187
|
+
#
|
|
188
|
+
#
|
|
189
|
+
def presents(target_class = :default, options = { nodoc: false })
|
|
190
|
+
raise "`presents` must be a class (in #{self.to_s})" \
|
|
191
|
+
unless target_class.is_a?(Class) || target_class == :default || target_class.nil?
|
|
192
|
+
|
|
193
|
+
target_class = brainstem_model_class if target_class == :default
|
|
194
|
+
configuration[brainstem_params_context][:presents] = \
|
|
195
|
+
options.merge(target_class: target_class)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
#
|
|
200
|
+
# Specifies a low-level description of a particular context, usually
|
|
201
|
+
# (but not exclusively) reserved for methods.
|
|
202
|
+
#
|
|
203
|
+
# Setting the +:nodoc+ option marks this description as 'internal use
|
|
204
|
+
# only', and causes formatters not to display a description.
|
|
205
|
+
#
|
|
206
|
+
# @param [String] text The description to set
|
|
207
|
+
# @param [Hash] options options to record with the description
|
|
208
|
+
# @option [Boolean] options :nodoc whether this description should not
|
|
209
|
+
# be output in the documentation.
|
|
210
|
+
#
|
|
211
|
+
def description(text, options = { nodoc: false })
|
|
212
|
+
configuration[brainstem_params_context][:description] = \
|
|
213
|
+
options.merge(info: text)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
#
|
|
218
|
+
# Specifies a title to be used in the description of a class. Can also
|
|
219
|
+
# be used for method section titles.
|
|
220
|
+
#
|
|
221
|
+
# Setting the +:nodoc+ option marks this title as 'internal use only',
|
|
222
|
+
# and causes formatters to fall back to the controller constant or to
|
|
223
|
+
# the action name as appropriate. If you are trying to set the entire
|
|
224
|
+
# controller or action as nondocumentable, instead, use the discrete
|
|
225
|
+
# +.nodoc!+ method in the desired context without a block.
|
|
226
|
+
#
|
|
227
|
+
# @param [String] text The title to set
|
|
228
|
+
# @param [Hash] options options to record with the title
|
|
229
|
+
# @option [Boolean] options :nodoc whether this title should not be
|
|
230
|
+
# output in the documentation.
|
|
231
|
+
#
|
|
232
|
+
def title(text, options = { nodoc: false })
|
|
233
|
+
configuration[brainstem_params_context][:title] = \
|
|
234
|
+
options.merge(info: text)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
#
|
|
240
|
+
# Lists all valid parameters for the current action. Falls back to the
|
|
241
|
+
# valid parameters for the default context.
|
|
242
|
+
#
|
|
243
|
+
# @params [Symbol] requested_context the context which to look up.
|
|
244
|
+
#
|
|
245
|
+
# @return [Hash{String => String, Hash] a hash of pairs of param names and
|
|
246
|
+
# descriptions or sub-hashes.
|
|
247
|
+
#
|
|
248
|
+
def brainstem_valid_params(requested_context = action_name.to_sym, root_param_name = brainstem_model_name)
|
|
249
|
+
contextual_key(requested_context, :valid_params)
|
|
250
|
+
.to_h
|
|
251
|
+
.select do |k, v|
|
|
252
|
+
root = v[:root].respond_to?(:call) ? v[:root].call(self.class) : v[:root]
|
|
253
|
+
root.to_s == root_param_name.to_s
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
alias_method :brainstem_valid_params_for, :brainstem_valid_params
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
#
|
|
260
|
+
# Lists all incoming param keys that will be rewritten to use a different
|
|
261
|
+
# name for internal usage for the current action.
|
|
262
|
+
#
|
|
263
|
+
# Rewrites all params to be symbols for backwards compatibility.
|
|
264
|
+
#
|
|
265
|
+
# @params [Symbol] requested_context the context which to look up.
|
|
266
|
+
#
|
|
267
|
+
# @return [Hash{Symbol => Symbol}] a map of incoming => internal
|
|
268
|
+
# param names.
|
|
269
|
+
#
|
|
270
|
+
def transforms(requested_context = action_name.to_sym)
|
|
271
|
+
tforms = contextual_key(requested_context, :transforms).to_h
|
|
272
|
+
tforms.inject({}) do |memo, (k, v)|
|
|
273
|
+
memo[k.to_sym] = v.to_sym
|
|
274
|
+
memo
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
alias_method :transforms_for, :transforms
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
#
|
|
281
|
+
# Retrieves a specific key in a given context, or if that doesn't exist,
|
|
282
|
+
# falls back to the parent context.
|
|
283
|
+
#
|
|
284
|
+
# @private
|
|
285
|
+
#
|
|
286
|
+
# @params [Symbol] context the context in which to first look for the key
|
|
287
|
+
# @params [Symbol] key the key name to look for
|
|
288
|
+
#
|
|
289
|
+
def contextual_key(context, key)
|
|
290
|
+
if configuration.has_key?(context.to_sym)
|
|
291
|
+
configuration[context.to_sym][key.to_sym]
|
|
292
|
+
else
|
|
293
|
+
configuration[DEFAULT_BRAINSTEM_PARAMS_CONTEXT][key.to_sym]
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
private :contextual_key
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|