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.
- 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,100 @@
|
|
1
|
+
require 'brainstem/concerns/optional'
|
2
|
+
|
3
|
+
module Brainstem
|
4
|
+
module ApiDocs
|
5
|
+
module Introspectors
|
6
|
+
class AbstractIntrospector
|
7
|
+
include Brainstem::Concerns::Optional
|
8
|
+
|
9
|
+
def valid_options
|
10
|
+
[ ]
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns a new instance of the introspector with the environment
|
14
|
+
# loaded, ready for introspection.
|
15
|
+
#
|
16
|
+
# @param [Hash] options arguments to pass on to the instance
|
17
|
+
# @return [AbstractIntrospector] the loaded instance
|
18
|
+
def self.with_loaded_environment(options = {})
|
19
|
+
new(options).tap(&:load_environment!)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# Override to return a collection of all controller classes.
|
24
|
+
#
|
25
|
+
# @return [Array<Class>] all controller classes to document
|
26
|
+
def controllers
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# Override to return a collection of all presenter classes.
|
32
|
+
#
|
33
|
+
# @return [Array<Class>] all presenter classes to document
|
34
|
+
def presenters
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# Override to return a collection of hashes with the minimum following
|
40
|
+
# keys:
|
41
|
+
#
|
42
|
+
# +:path+ - the relative path (i.e. the endpoint)
|
43
|
+
# +:controller+ - the managing controller
|
44
|
+
# +:action+ - the managing action
|
45
|
+
# +:http_method+ - an array of the HTTP methods this route is available on.
|
46
|
+
#
|
47
|
+
def routes
|
48
|
+
raise NotImplementedError
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# Provides both a sanity check to ensure that output confirms to
|
53
|
+
# interface and also confirms that there is actually something to
|
54
|
+
# generate docs for.
|
55
|
+
#
|
56
|
+
# @return [Boolean] Whether the Introspector is valid
|
57
|
+
def valid?
|
58
|
+
valid_controllers? && valid_presenters? && valid_routes?
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
#######################################################################
|
63
|
+
private
|
64
|
+
#######################################################################
|
65
|
+
|
66
|
+
# Don't allow instantiation through 'new'. We want to ensure that
|
67
|
+
# instantiation happens through +with_loaded_environment.
|
68
|
+
private_class_method :new
|
69
|
+
|
70
|
+
|
71
|
+
# Loads the host application environment.
|
72
|
+
# @api private
|
73
|
+
def load_environment!
|
74
|
+
raise NotImplementedError
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def valid_controllers?
|
79
|
+
controllers.is_a?(Array) &&
|
80
|
+
controllers.count > 0 &&
|
81
|
+
controllers.all? {|c| c.class == Class }
|
82
|
+
end
|
83
|
+
|
84
|
+
def valid_presenters?
|
85
|
+
presenters.is_a?(Array) &&
|
86
|
+
presenters.all? {|p| p.class == Class }
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid_routes?
|
90
|
+
routes.is_a?(Array) &&
|
91
|
+
routes.count > 0 &&
|
92
|
+
routes.all? do |r|
|
93
|
+
r.is_a?(Hash) &&
|
94
|
+
([:path, :controller, :action, :http_methods] - r.keys).empty?
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
require 'brainstem/api_docs/introspectors/abstract_introspector'
|
2
|
+
require 'brainstem/api_docs/exceptions'
|
3
|
+
|
4
|
+
# For 'constantize'
|
5
|
+
require 'active_support/inflector/methods'
|
6
|
+
|
7
|
+
module Brainstem
|
8
|
+
module ApiDocs
|
9
|
+
module Introspectors
|
10
|
+
class RailsIntrospector < AbstractIntrospector
|
11
|
+
|
12
|
+
#
|
13
|
+
# Loads ./config/environment.rb (by default) and eager loads all
|
14
|
+
# classes (otherwise +#descendants+ returns an empty set).
|
15
|
+
#
|
16
|
+
def load_environment!
|
17
|
+
load rails_environment_file unless env_already_loaded?
|
18
|
+
::Rails.application.eager_load!
|
19
|
+
|
20
|
+
validate!
|
21
|
+
rescue LoadError => e
|
22
|
+
raise IncorrectIntrospectorForAppException,
|
23
|
+
"Hosting app does not appear to be a Rails app." +
|
24
|
+
"You may have to manually specify an Introspector (#{e.message})."
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
#
|
29
|
+
# Returns a list of presenters that descend from the base presenter
|
30
|
+
# class.
|
31
|
+
#
|
32
|
+
# @return [Array<Class>] an array of descendant classes
|
33
|
+
#
|
34
|
+
def presenters
|
35
|
+
base_presenter_class.constantize.descendants
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
#
|
40
|
+
# Returns a list of controllers that descend from the base controller
|
41
|
+
# class.
|
42
|
+
#
|
43
|
+
# @return [Array<Class>] an array of descendant classes
|
44
|
+
#
|
45
|
+
def controllers
|
46
|
+
base_controller_class.constantize.descendants
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
#
|
51
|
+
# Returns an array of hashes describing the endpoints of the
|
52
|
+
# application. See +routes_method+ for the keys of those hashes.
|
53
|
+
#
|
54
|
+
# @see #routes_method
|
55
|
+
#
|
56
|
+
# @return [Array<Hash>] each route defined on the hosting app
|
57
|
+
def routes
|
58
|
+
routes_method.call
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
#######################################################################
|
63
|
+
private
|
64
|
+
#######################################################################
|
65
|
+
|
66
|
+
|
67
|
+
def valid_options
|
68
|
+
super | [
|
69
|
+
:routes_method,
|
70
|
+
:rails_environment_file,
|
71
|
+
:base_presenter_class,
|
72
|
+
:base_controller_class,
|
73
|
+
]
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
#
|
78
|
+
# Used to short-circuit loading if Rails is already loaded, which
|
79
|
+
# reduces start-up time substantially.
|
80
|
+
#
|
81
|
+
# @return [Boolean] whether Rails has already been loaded.
|
82
|
+
def env_already_loaded?
|
83
|
+
defined? Rails
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
# Returns the path of the Rails +config/environment.rb+ file - by
|
88
|
+
# default, +#{Dir.pwd}/config/environment.rb+.
|
89
|
+
#
|
90
|
+
# @return [String] the absolute path of the config/environment.rb file.
|
91
|
+
#
|
92
|
+
def rails_environment_file
|
93
|
+
@rails_environment_file ||= File.expand_path(
|
94
|
+
File.join(Dir.pwd, 'config', 'environment.rb')
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
#
|
100
|
+
# Allows a custom location to be set for the environment file if - for
|
101
|
+
# example - the command were to be called from a cron task that cannot
|
102
|
+
# change directory.
|
103
|
+
#
|
104
|
+
attr_writer :rails_environment_file
|
105
|
+
|
106
|
+
|
107
|
+
#
|
108
|
+
# Returns the name of the base presenter class.
|
109
|
+
#
|
110
|
+
# Because the initializer that contains configuration data is unlikely
|
111
|
+
# to have been loaded, this may also return a Proc, which will be called
|
112
|
+
# after the environment is loaded.
|
113
|
+
#
|
114
|
+
# @return [String,Proc] the base presenter class or a proc that returns
|
115
|
+
# the same
|
116
|
+
#
|
117
|
+
def base_presenter_class
|
118
|
+
proc_or_string = (@base_presenter_class ||= "::Brainstem::Presenter")
|
119
|
+
proc_or_string.respond_to?(:call) ? proc_or_string.call : proc_or_string
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
#
|
124
|
+
# Allows for the specification for an alternate base presenter class
|
125
|
+
# if - for example - only documentation of children of +MyBasePresenter+
|
126
|
+
# is desired.
|
127
|
+
#
|
128
|
+
# This argument accepts a string because most classes will not be
|
129
|
+
# defined at the time of passing, and will only be defined after
|
130
|
+
# environment load.
|
131
|
+
#
|
132
|
+
# Because the initializer that contains configuration data is unlikely
|
133
|
+
# to have been loaded, this may also return a Proc, which will be called
|
134
|
+
# after the environment is loaded.
|
135
|
+
#
|
136
|
+
# @param [String,Proc] base_presenter_class the class name to use as the
|
137
|
+
# base presenter, or a proc which returns the same.
|
138
|
+
#
|
139
|
+
attr_writer :base_presenter_class
|
140
|
+
|
141
|
+
|
142
|
+
#
|
143
|
+
# Returns the name of the base controller class.
|
144
|
+
#
|
145
|
+
# Because the initializer that contains configuration data is unlikely
|
146
|
+
# to have been loaded, this may also return a Proc, which will be called
|
147
|
+
# after the environment is loaded.
|
148
|
+
#
|
149
|
+
# @return [String,Proc] the base controller class or a proc that returns
|
150
|
+
# the same
|
151
|
+
#
|
152
|
+
def base_controller_class
|
153
|
+
proc_or_string = (@base_controller_class ||= "::ApplicationController")
|
154
|
+
proc_or_string.respond_to?(:call) ? proc_or_string.call : proc_or_string
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
#
|
159
|
+
# Allows for the specification for an alternate base controller class
|
160
|
+
# if - for example - only documentation of children of ApiController
|
161
|
+
# is desired. Best used through passing an argument to
|
162
|
+
# +with_loaded_environment+.
|
163
|
+
#
|
164
|
+
# This argument accepts a string because most classes will not be
|
165
|
+
# defined at the time of passing, and will only be defined after
|
166
|
+
# environment load.
|
167
|
+
#
|
168
|
+
# Because the initializer that contains configuration data is unlikely
|
169
|
+
# to have been loaded, this may also return a Proc, which will be called
|
170
|
+
# after the environment is loaded.
|
171
|
+
#
|
172
|
+
# @param [String,Proc] klass the class to use as the base controller, or
|
173
|
+
# a a method which returns the same.
|
174
|
+
#
|
175
|
+
attr_writer :base_controller_class
|
176
|
+
|
177
|
+
|
178
|
+
#
|
179
|
+
# Returns the proc that is called to format and retrieve routes.
|
180
|
+
# The proc's return must be an array of hashes that contains the
|
181
|
+
# following keys:
|
182
|
+
#
|
183
|
+
# +:path+ - the relative path
|
184
|
+
# +:controller+ - the managing controller as a constant
|
185
|
+
# +:controller_name+ - the internal underscored name of the controller
|
186
|
+
# +:action+ - the managing action
|
187
|
+
# +:http_method+ - an array of the HTTP methods this route is available on.
|
188
|
+
#
|
189
|
+
def routes_method
|
190
|
+
@routes_method ||= Proc.new do
|
191
|
+
Rails.application.routes.routes.map do |route|
|
192
|
+
next unless route.defaults.has_key?(:controller) &&
|
193
|
+
controller_const = "#{route.defaults[:controller]}_controller"
|
194
|
+
.classify
|
195
|
+
.constantize rescue nil
|
196
|
+
|
197
|
+
{
|
198
|
+
alias: route.name,
|
199
|
+
path: route.path.spec.to_s,
|
200
|
+
controller_name: route.defaults[:controller],
|
201
|
+
controller: controller_const,
|
202
|
+
action: route.defaults[:action],
|
203
|
+
http_methods: route.constraints
|
204
|
+
.fetch(:request_method, nil)
|
205
|
+
.inspect
|
206
|
+
.gsub(/[\/\$\^]/, '')
|
207
|
+
.split("|")
|
208
|
+
}
|
209
|
+
end.compact
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
#
|
215
|
+
# Allows setting the routes method used to retrieve the routes if - for
|
216
|
+
# example - your application needs to retrieve additional data or if it
|
217
|
+
# uses an explicit routing table to define documentable endpoints.
|
218
|
+
#
|
219
|
+
attr_writer :routes_method
|
220
|
+
|
221
|
+
|
222
|
+
#
|
223
|
+
# Throws an error if the introspector did not produce valid results.
|
224
|
+
#
|
225
|
+
def validate!
|
226
|
+
raise InvalidIntrospectorError, "Introspector is not valid." \
|
227
|
+
unless valid?
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
require 'brainstem/api_docs'
|
2
|
+
require 'brainstem/concerns/optional'
|
3
|
+
require 'brainstem/concerns/formattable'
|
4
|
+
require 'forwardable'
|
5
|
+
require 'active_support/inflector'
|
6
|
+
|
7
|
+
#
|
8
|
+
# Wrapper for common presenter information lookups.
|
9
|
+
#
|
10
|
+
module Brainstem
|
11
|
+
module ApiDocs
|
12
|
+
class Presenter
|
13
|
+
extend Forwardable
|
14
|
+
include Concerns::Optional
|
15
|
+
include Concerns::Formattable
|
16
|
+
|
17
|
+
|
18
|
+
def valid_options
|
19
|
+
super | [
|
20
|
+
:const,
|
21
|
+
:target_class,
|
22
|
+
:filename_pattern,
|
23
|
+
:filename_link_pattern,
|
24
|
+
:document_empty_associations,
|
25
|
+
:document_empty_filters
|
26
|
+
]
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_accessor :const,
|
30
|
+
:target_class,
|
31
|
+
:document_empty_associations,
|
32
|
+
:document_empty_filters
|
33
|
+
|
34
|
+
attr_writer :filename_pattern,
|
35
|
+
:filename_link_pattern
|
36
|
+
|
37
|
+
alias_method :document_empty_associations?, :document_empty_associations
|
38
|
+
alias_method :document_empty_filters?, :document_empty_filters
|
39
|
+
|
40
|
+
|
41
|
+
def initialize(atlas, options = {})
|
42
|
+
self.atlas = atlas
|
43
|
+
self.document_empty_associations = Brainstem::ApiDocs.document_empty_presenter_associations
|
44
|
+
self.document_empty_filters = Brainstem::ApiDocs.document_empty_presenter_filters
|
45
|
+
|
46
|
+
super options
|
47
|
+
yield self if block_given?
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def suggested_filename(format)
|
52
|
+
filename_pattern
|
53
|
+
.gsub('{{name}}', target_class.to_s.underscore)
|
54
|
+
.gsub('{{extension}}', extension)
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def suggested_filename_link(format)
|
59
|
+
filename_link_pattern
|
60
|
+
.gsub('{{name}}', target_class.to_s.underscore)
|
61
|
+
.gsub('{{extension}}', extension)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
attr_accessor :atlas
|
66
|
+
|
67
|
+
|
68
|
+
def extension
|
69
|
+
@extension ||= Brainstem::ApiDocs.output_extension
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def filename_pattern
|
74
|
+
@filename_pattern ||= Brainstem::ApiDocs.presenter_filename_pattern
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def filename_link_pattern
|
79
|
+
@filename_link_pattern ||= Brainstem::ApiDocs.presenter_filename_link_pattern
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
delegate :configuration => :const
|
84
|
+
delegate :find_by_class => :atlas
|
85
|
+
|
86
|
+
|
87
|
+
def nodoc?
|
88
|
+
configuration[:nodoc]
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def title
|
93
|
+
contextual_documentation(:title) || const.to_s.demodulize
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def brainstem_keys
|
98
|
+
const.possible_brainstem_keys.to_a.sort
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def description
|
103
|
+
contextual_documentation(:description) || ""
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
def valid_fields(fields = configuration[:fields])
|
108
|
+
fields.to_h.reject do |k, v|
|
109
|
+
if nested_field?(v)
|
110
|
+
valid_fields_in(v).none?
|
111
|
+
else
|
112
|
+
invalid_field?(v)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
alias_method :valid_fields_in, :valid_fields
|
117
|
+
|
118
|
+
|
119
|
+
def invalid_field?(field)
|
120
|
+
field.options[:nodoc]
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
def nested_field?(field)
|
125
|
+
!field.respond_to?(:options)
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
def valid_filters
|
130
|
+
configuration[:filters]
|
131
|
+
.to_h
|
132
|
+
.keep_if(&method(:documentable_filter?))
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def documentable_filter?(_, filter)
|
137
|
+
!filter[:nodoc] &&
|
138
|
+
(
|
139
|
+
document_empty_filters? || # document empty filters or
|
140
|
+
!(filter[:info] || "").empty? # has info string
|
141
|
+
)
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
def valid_sort_orders
|
146
|
+
configuration[:sort_orders].to_h.reject {|k, v| v[:nodoc] }
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
def valid_associations
|
151
|
+
configuration[:associations]
|
152
|
+
.to_h
|
153
|
+
.keep_if(&method(:documentable_association?))
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
def link_for_association(association)
|
159
|
+
if (associated_presenter = find_by_class(association.target_class)) &&
|
160
|
+
!associated_presenter.nodoc?
|
161
|
+
relative_path_to_presenter(associated_presenter, :markdown)
|
162
|
+
else
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
#
|
169
|
+
# Returns whether this association should be documented based on nodoc
|
170
|
+
# and empty description.
|
171
|
+
#
|
172
|
+
# @return [Bool] document this association?
|
173
|
+
#
|
174
|
+
def documentable_association?(_, association)
|
175
|
+
!association.options[:nodoc] && # not marked nodoc and
|
176
|
+
(
|
177
|
+
document_empty_associations? || # document empty associations or
|
178
|
+
!(association.description.nil? || association.description.empty?) # has description
|
179
|
+
)
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
def conditionals
|
184
|
+
configuration[:conditionals]
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def default_sort_order
|
189
|
+
configuration[:default_sort_order] || ""
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
def default_sort_field
|
194
|
+
@default_sort_field ||= (default_sort_order.split(":")[0] || nil)
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
def default_sort_direction
|
199
|
+
@default_sort_direction ||= (default_sort_order.split(":")[1] || nil)
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
#
|
204
|
+
# Returns a key if it exists and is documentable.
|
205
|
+
#
|
206
|
+
def contextual_documentation(key)
|
207
|
+
configuration.has_key?(key) &&
|
208
|
+
!configuration[key][:nodoc] &&
|
209
|
+
configuration[key][:info]
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
#
|
214
|
+
# Returns the relative path between this presenter and another given
|
215
|
+
# presenter.
|
216
|
+
#
|
217
|
+
def relative_path_to_presenter(presenter, format)
|
218
|
+
my_path = Pathname.new(File.dirname(suggested_filename_link(format)))
|
219
|
+
presenter_path = Pathname.new(presenter.suggested_filename_link(format))
|
220
|
+
|
221
|
+
presenter_path.relative_path_from(my_path).to_s
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|