apipie-dsl 2.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +485 -0
- data/app/controllers/apipie_dsl/apipie_dsls_controller.rb +190 -0
- data/app/helpers/apipie_dsl_helper.rb +110 -0
- data/app/public/apipie_dsl/javascripts/apipie_dsl.js +6 -0
- data/app/public/apipie_dsl/javascripts/bundled/bootstrap-collapse.js +138 -0
- data/app/public/apipie_dsl/javascripts/bundled/bootstrap.js +1726 -0
- data/app/public/apipie_dsl/javascripts/bundled/jquery.js +5 -0
- data/app/public/apipie_dsl/javascripts/bundled/prettify.js +28 -0
- data/app/public/apipie_dsl/stylesheets/application.css +7 -0
- data/app/public/apipie_dsl/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
- data/app/public/apipie_dsl/stylesheets/bundled/bootstrap.min.css +689 -0
- data/app/public/apipie_dsl/stylesheets/bundled/prettify.css +30 -0
- data/app/views/apipie_dsl/apipie_dsls/_index_class_meth.erb +11 -0
- data/app/views/apipie_dsl/apipie_dsls/_index_class_prop.erb +13 -0
- data/app/views/apipie_dsl/apipie_dsls/_languages.erb +6 -0
- data/app/views/apipie_dsl/apipie_dsls/_metadata.erb +1 -0
- data/app/views/apipie_dsl/apipie_dsls/_method.erb +34 -0
- data/app/views/apipie_dsl/apipie_dsls/_method_detail.erb +58 -0
- data/app/views/apipie_dsl/apipie_dsls/_params.html.erb +47 -0
- data/app/views/apipie_dsl/apipie_dsls/_params_plain.html.erb +19 -0
- data/app/views/apipie_dsl/apipie_dsls/_property.erb +24 -0
- data/app/views/apipie_dsl/apipie_dsls/_property_detail.erb +35 -0
- data/app/views/apipie_dsl/apipie_dsls/_raises.html.erb +23 -0
- data/app/views/apipie_dsl/apipie_dsls/_returns.html.erb +53 -0
- data/app/views/apipie_dsl/apipie_dsls/apipie_dsl_404.html.erb +17 -0
- data/app/views/apipie_dsl/apipie_dsls/class.html.erb +75 -0
- data/app/views/apipie_dsl/apipie_dsls/custom_help.html.erb +10 -0
- data/app/views/apipie_dsl/apipie_dsls/getting_started.html.erb +4 -0
- data/app/views/apipie_dsl/apipie_dsls/index.html.erb +72 -0
- data/app/views/apipie_dsl/apipie_dsls/method.html.erb +52 -0
- data/app/views/apipie_dsl/apipie_dsls/plain.html.erb +116 -0
- data/app/views/apipie_dsl/apipie_dsls/static.html.erb +158 -0
- data/app/views/layouts/apipie_dsl/apipie_dsl.html.erb +26 -0
- data/lib/apipie-dsl.rb +3 -0
- data/lib/apipie_dsl.rb +28 -0
- data/lib/apipie_dsl/Rakefile +6 -0
- data/lib/apipie_dsl/apipie_dsl_module.rb +51 -0
- data/lib/apipie_dsl/application.rb +321 -0
- data/lib/apipie_dsl/class_description.rb +107 -0
- data/lib/apipie_dsl/configuration.rb +87 -0
- data/lib/apipie_dsl/dsl.rb +596 -0
- data/lib/apipie_dsl/errors.rb +68 -0
- data/lib/apipie_dsl/exception_description.rb +39 -0
- data/lib/apipie_dsl/markup.rb +41 -0
- data/lib/apipie_dsl/method_description.rb +112 -0
- data/lib/apipie_dsl/parameter_description.rb +152 -0
- data/lib/apipie_dsl/railtie.rb +15 -0
- data/lib/apipie_dsl/return_description.rb +71 -0
- data/lib/apipie_dsl/routing.rb +17 -0
- data/lib/apipie_dsl/see_description.rb +35 -0
- data/lib/apipie_dsl/static_dispatcher.rb +70 -0
- data/lib/apipie_dsl/tag_list_description.rb +11 -0
- data/lib/apipie_dsl/tasks/apipie_dsl/cache.rake +43 -0
- data/lib/apipie_dsl/tasks/apipie_dsl/static.rake +43 -0
- data/lib/apipie_dsl/tasks/apipie_dsl/static_json.rake +29 -0
- data/lib/apipie_dsl/tasks_utils.rb +137 -0
- data/lib/apipie_dsl/utils.rb +83 -0
- data/lib/apipie_dsl/validator.rb +479 -0
- data/lib/apipie_dsl/version.rb +5 -0
- data/lib/generators/apipie_dsl/install/install_generator.rb +21 -0
- data/lib/generators/apipie_dsl/install/templates/initializer.rb.erb +9 -0
- data/lib/generators/apipie_dsl/views_generator.rb +14 -0
- data/test/test_helper.rb +6 -0
- metadata +220 -0
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ApipieDSL
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :app_name, :copyright, :markup, :doc_base_url, :layout,
|
6
|
+
:default_version, :debug, :version_in_url, :validate,
|
7
|
+
:doc_path, :languages, :link_extension, :translate, :locale,
|
8
|
+
:default_locale, :class_full_names, :autoload_methods,
|
9
|
+
:dsl_classes_matcher, :sections, :authenticate, :authorize,
|
10
|
+
:use_cache, :app_info, :help_layout
|
11
|
+
attr_writer :validate_value, :ignored, :reload_dsl, :default_section,
|
12
|
+
:dsl_classes_matchers, :cache_dir
|
13
|
+
|
14
|
+
alias_method :validate?, :validate
|
15
|
+
alias_method :class_full_names?, :class_full_names
|
16
|
+
alias_method :autoload_methods?, :autoload_methods
|
17
|
+
alias_method :use_cache?, :use_cache
|
18
|
+
|
19
|
+
def cache_dir
|
20
|
+
@cache_dir ||= File.join(Rails.root, 'public', 'apipie-dsl-cache')
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate_value
|
24
|
+
(validate? && @validate_value)
|
25
|
+
end
|
26
|
+
alias_method :validate_value?, :validate_value
|
27
|
+
|
28
|
+
# array of class names (strings) (might include methods as well)
|
29
|
+
# to be ignored when generationg the documentation
|
30
|
+
# e.g. %w[DSL::MyClass DSL::IO#puts]
|
31
|
+
def ignored
|
32
|
+
@ignored ||= []
|
33
|
+
@ignored.map(&:to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
def app_info=(description)
|
37
|
+
version = ApipieDSL.configuration.default_version
|
38
|
+
@app_info[version] = description
|
39
|
+
end
|
40
|
+
|
41
|
+
def dsl_classes_matchers
|
42
|
+
unless @dsl_classes_matcher.empty?
|
43
|
+
@dsl_classes_matchers << @dsl_classes_matcher
|
44
|
+
end
|
45
|
+
@dsl_classes_matchers = @dsl_classes_matchers.uniq
|
46
|
+
end
|
47
|
+
|
48
|
+
def reload_dsl?
|
49
|
+
@reload_dsl = if defined? Rails
|
50
|
+
Rails.env.development?
|
51
|
+
else
|
52
|
+
@reload_dsl
|
53
|
+
end
|
54
|
+
@reload_dsl && !dsl_classes_matchers.empty?
|
55
|
+
end
|
56
|
+
|
57
|
+
def default_section
|
58
|
+
@default_section || @sections.first
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize
|
62
|
+
@markup = ApipieDSL::Markup::RDoc.new
|
63
|
+
@app_name = 'Another DOC'
|
64
|
+
@app_info = {}
|
65
|
+
@copyright = nil
|
66
|
+
@validate = :implicitly
|
67
|
+
@validate_value = true
|
68
|
+
@doc_base_url = '/apipie-dsl'
|
69
|
+
@layout = 'apipie_dsl/apipie_dsl'
|
70
|
+
@default_version = '1.0'
|
71
|
+
@debug = false
|
72
|
+
@version_in_url = true
|
73
|
+
@doc_path = 'doc'
|
74
|
+
@link_extension = '.html'
|
75
|
+
@languages = []
|
76
|
+
@default_locale = 'en'
|
77
|
+
@locale = lambda { |_locale| @default_locale }
|
78
|
+
@translate = lambda { |str, _locale| str }
|
79
|
+
@class_full_names = true
|
80
|
+
@autoload_methods = false
|
81
|
+
@dsl_classes_matcher = ''
|
82
|
+
@dsl_classes_matchers = []
|
83
|
+
@sections = ['all']
|
84
|
+
@default_section = nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,596 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ApipieDSL
|
4
|
+
module Base
|
5
|
+
def apipie_eval_dsl(*args, &block)
|
6
|
+
raise ArgumentError, 'Block expected' unless block_given?
|
7
|
+
|
8
|
+
instance_exec(*args, &block)
|
9
|
+
dsl_data
|
10
|
+
ensure
|
11
|
+
dsl_data_clear
|
12
|
+
end
|
13
|
+
|
14
|
+
def dsl_data
|
15
|
+
@dsl_data ||= dsl_data_init
|
16
|
+
end
|
17
|
+
|
18
|
+
def dsl_data_clear
|
19
|
+
@dsl_data = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def dsl_data_init
|
25
|
+
@dsl_data =
|
26
|
+
{
|
27
|
+
name: nil,
|
28
|
+
short_description: nil,
|
29
|
+
description: nil,
|
30
|
+
dsl_versions: [],
|
31
|
+
deprecated: false,
|
32
|
+
meta: nil,
|
33
|
+
params: [],
|
34
|
+
properties: [],
|
35
|
+
raises: [],
|
36
|
+
returns: nil,
|
37
|
+
see: [],
|
38
|
+
show: true,
|
39
|
+
examples: [],
|
40
|
+
sections: ['all']
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module Common
|
46
|
+
def dsl_versions(*versions)
|
47
|
+
dsl_data[:dsl_versions].concat(versions)
|
48
|
+
end
|
49
|
+
alias_method :dsl_version, :dsl_versions
|
50
|
+
|
51
|
+
def desc(description)
|
52
|
+
dsl_data[:description] = description
|
53
|
+
end
|
54
|
+
alias_method :description, :desc
|
55
|
+
alias_method :full_description, :desc
|
56
|
+
|
57
|
+
def short(short)
|
58
|
+
dsl_data[:short_description] = short
|
59
|
+
end
|
60
|
+
alias_method :short_description, :short
|
61
|
+
|
62
|
+
# Describe additional metadata
|
63
|
+
#
|
64
|
+
# meta :author => { :name => 'John', :surname => 'Doe' }
|
65
|
+
def meta(meta)
|
66
|
+
dsl_data[:meta] = meta
|
67
|
+
end
|
68
|
+
|
69
|
+
# Add tags to classes and methods group operations together.
|
70
|
+
def tags(*args)
|
71
|
+
tags = args.length == 1 ? args.first : args
|
72
|
+
dsl_data[:tag_list] += tags
|
73
|
+
end
|
74
|
+
|
75
|
+
def deprecated(value)
|
76
|
+
dsl_data[:deprecated] = value
|
77
|
+
end
|
78
|
+
|
79
|
+
# Determine if the method (class) should be included
|
80
|
+
# in the documentation
|
81
|
+
def show(show)
|
82
|
+
dsl_data[:show] = show
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
module Parameter
|
87
|
+
SUPPORTED_TYPES = %i[required optional keyword block].freeze
|
88
|
+
# Describe method's parameter
|
89
|
+
#
|
90
|
+
# Example:
|
91
|
+
# param :greeting, String, :desc => "arbitrary text", :type => :required
|
92
|
+
# def hello_world(greeting)
|
93
|
+
# puts greeting
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
def param(name, validator, desc_or_options = nil, options = {}, &block)
|
97
|
+
dsl_data[:params] << [name,
|
98
|
+
validator,
|
99
|
+
desc_or_options,
|
100
|
+
options.merge(param_group: @current_param_group),
|
101
|
+
block]
|
102
|
+
end
|
103
|
+
|
104
|
+
def required(name, validator, desc_or_options = nil, options = {}, &block)
|
105
|
+
options[:type] = :required
|
106
|
+
param(name, validator, desc_or_options, options, &block)
|
107
|
+
end
|
108
|
+
|
109
|
+
def optional(name, validator, desc_or_options = nil, options = {}, &block)
|
110
|
+
options[:type] = :optional
|
111
|
+
param(name, validator, desc_or_options, options, &block)
|
112
|
+
end
|
113
|
+
|
114
|
+
def keyword(name, validator, desc_or_options = nil, options = {}, &block)
|
115
|
+
options[:type] = :keyword
|
116
|
+
param(name, validator, desc_or_options, options, &block)
|
117
|
+
end
|
118
|
+
|
119
|
+
def block(desc_or_options = nil, options = {}, &block)
|
120
|
+
options[:type] = :block
|
121
|
+
name = options[:name] || :block
|
122
|
+
param(name, Proc, desc_or_options, options)
|
123
|
+
end
|
124
|
+
|
125
|
+
def list(name, desc_or_options = nil, options = {})
|
126
|
+
options[:type] = :optional
|
127
|
+
options[:default] ||= 'empty list'
|
128
|
+
param(name, :rest, desc_or_options, options)
|
129
|
+
end
|
130
|
+
alias_method :splat, :list
|
131
|
+
alias_method :rest, :list
|
132
|
+
|
133
|
+
def define_param_group(name, &block)
|
134
|
+
ApipieDSL.define_param_group(class_scope, name, &block)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Reuses param group for this method. The definition is looked up
|
138
|
+
# in scope of this class. If the group was defined in
|
139
|
+
# different class, the second param can be used to specify it.
|
140
|
+
def param_group(name, scope_or_options = nil, options = {})
|
141
|
+
if scope_or_options.is_a?(Hash)
|
142
|
+
options.merge!(scope_or_options)
|
143
|
+
scope = options[:scope]
|
144
|
+
else
|
145
|
+
scope = scope_or_options
|
146
|
+
end
|
147
|
+
scope ||= default_param_group_scope
|
148
|
+
|
149
|
+
@current_param_group = {
|
150
|
+
scope: scope,
|
151
|
+
name: name,
|
152
|
+
options: options
|
153
|
+
}
|
154
|
+
instance_exec(&ApipieDSL.get_param_group(scope, name))
|
155
|
+
ensure
|
156
|
+
@current_param_group = nil
|
157
|
+
end
|
158
|
+
|
159
|
+
# Where the group definition should be looked up when no scope
|
160
|
+
# given. This is expected to return a class.
|
161
|
+
def default_param_group_scope
|
162
|
+
class_scope
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
module Method
|
167
|
+
include ApipieDSL::Parameter
|
168
|
+
|
169
|
+
def method(name, desc = nil, _options = {})
|
170
|
+
dsl_data[:name] = name
|
171
|
+
dsl_data[:short_description] = desc
|
172
|
+
end
|
173
|
+
|
174
|
+
def aliases(*names)
|
175
|
+
dsl_data[:aliases] = names
|
176
|
+
end
|
177
|
+
|
178
|
+
# Describe possible errors
|
179
|
+
#
|
180
|
+
# Example:
|
181
|
+
# raises :desc => "wrong argument", :error => ArgumentError, :meta => [:some, :more, :data]
|
182
|
+
# raises ArgumentError, "wrong argument"
|
183
|
+
# def print_string(string)
|
184
|
+
# raise ArgumentError unless string.is_a?(String)
|
185
|
+
# puts string
|
186
|
+
# end
|
187
|
+
#
|
188
|
+
def raises(error_or_options, desc = nil, options = {})
|
189
|
+
dsl_data[:raises] << [error_or_options, desc, options]
|
190
|
+
end
|
191
|
+
|
192
|
+
def returns(retobj_or_options, desc_or_options = nil, options = {}, &block)
|
193
|
+
raise MultipleReturnsError unless dsl_data[:returns].nil?
|
194
|
+
|
195
|
+
if desc_or_options.is_a?(Hash)
|
196
|
+
options.merge!(desc_or_options)
|
197
|
+
elsif !desc_or_options.nil?
|
198
|
+
options[:desc] = desc_or_options
|
199
|
+
end
|
200
|
+
|
201
|
+
if retobj_or_options.is_a?(Hash)
|
202
|
+
options.merge!(retobj_or_options)
|
203
|
+
elsif retobj_or_options.is_a?(Symbol)
|
204
|
+
options[:param_group] = retobj_or_options
|
205
|
+
else
|
206
|
+
options[:object_of] ||= retobj_or_options
|
207
|
+
end
|
208
|
+
|
209
|
+
options[:scope] ||= default_param_group_scope
|
210
|
+
|
211
|
+
raise ArgumentError, 'Block can be specified for Hash return type only' if block && (options[:object_of] != Hash)
|
212
|
+
|
213
|
+
data = [options, block]
|
214
|
+
dsl_data[:returns] = data unless options[:property]
|
215
|
+
data
|
216
|
+
end
|
217
|
+
|
218
|
+
# Reference other similar method
|
219
|
+
#
|
220
|
+
# method :print
|
221
|
+
# see "MyIO#puts"
|
222
|
+
# def print; end
|
223
|
+
def see(method, options = {})
|
224
|
+
args = [method, options]
|
225
|
+
dsl_data[:see] << args
|
226
|
+
end
|
227
|
+
|
228
|
+
def example(example, desc_or_options = nil, options = {})
|
229
|
+
if desc_or_options.is_a?(Hash)
|
230
|
+
options.merge!(desc_or_options)
|
231
|
+
elsif !desc_or_options.nil?
|
232
|
+
options[:desc] = desc_or_options
|
233
|
+
end
|
234
|
+
dsl_data[:examples] << { example: example, desc: options[:desc], for: options[:for] }
|
235
|
+
end
|
236
|
+
|
237
|
+
def example_for(method_name, example, desc_or_options = nil, options = {})
|
238
|
+
if desc_or_options.is_a?(Hash)
|
239
|
+
options.merge!(desc_or_options)
|
240
|
+
elsif !desc_or_options.nil?
|
241
|
+
options[:desc] = desc_or_options
|
242
|
+
end
|
243
|
+
dsl_data[:examples] << { example: example, desc: options[:desc], for: method_name }
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
module Klass
|
248
|
+
def app_info(app_info)
|
249
|
+
dsl_data[:app_info] = app_info
|
250
|
+
end
|
251
|
+
|
252
|
+
def class_description(&block)
|
253
|
+
dsl_data = apipie_eval_dsl(&block)
|
254
|
+
dsl_data[:dsl_versions] = ApipieDSL.class_versions(class_scope) if dsl_data[:dsl_versions].empty?
|
255
|
+
versions = dsl_data[:dsl_versions]
|
256
|
+
versions.map do |version|
|
257
|
+
ApipieDSL.define_class_description(class_scope, version, dsl_data)
|
258
|
+
end
|
259
|
+
ApipieDSL.set_class_versions(class_scope, versions)
|
260
|
+
end
|
261
|
+
|
262
|
+
def name(new_name)
|
263
|
+
dsl_data[:class_name] = new_name
|
264
|
+
end
|
265
|
+
alias_method :label, :name
|
266
|
+
|
267
|
+
def refs(*class_names)
|
268
|
+
dsl_data[:refs] = class_names
|
269
|
+
end
|
270
|
+
alias_method :referenced_on, :refs
|
271
|
+
|
272
|
+
def sections(sec_or_options, options = {})
|
273
|
+
if sec_or_options.is_a?(Hash)
|
274
|
+
options.merge!(sec_or_options)
|
275
|
+
elsif !sec_or_options.nil?
|
276
|
+
options[:only] = sec_or_options
|
277
|
+
end
|
278
|
+
only = [options[:only]].flatten || ApipieDSL.configuration.sections
|
279
|
+
except = if options[:except]
|
280
|
+
[options[:except]].flatten
|
281
|
+
else
|
282
|
+
[]
|
283
|
+
end
|
284
|
+
dsl_data[:sections] = only - except
|
285
|
+
end
|
286
|
+
|
287
|
+
def property(name, retobj_or_options, desc_or_options = nil, options = {}, &block)
|
288
|
+
if desc_or_options.is_a?(Hash)
|
289
|
+
options.merge!(desc_or_options)
|
290
|
+
elsif !desc_or_options.nil?
|
291
|
+
options[:desc] = desc_or_options
|
292
|
+
end
|
293
|
+
|
294
|
+
options[:property] = true
|
295
|
+
returns = returns(retobj_or_options, desc_or_options, options, &block)
|
296
|
+
prop_dsl_data = {
|
297
|
+
short_description: options[:desc],
|
298
|
+
returns: returns
|
299
|
+
}
|
300
|
+
dsl_data[:properties] << [name, prop_dsl_data]
|
301
|
+
end
|
302
|
+
alias_method :prop, :property
|
303
|
+
|
304
|
+
def define_prop_group(name, &block)
|
305
|
+
ApipieDSL.define_prop_group(class_scope, name, &block)
|
306
|
+
end
|
307
|
+
|
308
|
+
# Reuses param group for this method. The definition is looked up
|
309
|
+
# in scope of this class. If the group was defined in
|
310
|
+
# different class, the second param can be used to specify it.
|
311
|
+
def prop_group(name, scope_or_options = nil, options = {})
|
312
|
+
if scope_or_options.is_a?(Hash)
|
313
|
+
options.merge!(scope_or_options)
|
314
|
+
scope = options[:scope]
|
315
|
+
else
|
316
|
+
scope = scope_or_options
|
317
|
+
end
|
318
|
+
scope ||= default_prop_group_scope
|
319
|
+
|
320
|
+
@current_prop_group = {
|
321
|
+
scope: scope,
|
322
|
+
name: name,
|
323
|
+
options: options
|
324
|
+
}
|
325
|
+
@meta = (options[:meta] || {}).tap { |meta| meta[:class_scope] = class_scope }
|
326
|
+
instance_exec(&ApipieDSL.get_prop_group(scope, name))
|
327
|
+
ensure
|
328
|
+
@current_prop_group = nil
|
329
|
+
@meta = nil
|
330
|
+
end
|
331
|
+
|
332
|
+
# Where the group definition should be looked up when no scope
|
333
|
+
# given. This is expected to return a class.
|
334
|
+
def default_prop_group_scope
|
335
|
+
class_scope
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
module Delegatable
|
340
|
+
class Delegatee
|
341
|
+
include ApipieDSL::Base
|
342
|
+
include ApipieDSL::Common
|
343
|
+
include ApipieDSL::Klass
|
344
|
+
include ApipieDSL::Method
|
345
|
+
|
346
|
+
attr_reader :class_scope
|
347
|
+
|
348
|
+
def initialize(class_scope)
|
349
|
+
@class_scope = class_scope
|
350
|
+
end
|
351
|
+
|
352
|
+
def with(options = {}, &block)
|
353
|
+
@dsl_block = block if block_given?
|
354
|
+
@options = options
|
355
|
+
self
|
356
|
+
end
|
357
|
+
|
358
|
+
def eval_dsl_for(context)
|
359
|
+
case context
|
360
|
+
when :method
|
361
|
+
apipie_eval_dsl(&@dsl_block)
|
362
|
+
when :class
|
363
|
+
class_description(&@dsl_block)
|
364
|
+
when :param_group
|
365
|
+
define_param_group(@options[:name], &@dsl_block)
|
366
|
+
when :prop_group
|
367
|
+
define_prop_group(@options[:name], &@dsl_block)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def self.instance_for(class_scope)
|
372
|
+
@instance_for = new(class_scope)
|
373
|
+
end
|
374
|
+
|
375
|
+
def self.instance_reset
|
376
|
+
@instance_for = nil
|
377
|
+
end
|
378
|
+
|
379
|
+
def self.instance
|
380
|
+
@instance_for
|
381
|
+
end
|
382
|
+
|
383
|
+
def self.extension_data
|
384
|
+
@extension_data ||= []
|
385
|
+
end
|
386
|
+
|
387
|
+
# rubocop:disable Metrics/AbcSize
|
388
|
+
def self.define_validators(class_scope, method_desc)
|
389
|
+
return if method_desc.nil? || ![true, :implicitly, :explicitly].include?(ApipieDSL.configuration.validate)
|
390
|
+
return unless [true, :implicitly].include?(ApipieDSL.configuration.validate)
|
391
|
+
|
392
|
+
old_method = class_scope.instance_method(method_desc.name)
|
393
|
+
old_params = old_method.parameters.map { |param| param[1] }
|
394
|
+
|
395
|
+
class_scope.define_method(method_desc.name) do |*args|
|
396
|
+
# apipie validations start
|
397
|
+
if ApipieDSL.configuration.validate_value?
|
398
|
+
documented_params = ApipieDSL.get_method_description(ApipieDSL.get_class_name(self.class), __method__)
|
399
|
+
.param_descriptions
|
400
|
+
param_values = old_params.each_with_object({}) { |param, values| values[param] = args.shift }
|
401
|
+
|
402
|
+
documented_params.each do |param|
|
403
|
+
param.validate(param_values[param.name]) if param_values.key?(param.name)
|
404
|
+
end
|
405
|
+
end
|
406
|
+
# apipie validations end
|
407
|
+
old_method.bind(self).call(*args)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
def self.update_method_desc(method_desc, dsl_data)
|
412
|
+
method_desc.full_description = dsl_data[:description] || method_desc.full_description
|
413
|
+
method_desc.short_description = dsl_data[:short_description] || method_desc.short_description
|
414
|
+
if dsl_data[:meta]&.is_a?(Hash)
|
415
|
+
method_desc.metadata&.merge!(dsl_data[:meta])
|
416
|
+
else
|
417
|
+
method_desc.metadata = dsl_data[:meta]
|
418
|
+
end
|
419
|
+
method_desc.show = dsl_data[:show]
|
420
|
+
method_desc.raises += dsl_data[:raises].map do |args|
|
421
|
+
ApipieDSL::ExceptionDescription.from_dsl_data(args)
|
422
|
+
end
|
423
|
+
# Update parameters
|
424
|
+
params = dsl_data[:params].map do |args|
|
425
|
+
ApipieDSL::ParameterDescription.from_dsl_data(method_desc, args)
|
426
|
+
end
|
427
|
+
ParameterDescription.merge(method_desc.plain_params, params)
|
428
|
+
end
|
429
|
+
# rubocop:enable Metrics/AbcSize
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
module Module
|
434
|
+
include ApipieDSL::Delegatable
|
435
|
+
|
436
|
+
def apipie_class(name, desc_or_options = nil, options = {}, &block)
|
437
|
+
delegatee = prepare_delegatee(self, desc_or_options, options, &block)
|
438
|
+
delegatee.name(name)
|
439
|
+
delegatee.with(options).eval_dsl_for(:class)
|
440
|
+
|
441
|
+
Delegatee.instance_reset
|
442
|
+
end
|
443
|
+
|
444
|
+
def apipie_method(name, desc_or_options = nil, options = {}, &block)
|
445
|
+
delegatee = prepare_delegatee(self, desc_or_options, options, &block)
|
446
|
+
dsl_data = delegatee.eval_dsl_for(:method)
|
447
|
+
class_scope = delegatee.class_scope
|
448
|
+
ApipieDSL.remove_method_description(class_scope, dsl_data[:dsl_versions], name)
|
449
|
+
ApipieDSL.define_method_description(class_scope, name, dsl_data)
|
450
|
+
|
451
|
+
Delegatee.instance_reset
|
452
|
+
end
|
453
|
+
|
454
|
+
def apipie(context = :method, desc_or_options = nil, options = {}, &block)
|
455
|
+
if desc_or_options.is_a?(Hash)
|
456
|
+
options = options.merge(desc_or_options)
|
457
|
+
elsif desc_or_options.is_a?(String)
|
458
|
+
options[:desc] = desc_or_options
|
459
|
+
end
|
460
|
+
options[:name] ||= context.to_s
|
461
|
+
|
462
|
+
block = proc {} unless block_given?
|
463
|
+
|
464
|
+
delegatee = Delegatee.instance_for(self).with(&block)
|
465
|
+
delegatee.short(options[:desc])
|
466
|
+
# Don't eval the block, since it will be evaluated after method is defined
|
467
|
+
return if context == :method
|
468
|
+
|
469
|
+
delegatee.with(options).eval_dsl_for(context)
|
470
|
+
Delegatee.instance_reset
|
471
|
+
end
|
472
|
+
|
473
|
+
def method_added(method_name)
|
474
|
+
super
|
475
|
+
if Delegatee.instance.nil?
|
476
|
+
# Don't autoload methods if validations are enabled but no apipie block
|
477
|
+
# was called
|
478
|
+
return if ApipieDSL.configuration.validate?
|
479
|
+
# If no apipie block was called but validations are disabled then
|
480
|
+
# it's possible to autoload methods
|
481
|
+
return unless ApipieDSL.configuration.autoload_methods?
|
482
|
+
|
483
|
+
apipie
|
484
|
+
end
|
485
|
+
|
486
|
+
instance = Delegatee.instance
|
487
|
+
class_scope = instance.class_scope
|
488
|
+
# Mainly for Rails in case of constant loading within apipie block.
|
489
|
+
# Prevents methods, that are being defined in other class than the class
|
490
|
+
# where apipie block was called, to be documented with current apipie block
|
491
|
+
return unless class_scope == self
|
492
|
+
|
493
|
+
dsl_data = instance.eval_dsl_for(:method)
|
494
|
+
|
495
|
+
ApipieDSL.remove_method_description(class_scope, dsl_data[:dsl_versions], method_name)
|
496
|
+
method_desc = ApipieDSL.define_method_description(class_scope, method_name, dsl_data)
|
497
|
+
|
498
|
+
Delegatee.instance_reset
|
499
|
+
Delegatee.define_validators(class_scope, method_desc)
|
500
|
+
ensure
|
501
|
+
# Reset if we finished method describing in the right class
|
502
|
+
Delegatee.instance_reset if class_scope == self
|
503
|
+
end
|
504
|
+
|
505
|
+
private
|
506
|
+
|
507
|
+
def prepare_delegatee(scope, desc_or_options, options, &block)
|
508
|
+
if desc_or_options.is_a?(Hash)
|
509
|
+
options = options.merge(desc_or_options)
|
510
|
+
elsif desc_or_options.is_a?(String)
|
511
|
+
options[:desc] = desc_or_options
|
512
|
+
end
|
513
|
+
|
514
|
+
block = proc {} unless block_given?
|
515
|
+
delegatee = Delegatee.instance_for(scope).with(&block)
|
516
|
+
delegatee.short(options[:desc])
|
517
|
+
delegatee
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
module Class
|
522
|
+
include Module
|
523
|
+
end
|
524
|
+
|
525
|
+
module Extension
|
526
|
+
include ApipieDSL::Delegatable
|
527
|
+
|
528
|
+
def apipie(context = :method, desc_or_options = nil, options = {}, &block)
|
529
|
+
if desc_or_options.is_a?(Hash)
|
530
|
+
options = options.merge(desc_or_options)
|
531
|
+
elsif desc_or_options.is_a?(String)
|
532
|
+
options[:desc] = desc_or_options
|
533
|
+
end
|
534
|
+
options[:name] ||= context.to_s
|
535
|
+
|
536
|
+
block = proc {} unless block_given?
|
537
|
+
|
538
|
+
delegatee = Delegatee.instance_for(self).with(&block)
|
539
|
+
delegatee.short(options[:desc])
|
540
|
+
# Don't eval the block, since it will be evaluated after method is defined
|
541
|
+
return if context == :method
|
542
|
+
|
543
|
+
# Currently method extensions are supported only
|
544
|
+
Delegatee.instance_reset
|
545
|
+
end
|
546
|
+
|
547
|
+
def apipie_update(context = :method, &block)
|
548
|
+
block = proc {} unless block_given?
|
549
|
+
|
550
|
+
delegatee = Delegatee.instance_for(self).with(&block)
|
551
|
+
delegatee.dsl_data[:update_only] = true
|
552
|
+
# Don't eval the block, since it will be evaluated after method is defined
|
553
|
+
return if context == :method
|
554
|
+
|
555
|
+
# Currently method extensions are supported only
|
556
|
+
Delegatee.instance_reset
|
557
|
+
end
|
558
|
+
|
559
|
+
def prepended(klass)
|
560
|
+
super
|
561
|
+
Delegatee.extension_data.each do |method_name, dsl_data|
|
562
|
+
class_scope = klass
|
563
|
+
if dsl_data[:update_only]
|
564
|
+
class_name = ApipieDSL.get_class_name(class_scope)
|
565
|
+
# Update the old method description
|
566
|
+
method_desc = ApipieDSL.get_method_description(class_name, method_name)
|
567
|
+
unless method_desc
|
568
|
+
raise StandardError, "Could not find method description for #{class_name}##{method_name}. Was the method defined?"
|
569
|
+
end
|
570
|
+
|
571
|
+
Delegatee.update_method_desc(method_desc, dsl_data)
|
572
|
+
# Define validators for the new method
|
573
|
+
class_scope = self
|
574
|
+
else
|
575
|
+
ApipieDSL.remove_method_description(class_scope, dsl_data[:dsl_versions], method_name)
|
576
|
+
method_desc = ApipieDSL.define_method_description(class_scope, method_name, dsl_data)
|
577
|
+
end
|
578
|
+
Delegatee.instance_reset
|
579
|
+
Delegatee.define_validators(class_scope, method_desc)
|
580
|
+
end
|
581
|
+
ensure
|
582
|
+
Delegatee.instance_reset
|
583
|
+
end
|
584
|
+
|
585
|
+
def method_added(method_name)
|
586
|
+
super
|
587
|
+
# Methods autoload is not supported for extension modules
|
588
|
+
return if Delegatee.instance.nil?
|
589
|
+
|
590
|
+
dsl_data = Delegatee.instance.eval_dsl_for(:method)
|
591
|
+
Delegatee.extension_data << [method_name, dsl_data]
|
592
|
+
ensure
|
593
|
+
Delegatee.instance_reset
|
594
|
+
end
|
595
|
+
end
|
596
|
+
end
|