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