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