meta-api 0.0.5 → 0.0.7
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 +18 -1
- data/Gemfile.lock +1 -1
- data/README.md +32 -21
- data/config/locales/zh-CN.yml +1 -1
- data/docs//346/225/231/347/250/213.md +566 -175
- data/docs//347/264/242/345/274/225.md +10 -0
- data/examples/rails_app/Gemfile.lock +1 -1
- data/lib/meta/application/execution.rb +48 -24
- data/lib/meta/application/linked_action.rb +7 -9
- data/lib/meta/application/metadata.rb +5 -3
- data/lib/meta/application/parameters.rb +16 -8
- data/lib/meta/application/route.rb +24 -14
- data/lib/meta/config.rb +8 -3
- data/lib/meta/errors.rb +1 -1
- data/lib/meta/json_schema/builders/schema_builder_tool.rb +8 -2
- data/lib/meta/json_schema/schemas/base_schema.rb +19 -5
- data/lib/meta/json_schema/schemas/object_schema.rb +1 -1
- data/lib/meta/json_schema/schemas/properties.rb +11 -2
- data/lib/meta/json_schema/support/type_converter.rb +2 -2
- data/lib/meta/json_schema/support/validators.rb +1 -1
- data/lib/meta/route_dsl/application_builder.rb +18 -25
- data/lib/meta/route_dsl/around_action_builder.rb +37 -27
- data/lib/meta/route_dsl/meta_builder.rb +16 -9
- data/lib/meta/route_dsl/parameters_builder.rb +29 -6
- data/lib/meta/route_dsl/route_builder.rb +10 -44
- data/lib/meta/route_dsl/uniformed_params_builder.rb +27 -9
- data/lib/meta/swagger_doc.rb +2 -2
- data/lib/meta/utils/kwargs/builder.rb +4 -2
- data/lib/meta/utils/kwargs/checker.rb +36 -0
- data/lib/meta/utils/path.rb +8 -2
- data/lib/meta/utils/route_dsl_builders.rb +30 -0
- data/meta-api.gemspec +1 -1
- metadata +5 -4
- data/lib/meta/route_dsl/helpers.rb +0 -15
@@ -6,32 +6,39 @@ require_relative 'uniformed_params_builder'
|
|
6
6
|
module Meta
|
7
7
|
module RouteDSL
|
8
8
|
class MetaBuilder
|
9
|
-
def initialize(&block)
|
9
|
+
def initialize(route_full_path:, route_method: :all, &block)
|
10
|
+
@route_full_path = route_full_path
|
11
|
+
@method = route_method
|
10
12
|
@meta = {}
|
13
|
+
@parameters_builder = ParametersBuilder.new(route_full_path: route_full_path, route_method: route_method) # 默认给一个空的参数构建器,它只会处理 path 参数
|
11
14
|
|
12
15
|
instance_exec &block if block_given?
|
13
16
|
end
|
14
17
|
|
15
18
|
def build
|
16
|
-
@meta
|
19
|
+
meta = @meta
|
20
|
+
if @meta[:parameters].nil? && @route_full_path =~ /[:*].+/
|
21
|
+
meta[:parameters] = ParametersBuilder.new(route_full_path: @route_full_path, route_method: @method).build
|
22
|
+
end
|
23
|
+
meta
|
17
24
|
end
|
18
25
|
|
19
26
|
def parameters(&block)
|
20
|
-
@meta[:parameters] = ParametersBuilder.new(&block).build
|
27
|
+
@meta[:parameters] = ParametersBuilder.new(route_full_path: @route_full_path, route_method: @method, &block).build
|
21
28
|
end
|
22
29
|
|
23
30
|
def request_body(options = {}, &block)
|
24
|
-
@meta[:request_body] = JsonSchema::SchemaBuilderTool.build(options, &block)
|
31
|
+
@meta[:request_body] = JsonSchema::SchemaBuilderTool.build(options, &block).to_schema
|
25
32
|
end
|
26
33
|
|
27
34
|
# params 宏是一个遗留的宏,它在一个宏定义块内同时定义 parameters 和 request_body
|
28
35
|
def params(&block)
|
29
|
-
@meta[:parameters], @meta[:request_body] = UniformedParamsBuilder.new(&block).build
|
36
|
+
@meta[:parameters], @meta[:request_body] = UniformedParamsBuilder.new(route_full_path: @route_full_path, route_method: @method, &block).build
|
30
37
|
end
|
31
38
|
|
32
|
-
def status(code, *other_codes, &block)
|
39
|
+
def status(code, *other_codes, **options, &block)
|
33
40
|
codes = [code, *other_codes]
|
34
|
-
entity_schema = JsonSchema::SchemaBuilderTool.build(&block)
|
41
|
+
entity_schema = JsonSchema::SchemaBuilderTool.build(options, &block)
|
35
42
|
@meta[:responses] = @meta[:responses] || {}
|
36
43
|
codes.each { |code| @meta[:responses][code] = entity_schema }
|
37
44
|
end
|
@@ -47,8 +54,8 @@ module Meta
|
|
47
54
|
module Delegator
|
48
55
|
method_names = MetaBuilder.public_instance_methods(false) - ['build']
|
49
56
|
method_names.each do |method_name|
|
50
|
-
define_method(method_name) do |*args, &block|
|
51
|
-
@meta_builder.send(method_name, *args, &block) and self
|
57
|
+
define_method(method_name) do |*args, **kwargs, &block|
|
58
|
+
@meta_builder.send(method_name, *args, **kwargs, &block) and self
|
52
59
|
end
|
53
60
|
end
|
54
61
|
end
|
@@ -1,24 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative '../application/parameters'
|
4
|
+
require_relative '../utils/kwargs/checker'
|
3
5
|
|
4
6
|
module Meta
|
5
7
|
module RouteDSL
|
6
8
|
class ParametersBuilder
|
7
|
-
def initialize(&block)
|
8
|
-
@
|
9
|
+
def initialize(route_full_path:, route_method:, &block)
|
10
|
+
@route_full_path = route_full_path || ''
|
11
|
+
@route_method = route_method
|
12
|
+
@parameter_options = {}
|
9
13
|
|
10
14
|
instance_exec &block if block_given?
|
11
15
|
end
|
12
16
|
|
13
|
-
def param(name, options)
|
17
|
+
def param(name, options = {})
|
18
|
+
# 修正 path 参数的选项
|
14
19
|
options = options.dup
|
15
|
-
|
20
|
+
if path_param_names.include?(name) # path 参数
|
21
|
+
options = Utils::KeywordArgs::Checker.fix!(options, in: 'path', required: true)
|
22
|
+
else
|
23
|
+
options = Utils::KeywordArgs::Checker.merge_defaults!(options, in: 'query')
|
24
|
+
end
|
16
25
|
|
17
|
-
|
26
|
+
in_op = options.delete(:in)
|
27
|
+
@parameter_options[name] = { in: in_op, schema: JsonSchema::BaseSchema.new(options) }
|
18
28
|
end
|
19
29
|
|
20
30
|
def build
|
21
|
-
|
31
|
+
# 补充未声明的 path 参数
|
32
|
+
(path_param_names - @parameter_options.keys).each do |name|
|
33
|
+
@parameter_options[name] = { in: 'path', schema: JsonSchema::BaseSchema.new(required: true) }
|
34
|
+
end
|
35
|
+
|
36
|
+
Parameters.new(@parameter_options)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def path_param_names
|
42
|
+
@_path_param_names ||= @route_full_path.split('/')
|
43
|
+
.filter { |part| part =~ /[:*].+/ }
|
44
|
+
.map { |part| part[1..-1].to_sym }
|
22
45
|
end
|
23
46
|
end
|
24
47
|
end
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require 'json'
|
4
4
|
require_relative '../entity'
|
5
5
|
require_relative '../application/route'
|
6
|
-
require_relative 'helpers'
|
7
6
|
require_relative 'chain_builder'
|
8
7
|
require_relative 'action_builder'
|
9
8
|
require_relative 'meta_builder'
|
@@ -16,52 +15,27 @@ module Meta
|
|
16
15
|
|
17
16
|
alias :if_status :status
|
18
17
|
|
19
|
-
|
18
|
+
# 这里的 path 局部的路径,也就是由 route 宏命令定义的路径
|
19
|
+
def initialize(path, method = :all, parent_path: '',&block)
|
20
|
+
route_full_path = Utils::Path.join(parent_path, path)
|
21
|
+
|
20
22
|
@path = path || ''
|
21
23
|
@method = method || :all
|
22
24
|
@action_builder = nil
|
23
|
-
@meta_builder = MetaBuilder.new
|
25
|
+
@meta_builder = MetaBuilder.new(route_full_path: route_full_path, route_method: method)
|
24
26
|
|
25
27
|
instance_exec &block if block_given?
|
26
28
|
end
|
27
29
|
|
28
|
-
def build(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
meta2[:parameters] = meta[:parameters].merge(meta2[:parameters])
|
33
|
-
end
|
34
|
-
|
35
|
-
# 合并 parameters 参数
|
36
|
-
meta2[:parameters] ||= {}
|
37
|
-
path_params = Utils::Path.join(parent_path, @path).split('/')
|
38
|
-
.filter { |part| part =~ /[:*].+/ }
|
39
|
-
.map { |part| part[1..-1].to_sym }
|
40
|
-
path_params.each do |name|
|
41
|
-
unless meta2[:parameters].key?(name)
|
42
|
-
meta2[:parameters][name] = {
|
43
|
-
in: 'path',
|
44
|
-
schema: JsonSchema::BaseSchema.new(required: true)
|
45
|
-
}
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# 构建洋葱圈模型的 LinkedAction
|
50
|
-
# 合并父级传递过来的 callbacks,将 before 和 around 放在前面,after 放在后面
|
51
|
-
parent_before = callbacks.filter { |cb| cb[:lifecycle] == :before || cb[:lifecycle] == :around }
|
52
|
-
parent_after = callbacks.filter { |cb| cb[:lifecycle] == :after }
|
53
|
-
callbacks = parent_before + [{ lifecycle: :before, proc: @action_builder&.build }] + parent_after
|
54
|
-
# 使用 AroundActionBuilder 构建洋葱圈模型
|
55
|
-
around_action_builder = AroundActionBuilder.new
|
56
|
-
callbacks.each do |cb|
|
57
|
-
around_action_builder.send(cb[:lifecycle], &cb[:proc]) if cb[:proc]
|
58
|
-
end
|
59
|
-
action = around_action_builder.build
|
30
|
+
def build(meta_options: {}, callbacks: {})
|
31
|
+
meta_options = Utils::RouteDSLBuilders.merge_meta_options(meta_options, @meta_builder.build)
|
32
|
+
callbacks = Utils::RouteDSLBuilders.merge_callbacks(callbacks, [{ lifecycle: :before, proc: @action_builder&.build }])
|
33
|
+
action = AroundActionBuilder.build_from_callbacks(callbacks: callbacks)
|
60
34
|
|
61
35
|
Route.new(
|
62
36
|
path: @path,
|
63
37
|
method: @method,
|
64
|
-
meta:
|
38
|
+
meta: meta_options,
|
65
39
|
action: action
|
66
40
|
)
|
67
41
|
end
|
@@ -81,14 +55,6 @@ module Meta
|
|
81
55
|
self
|
82
56
|
end
|
83
57
|
end
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
def clone_meta(meta)
|
88
|
-
meta = meta.clone
|
89
|
-
meta[:responses] = meta[:responses].clone if meta[:responses]
|
90
|
-
meta
|
91
|
-
end
|
92
58
|
end
|
93
59
|
end
|
94
60
|
end
|
@@ -3,31 +3,49 @@
|
|
3
3
|
module Meta
|
4
4
|
module RouteDSL
|
5
5
|
class UniformedParamsBuilder
|
6
|
-
def initialize(&block)
|
7
|
-
@
|
8
|
-
@
|
6
|
+
def initialize(route_full_path:, route_method:, &block)
|
7
|
+
@route_full_path = route_full_path
|
8
|
+
@route_method = route_method
|
9
|
+
@parameters_builder = ParametersBuilder.new(route_full_path: @route_full_path, route_method: @route_method)
|
10
|
+
|
11
|
+
@parameter_options = {}
|
9
12
|
|
10
13
|
instance_exec &block if block_given?
|
11
14
|
end
|
12
15
|
|
13
16
|
def param(name, options = {}, &block)
|
14
|
-
options = options.dup
|
15
|
-
|
17
|
+
options = (options || {}).dup
|
18
|
+
in_op = options.delete(:in) || \
|
19
|
+
if path_param_names.include?(name)
|
20
|
+
'path'
|
21
|
+
elsif @route_method == :get
|
22
|
+
'query'
|
23
|
+
else
|
24
|
+
'body'
|
25
|
+
end
|
16
26
|
|
17
|
-
if
|
27
|
+
if in_op == 'body'
|
18
28
|
property name, options, &block
|
19
29
|
else
|
20
|
-
@
|
30
|
+
@parameters_builder.param name, options
|
21
31
|
end
|
22
32
|
end
|
23
33
|
|
24
34
|
def property(name, options = {}, &block)
|
35
|
+
@request_body_builder ||= JsonSchema::ObjectSchemaBuilder.new
|
25
36
|
@request_body_builder.property name, options, &block
|
26
37
|
end
|
27
38
|
|
28
39
|
def build
|
29
|
-
|
30
|
-
|
40
|
+
[@parameters_builder.build, @request_body_builder&.to_schema]
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def path_param_names
|
46
|
+
@_path_param_names ||= @route_full_path.split('/')
|
47
|
+
.filter { |part| part =~ /[:*].+/ }
|
48
|
+
.map { |part| part[1..-1].to_sym }
|
31
49
|
end
|
32
50
|
end
|
33
51
|
end
|
data/lib/meta/swagger_doc.rb
CHANGED
@@ -47,13 +47,13 @@ module Meta
|
|
47
47
|
# ]
|
48
48
|
def get_paths_and_routes!(application, prefix = '', store_routes = [])
|
49
49
|
if (application.is_a?(Class) && application < Application) || application.is_a?(Application)
|
50
|
-
prefix =
|
50
|
+
prefix = Utils::Path.join(prefix, application.prefix)
|
51
51
|
(application.routes + application.applications).each do |mod|
|
52
52
|
get_paths_and_routes!(mod, prefix, store_routes)
|
53
53
|
end
|
54
54
|
elsif application.is_a?(Route)
|
55
55
|
route = application
|
56
|
-
route_path = route.path == :all ? prefix :
|
56
|
+
route_path = route.path == :all ? prefix : Utils::Path.join(prefix, route.path)
|
57
57
|
store_routes << [route_path, route] unless route.method == :all
|
58
58
|
else
|
59
59
|
raise "Param application must be a Application instance, Application module or a Route instance, but it got a `#{application}`"
|
@@ -55,10 +55,11 @@ module Meta
|
|
55
55
|
class Argument
|
56
56
|
DEFAULT_TRANSFORMER = ->(value) { value }
|
57
57
|
|
58
|
-
def initialize(name:, normalizer: DEFAULT_TRANSFORMER, alias_names: [])
|
58
|
+
def initialize(name:, normalizer: DEFAULT_TRANSFORMER, validator: nil, default: nil, alias_names: [])
|
59
59
|
@key_name = name
|
60
60
|
@consumer_names = [name] + alias_names
|
61
|
-
@normalizer = normalizer
|
61
|
+
@normalizer = default ? ->(value) { normalizer.call(value || default) } : normalizer
|
62
|
+
@validator = validator
|
62
63
|
end
|
63
64
|
|
64
65
|
def consume(final_args, args)
|
@@ -71,6 +72,7 @@ module Meta
|
|
71
72
|
def consume_name(final_args, args, consumer_name)
|
72
73
|
if args.key?(consumer_name)
|
73
74
|
value = @normalizer.call(args.delete(consumer_name))
|
75
|
+
@validator.call(value) if @validator
|
74
76
|
final_args[@key_name] = value
|
75
77
|
true
|
76
78
|
else
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Meta
|
4
|
+
module Utils
|
5
|
+
class KeywordArgs
|
6
|
+
module Checker
|
7
|
+
class << self
|
8
|
+
# 将 options 内的值修正为固定值,该方法会原地修改 options 选项。
|
9
|
+
# 如果 options 中的缺失相应的值,则使用 fixed_values 中的值补充;如果 options 中的值不等于 fixed_values 中对应的值,则抛出异常。
|
10
|
+
# 示例:
|
11
|
+
# (1)fix!({}, { a: 1, b: 2 }) # => { a: 1, b: 2 }
|
12
|
+
# (2)fix!({ a: 1 }, { a: 2 }) # raise error
|
13
|
+
def fix!(options, fixed_values)
|
14
|
+
fixed_values.each do |key, value|
|
15
|
+
if options.include?(key)
|
16
|
+
if options[key] != value
|
17
|
+
raise ArgumentError, "关键字参数 #{key} 的值不正确,必须为 #{value}"
|
18
|
+
end
|
19
|
+
else
|
20
|
+
options[key] = value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
options
|
24
|
+
end
|
25
|
+
|
26
|
+
def merge_defaults!(options, defaults)
|
27
|
+
defaults.each do |key, value|
|
28
|
+
options[key] = value unless options[key]
|
29
|
+
end
|
30
|
+
options
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/meta/utils/path.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
#
|
2
3
|
|
3
4
|
module Meta
|
4
5
|
module Utils
|
5
6
|
class Path
|
6
7
|
class << self
|
8
|
+
# 规范化 path 结构,确保 path 以 '/' 开头,不以 '/' 结尾。
|
9
|
+
# 仅有一个例外,如果 path 为 nil 或空字符串,则返回空字符串 ''.
|
7
10
|
def normalize_path(path)
|
8
11
|
path = '/' unless path
|
9
12
|
path = '/' + path unless path.start_with?('/')
|
@@ -11,8 +14,11 @@ module Meta
|
|
11
14
|
path
|
12
15
|
end
|
13
16
|
|
14
|
-
|
15
|
-
|
17
|
+
# 合并两个 path. 有且只有一个例外,如果 p1 或 p2 其中之一为 '/',则返回另一个。
|
18
|
+
def join(*parts)
|
19
|
+
parts = parts.map { |p| (p || '').delete_prefix('/').delete_suffix('/') }
|
20
|
+
parts = parts.reject { |p| p.nil? || p.empty? }
|
21
|
+
'/' + parts.join('/')
|
16
22
|
end
|
17
23
|
end
|
18
24
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Meta
|
4
|
+
module Utils
|
5
|
+
class RouteDSLBuilders
|
6
|
+
class << self
|
7
|
+
def merge_meta_options(options1, options2)
|
8
|
+
final_options = (options1 || {}).merge(options2 || {})
|
9
|
+
if options1[:parameters] && options2[:parameters]
|
10
|
+
final_options[:parameters] = options1[:parameters].merge(options2[:parameters])
|
11
|
+
end
|
12
|
+
if options1[:request_body].is_a?(Meta::JsonSchema::ObjectSchema) && options2[:request_body].is_a?(Meta::JsonSchema::ObjectSchema)
|
13
|
+
final_options[:request_body] = options1[:request_body].merge_other_properties(options2[:request_body].properties)
|
14
|
+
end
|
15
|
+
if options1[:responses] && options2[:responses]
|
16
|
+
final_options[:responses] = options1[:responses].merge(options2[:responses])
|
17
|
+
end
|
18
|
+
final_options
|
19
|
+
end
|
20
|
+
|
21
|
+
def merge_callbacks(parent_callbacks, current_callbacks)
|
22
|
+
# 合并父级传递过来的 callbacks,将 before 和 around 放在前面,after 放在后面
|
23
|
+
parent_before = parent_callbacks.filter { |cb| cb[:lifecycle] == :before || cb[:lifecycle] == :around }
|
24
|
+
parent_after = parent_callbacks.filter { |cb| cb[:lifecycle] == :after }
|
25
|
+
parent_before + current_callbacks + parent_after
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/meta-api.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: meta-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yetrun
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: 一个 Web API 框架,该框架采用定义元信息的方式编写 API,并同步生成 API 文档
|
14
14
|
email:
|
@@ -113,7 +113,6 @@ files:
|
|
113
113
|
- lib/meta/route_dsl/application_builder.rb
|
114
114
|
- lib/meta/route_dsl/around_action_builder.rb
|
115
115
|
- lib/meta/route_dsl/chain_builder.rb
|
116
|
-
- lib/meta/route_dsl/helpers.rb
|
117
116
|
- lib/meta/route_dsl/meta_builder.rb
|
118
117
|
- lib/meta/route_dsl/parameters_builder.rb
|
119
118
|
- lib/meta/route_dsl/route_builder.rb
|
@@ -121,7 +120,9 @@ files:
|
|
121
120
|
- lib/meta/swagger_doc.rb
|
122
121
|
- lib/meta/utils/kwargs/builder.rb
|
123
122
|
- lib/meta/utils/kwargs/check.rb
|
123
|
+
- lib/meta/utils/kwargs/checker.rb
|
124
124
|
- lib/meta/utils/path.rb
|
125
|
+
- lib/meta/utils/route_dsl_builders.rb
|
125
126
|
- meta-api.gemspec
|
126
127
|
homepage: https://github.com/yetrun/web-frame
|
127
128
|
licenses:
|
@@ -145,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
146
|
- !ruby/object:Gem::Version
|
146
147
|
version: '0'
|
147
148
|
requirements: []
|
148
|
-
rubygems_version: 3.
|
149
|
+
rubygems_version: 3.4.15
|
149
150
|
signing_key:
|
150
151
|
specification_version: 4
|
151
152
|
summary: 一个 Web API 框架
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Meta
|
4
|
-
module RouteDSL
|
5
|
-
module Helpers
|
6
|
-
class << self
|
7
|
-
def join_path(*parts)
|
8
|
-
parts = parts.map { |p| (p || '').delete_prefix('/').delete_suffix('/') }
|
9
|
-
parts = parts.reject { |p| p.nil? || p.empty? }
|
10
|
-
'/' + parts.join('/')
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|