meta-api 0.0.3 → 0.0.5
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 +10 -0
- data/Gemfile.lock +2 -2
- data/README.md +1 -1
- data/config/locales/zh-CN.yml +11 -3
- data/docs//346/225/231/347/250/213.md +28 -1
- data/examples/rails_app/Gemfile.lock +1 -1
- data/lib/meta/application/execution.rb +1 -1
- data/lib/meta/application/linked_action.rb +18 -0
- data/lib/meta/application/metadata.rb +1 -1
- data/lib/meta/application/parameters.rb +2 -2
- data/lib/meta/application/route.rb +4 -5
- data/lib/meta/json_schema/builders/array_schema_builder.rb +4 -9
- data/lib/meta/json_schema/schemas/base_schema.rb +15 -2
- data/lib/meta/json_schema/support/schema_options.rb +0 -9
- data/lib/meta/json_schema/support/type_converter.rb +13 -11
- data/lib/meta/route_dsl/application_builder.rb +24 -10
- data/lib/meta/route_dsl/around_action_builder.rb +80 -0
- data/lib/meta/route_dsl/route_builder.rb +14 -5
- data/meta-api.gemspec +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3fc58de696caf4cf3abade846de9af53bb6491bda575b361461aa67f07d2fb9
|
4
|
+
data.tar.gz: 68d00c2d43086af73a10bbe6bfc103a5ee8611751b01ad2b9c811edf9197438a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18cf46619c9f286fe01fbc3b941a6ca5a5e380740ad94e4d6827040cd9c6a014f6249eb13bc609a9f56fe2c15da6e044250f610b0eafdf6202aa9b6a16a0f15b
|
7
|
+
data.tar.gz: edc1532a86fe8776f061cfd1f550392ba280a4e693437b6d6260bfcfce8f0a33d7ae66e6f95250fb8530751307751a53857c0fd2a32a57ec9f8764bf6a9037da
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# 更新日志
|
2
2
|
|
3
|
+
## 0.0.5(2023 年 4 月 18 日)
|
4
|
+
|
5
|
+
1. 调整了 `around` 钩子的执行顺序,它与 `before` 钩子共同定义顺序。
|
6
|
+
2. 修复了若干 bug.
|
7
|
+
|
8
|
+
## 0.0.4(2023 年 4 月 18 日)
|
9
|
+
|
10
|
+
1. `Application` 添加 `.around` 宏。
|
11
|
+
2. `render` 时支持传递 `user_data` 选项,用作 value 解析的第二个参数。
|
12
|
+
|
3
13
|
## 0.0.3(2023 年 4 月 4 日)
|
4
14
|
|
5
15
|
1. 添加两个新的选项 `ref:` 和 `dynamic_ref:`,以便后期取代 `using:`.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
data/config/locales/zh-CN.yml
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
zh-CN:
|
2
2
|
json_schema:
|
3
|
+
type_names:
|
4
|
+
basic: "基本类型"
|
5
|
+
array: "数组类型"
|
3
6
|
errors:
|
4
|
-
required:
|
5
|
-
format:
|
6
|
-
allowable:
|
7
|
+
required: "未提供"
|
8
|
+
format: "格式不正确"
|
9
|
+
allowable: "不在允许的值范围内"
|
10
|
+
type_convert:
|
11
|
+
basic: "类型转化失败,期望得到一个 `%{target_type}` 类型,但值 `%{value}` 无法转化"
|
12
|
+
array: "转化为数组类型时期望对象拥有 `to_a` 方法"
|
13
|
+
object: "类型转化失败,期望得到一个 `object` 类型,但值 `%{value}` 是一个%{real_type}"
|
14
|
+
unknown: "未知的目标类型 `%{target_type}`"
|
@@ -118,7 +118,7 @@ class DemoApp < Meta::Application
|
|
118
118
|
end
|
119
119
|
```
|
120
120
|
|
121
|
-
###
|
121
|
+
### 钩子
|
122
122
|
|
123
123
|
*(如果不涉及到钩子和异常拦截,嵌套路由将毫无意义。)*
|
124
124
|
|
@@ -142,6 +142,33 @@ class DemoApp < Meta::Application
|
|
142
142
|
end
|
143
143
|
```
|
144
144
|
|
145
|
+
同时还支持 `around` 钩子(*试验特性*):
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
class DemoApp < Meta::Application
|
149
|
+
before { puts 1 }
|
150
|
+
around { |next_action|
|
151
|
+
puts 2
|
152
|
+
next_action.execute(self)
|
153
|
+
puts 7
|
154
|
+
}
|
155
|
+
before { puts 3 }
|
156
|
+
after { puts 5 }
|
157
|
+
after { puts 6 }
|
158
|
+
|
159
|
+
get '/request' do
|
160
|
+
puts 4
|
161
|
+
end
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
所有钩子的执行顺序是:
|
166
|
+
|
167
|
+
1. `before` 钩子和 `around` 钩子的前半部分,按照定义的顺序执行
|
168
|
+
2. 然后执行路由方法。
|
169
|
+
3. 然后执行 `after` 钩子,按照定义的顺序执行。
|
170
|
+
4. 最后执行 `around` 钩子的后半部分,按照定义的逆序执行。
|
171
|
+
|
145
172
|
### 异常拦截
|
146
173
|
|
147
174
|
在 `namespace` 中使用 `rescue_error` 拦截异常。
|
@@ -123,7 +123,7 @@ module Meta
|
|
123
123
|
if request_body.empty?
|
124
124
|
json = {}
|
125
125
|
elsif !request.content_type.start_with?('application/json')
|
126
|
-
raise Errors::UnsupportedContentType, "只接受 Content-Type 为 application/json
|
126
|
+
raise Errors::UnsupportedContentType, "只接受 Content-Type 为 application/json 的请求参数,当前格式:#{request.content_type}"
|
127
127
|
else
|
128
128
|
json = JSON.parse(request_body)
|
129
129
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# 洋葱圈模型的链式调用,需要结合 Meta::RouteDSL::AroundActionBuilder 才可以看到它奇效。
|
4
|
+
|
5
|
+
module Meta
|
6
|
+
module RouteDSL
|
7
|
+
class LinkedAction
|
8
|
+
def initialize(current_proc, next_action)
|
9
|
+
@current_proc = current_proc
|
10
|
+
@next_action = next_action
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(execution)
|
14
|
+
execution.instance_exec(@next_action, &@current_proc)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -28,11 +28,11 @@ module Meta
|
|
28
28
|
{
|
29
29
|
name: name,
|
30
30
|
in: options[:in],
|
31
|
-
required: property_options[:required]
|
31
|
+
required: property_options.key?(:required) ? property_options[:required] : false,
|
32
32
|
description: property_options[:description] || '',
|
33
33
|
schema: {
|
34
34
|
type: property_options[:type]
|
35
|
-
}
|
35
|
+
}.compact
|
36
36
|
}.compact
|
37
37
|
end unless parameters.empty?
|
38
38
|
end
|
@@ -8,24 +8,23 @@ module Meta
|
|
8
8
|
class Route
|
9
9
|
include PathMatchingMod.new(path_method: :path, matching_mode: :full)
|
10
10
|
|
11
|
-
attr_reader :path, :method, :meta, :
|
11
|
+
attr_reader :path, :method, :meta, :action
|
12
12
|
|
13
|
-
def initialize(path: '', method: :all, meta: {},
|
13
|
+
def initialize(path: '', method: :all, meta: {}, action: nil)
|
14
14
|
@path = Utils::Path.normalize_path(path)
|
15
15
|
@method = method
|
16
16
|
@meta = Metadata.new(meta)
|
17
|
-
@
|
17
|
+
@action = action
|
18
18
|
end
|
19
19
|
|
20
20
|
def execute(execution, remaining_path)
|
21
21
|
path_matching.merge_path_params(remaining_path, execution.request)
|
22
22
|
|
23
|
-
# 依次执行这个环境
|
24
23
|
begin
|
25
24
|
execution.parse_parameters(@meta[:parameters]) if @meta[:parameters]
|
26
25
|
execution.parse_request_body(@meta[:request_body]) if @meta[:request_body]
|
27
26
|
|
28
|
-
|
27
|
+
action.execute(execution) if action
|
29
28
|
|
30
29
|
render_entity(execution) if @meta[:responses]
|
31
30
|
rescue Execution::Abort
|
@@ -5,15 +5,10 @@ module Meta
|
|
5
5
|
class ArraySchemaBuilder
|
6
6
|
def initialize(options, &block)
|
7
7
|
options = options.dup
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
elsif options[:dynamic_ref]
|
13
|
-
items_options = { dynamic_ref: options.delete(:dynamic_ref) }
|
14
|
-
else
|
15
|
-
items_options = {}
|
16
|
-
end
|
8
|
+
items_options = options.delete(:items) || {}
|
9
|
+
items_options[:ref] = options.delete(:ref) if options[:ref]
|
10
|
+
items_options[:dynamic_ref] = options.delete(:dynamic_ref) if options[:dynamic_ref]
|
11
|
+
|
17
12
|
@items_schema = SchemaBuilderTool.build(items_options, &block)
|
18
13
|
@base_options = options
|
19
14
|
end
|
@@ -6,6 +6,12 @@ require_relative '../support/schema_options'
|
|
6
6
|
module Meta
|
7
7
|
module JsonSchema
|
8
8
|
class BaseSchema
|
9
|
+
OPTIONS_CHECKER = Utils::KeywordArgs::Builder.build do
|
10
|
+
key :type, :items, :description, :presenter, :value, :default, :properties, :convert
|
11
|
+
key :validate, :required, :format, :allowable
|
12
|
+
key :param, :render
|
13
|
+
end
|
14
|
+
|
9
15
|
# `options` 包含了转换器、验证器、文档、选项。
|
10
16
|
#
|
11
17
|
# 由于本对象可继承,基于不同的继承可分别表示基本类型、对象和数组,所以该属
|
@@ -16,11 +22,12 @@ module Meta
|
|
16
22
|
attr_reader :options
|
17
23
|
|
18
24
|
def initialize(options = {})
|
25
|
+
options = OPTIONS_CHECKER.check(options)
|
19
26
|
@options = SchemaOptions.normalize(options)
|
20
27
|
end
|
21
28
|
|
22
29
|
USER_OPTIONS_CHECKER = Utils::KeywordArgs::Builder.build do
|
23
|
-
key :stage, :execution, :object_value, :type_conversion, :validation
|
30
|
+
key :stage, :execution, :object_value, :type_conversion, :validation, :user_data
|
24
31
|
|
25
32
|
# 以下三个是 ObjectSchema 需要的选项
|
26
33
|
key :discard_missing, :exclude, :scope
|
@@ -57,7 +64,13 @@ module Meta
|
|
57
64
|
|
58
65
|
def resolve_value(user_options)
|
59
66
|
value_proc = options[:value]
|
60
|
-
|
67
|
+
if value_proc.lambda?
|
68
|
+
value_proc_params = []
|
69
|
+
value_proc_params << user_options[:object_value] if value_proc.arity >= 1
|
70
|
+
value_proc_params << user_options[:user_data] if value_proc.arity >= 2
|
71
|
+
else
|
72
|
+
value_proc_params = [user_options[:object_value], user_options[:user_data]]
|
73
|
+
end
|
61
74
|
|
62
75
|
if user_options[:execution]
|
63
76
|
user_options[:execution].instance_exec(*value_proc_params, &value_proc)
|
@@ -5,13 +5,6 @@ require_relative '../../utils/kwargs/builder'
|
|
5
5
|
module Meta
|
6
6
|
module JsonSchema
|
7
7
|
module SchemaOptions
|
8
|
-
OPTIONS_CHECKER = Utils::KeywordArgs::Builder.build do
|
9
|
-
key :type, :items, :description, :presenter, :value, :format, :required, :default, :validate, :allowable, :properties, :convert
|
10
|
-
key :using, normalizer: ->(value) { value.is_a?(Proc) ? { resolve: value } : value }
|
11
|
-
key :param
|
12
|
-
key :render
|
13
|
-
end
|
14
|
-
|
15
8
|
@default_options = {
|
16
9
|
scope: [],
|
17
10
|
required: false
|
@@ -34,8 +27,6 @@ module Meta
|
|
34
27
|
end
|
35
28
|
|
36
29
|
def normalize(options)
|
37
|
-
options = OPTIONS_CHECKER.check(options)
|
38
|
-
|
39
30
|
# 只要 options 中设置为 nil 的选项没有明确的意义,则下行代码是永远有效的
|
40
31
|
options = (@default_options.compact).merge(options.compact)
|
41
32
|
if options[:using]
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'bigdecimal'
|
4
|
+
|
3
5
|
module Meta
|
4
6
|
module JsonSchema
|
5
7
|
class ObjectWrapper
|
@@ -29,7 +31,7 @@ module Meta
|
|
29
31
|
@definity_types = {
|
30
32
|
'boolean' => [TrueClass, FalseClass],
|
31
33
|
'integer' => [Integer],
|
32
|
-
'number' => [Integer, Float],
|
34
|
+
'number' => [Integer, Float, BigDecimal],
|
33
35
|
'string' => [String],
|
34
36
|
'array' => [Array],
|
35
37
|
'object' => [Hash, ObjectWrapper]
|
@@ -39,7 +41,7 @@ module Meta
|
|
39
41
|
@boolean_converters = {
|
40
42
|
[String] => lambda do |value|
|
41
43
|
unless %w[true True TRUE false False FALSE].include?(value)
|
42
|
-
raise TypeConvertError,
|
44
|
+
raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.basic', target_type: 'boolean', value: value)
|
43
45
|
end
|
44
46
|
|
45
47
|
value.downcase == 'true'
|
@@ -50,14 +52,14 @@ module Meta
|
|
50
52
|
[String] => lambda do |value|
|
51
53
|
# 允许的格式:+34、-34、34、34.0 等
|
52
54
|
unless value =~ /^[+-]?\d+(\.0+)?$/
|
53
|
-
raise TypeConvertError,
|
55
|
+
raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.basic', target_type: 'integer', value: value)
|
54
56
|
end
|
55
57
|
|
56
58
|
value.to_i
|
57
59
|
end,
|
58
|
-
[Float] => lambda do |value|
|
60
|
+
[Float, BigDecimal] => lambda do |value|
|
59
61
|
unless value.to_i == value
|
60
|
-
raise TypeConvertError,
|
62
|
+
raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.basic', target_type: 'integer', value: value)
|
61
63
|
end
|
62
64
|
|
63
65
|
value.to_i
|
@@ -67,7 +69,7 @@ module Meta
|
|
67
69
|
@number_converters = {
|
68
70
|
[String] => lambda do |value|
|
69
71
|
unless value =~ /^[+-]?\d+(\.\d+)?$/
|
70
|
-
raise TypeConvertError,
|
72
|
+
raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.basic', target_type: 'number', value: value)
|
71
73
|
end
|
72
74
|
|
73
75
|
float = value.to_f
|
@@ -84,7 +86,7 @@ module Meta
|
|
84
86
|
@array_converters = {
|
85
87
|
[Object] => lambda do |value|
|
86
88
|
unless value.respond_to?(:to_a)
|
87
|
-
raise TypeConvertError,
|
89
|
+
raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.array')
|
88
90
|
end
|
89
91
|
|
90
92
|
value.to_a
|
@@ -94,9 +96,9 @@ module Meta
|
|
94
96
|
@object_converters = {
|
95
97
|
[Object] => lambda do |value|
|
96
98
|
if [TrueClass, FalseClass, Integer, Float, String].any? { |ruby_type| value.is_a?(ruby_type) }
|
97
|
-
raise TypeConvertError,
|
99
|
+
raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.object', value: value, real_type: I18n.t(:'json_schema.type_description.basic'))
|
98
100
|
elsif value.is_a?(Array)
|
99
|
-
raise TypeConvertError,
|
101
|
+
raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.object', value: value, real_type: I18n.t(:'json_schema.type_description.array'))
|
100
102
|
end
|
101
103
|
|
102
104
|
ObjectWrapper.new(value)
|
@@ -106,7 +108,7 @@ module Meta
|
|
106
108
|
class << self
|
107
109
|
def convert_value(value, target_type)
|
108
110
|
return nil if value.nil?
|
109
|
-
raise JsonSchema::TypeConvertError,
|
111
|
+
raise JsonSchema::TypeConvertError, I18n.t(:'json_schema.errors.type_convert.unknown') unless @definity_types.keys.include?(target_type)
|
110
112
|
return value if match_definity_types?(value, target_type)
|
111
113
|
|
112
114
|
convert_to_definity_type(value, target_type)
|
@@ -126,7 +128,7 @@ module Meta
|
|
126
128
|
return converter.call(value)
|
127
129
|
end
|
128
130
|
end
|
129
|
-
raise TypeConvertError,
|
131
|
+
raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.basic', target_type: target_type, value: value)
|
130
132
|
end
|
131
133
|
end
|
132
134
|
end
|
@@ -10,8 +10,7 @@ module Meta
|
|
10
10
|
|
11
11
|
def initialize(prefix = nil, &block)
|
12
12
|
@mod_prefix = prefix
|
13
|
-
@
|
14
|
-
@after_callbacks = []
|
13
|
+
@callbacks = []
|
15
14
|
@error_guards = []
|
16
15
|
@meta_builder = MetaBuilder.new
|
17
16
|
@mod_builders = []
|
@@ -20,7 +19,7 @@ module Meta
|
|
20
19
|
instance_exec &block if block_given?
|
21
20
|
end
|
22
21
|
|
23
|
-
def build(parent_path: '', meta: {},
|
22
|
+
def build(parent_path: '', meta: {}, callbacks: [])
|
24
23
|
# 合并 meta 时不仅仅是覆盖,比如 parameters 参数需要合并
|
25
24
|
meta2 = (meta || {}).merge(@meta_builder.build)
|
26
25
|
if meta[:parameters] && meta2[:parameters]
|
@@ -28,9 +27,11 @@ module Meta
|
|
28
27
|
end
|
29
28
|
|
30
29
|
# 构建子模块
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
# 合并父级传递过来的 callbacks,将 before 和 around 放在前面,after 放在后面
|
31
|
+
parent_before = callbacks.filter { |cb| cb[:lifecycle] == :before || cb[:lifecycle] == :around }
|
32
|
+
parent_after = callbacks.filter { |cb| cb[:lifecycle] == :after }
|
33
|
+
callbacks = parent_before + @callbacks + parent_after
|
34
|
+
mods = @mod_builders.map { |builder| builder.build(parent_path: Utils::Path.join(parent_path, @mod_prefix), meta: meta2, callbacks: callbacks) }
|
34
35
|
|
35
36
|
Application.new(
|
36
37
|
prefix: @mod_prefix,
|
@@ -64,11 +65,24 @@ module Meta
|
|
64
65
|
|
65
66
|
# 定义模块内的公共逻辑
|
66
67
|
def before(&block)
|
67
|
-
@
|
68
|
+
@callbacks << {
|
69
|
+
lifecycle: :before,
|
70
|
+
proc: block
|
71
|
+
}
|
68
72
|
end
|
69
73
|
|
70
74
|
def after(&block)
|
71
|
-
@
|
75
|
+
@callbacks << {
|
76
|
+
lifecycle: :after,
|
77
|
+
proc: block
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def around(&block)
|
82
|
+
@callbacks << {
|
83
|
+
lifecycle: :around,
|
84
|
+
proc: block
|
85
|
+
}
|
72
86
|
end
|
73
87
|
|
74
88
|
def rescue_error(error_class, &block)
|
@@ -94,13 +108,13 @@ module Meta
|
|
94
108
|
@meta = meta
|
95
109
|
end
|
96
110
|
|
97
|
-
def build(parent_path: '', meta: {},
|
111
|
+
def build(parent_path: '', meta: {}, **kwargs)
|
98
112
|
# 合并 meta 时不仅仅是覆盖,比如 parameters 参数需要合并
|
99
113
|
meta2 = (meta || {}).merge(@meta)
|
100
114
|
if meta[:parameters] && meta2[:parameters]
|
101
115
|
meta2[:parameters] = meta[:parameters].merge(meta2[:parameters])
|
102
116
|
end
|
103
|
-
@builder.build(parent_path: parent_path, meta: meta2,
|
117
|
+
@builder.build(parent_path: parent_path, meta: meta2, **kwargs)
|
104
118
|
end
|
105
119
|
end
|
106
120
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# 洋葱圈模型的构建器。
|
4
|
+
#
|
5
|
+
# 因为应用的底层仅使用洋葱圈模型,所以在 DSL 层面,我们需要将 before、after、around 等序列和当前 action 共同构建洋葱圈模型。
|
6
|
+
# 在这个类中,仅仅列出了 before、around、after 三个方法。
|
7
|
+
# 因此第一步,当前执行的 action 会用 before 逻辑代替。
|
8
|
+
# 其次,有必要声明 before、after、around 的执行顺序。before 和 after 的顺序关系容易理解,重点是要关注 around 的执行顺序。
|
9
|
+
# around 的前半部分与 before 的关系:按照声明的顺序执行。
|
10
|
+
# around 与 after 的关系:after 序列会在之前执行,然后是 around 序列的后半部分。
|
11
|
+
|
12
|
+
require_relative '../application/linked_action'
|
13
|
+
|
14
|
+
module Meta
|
15
|
+
module RouteDSL
|
16
|
+
class AroundActionBuilder
|
17
|
+
def initialize
|
18
|
+
@before = []
|
19
|
+
@after = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def around(&block)
|
23
|
+
@before << block
|
24
|
+
end
|
25
|
+
|
26
|
+
def before(&block)
|
27
|
+
@before << Proc.new do |next_action|
|
28
|
+
self.instance_exec(&block)
|
29
|
+
next_action.execute(self) if next_action
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def after(&block)
|
34
|
+
# 在洋葱圈模型中,先声明的 after 逻辑会在最后执行,因此为了保证 after 逻辑的执行顺序
|
35
|
+
@after.unshift(Proc.new do |next_action|
|
36
|
+
next_action.execute(self) if next_action
|
37
|
+
self.instance_exec(&block)
|
38
|
+
end)
|
39
|
+
end
|
40
|
+
|
41
|
+
def build
|
42
|
+
# 从后向前构建
|
43
|
+
(@before + @after).reverse.reduce(nil) do |following, p|
|
44
|
+
LinkedAction.new(p, following)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# 使用 before、after、around 系列和当前 action 共同构建洋葱圈模型。
|
49
|
+
# Note: 该方法可能被废弃!
|
50
|
+
#
|
51
|
+
# 构建成功后,执行顺序是:
|
52
|
+
#
|
53
|
+
# - before 序列
|
54
|
+
# - around 序列的前半部分
|
55
|
+
# - action
|
56
|
+
# - around 序列的后半部分
|
57
|
+
# - after 序列
|
58
|
+
#
|
59
|
+
def self.build(before: [], after: [], around: [], action: nil)
|
60
|
+
builder = AroundActionBuilder.new
|
61
|
+
|
62
|
+
# 首先构建 before 序列,保证它最先执行
|
63
|
+
builder.around do |next_action|
|
64
|
+
before.each { |p| self.instance_exec(&p) }
|
65
|
+
next_action.execute(self)
|
66
|
+
end unless before.empty?
|
67
|
+
# 然后构建 after 序列,保证它最后执行
|
68
|
+
builder.around do |next_action|
|
69
|
+
next_action.execute(self)
|
70
|
+
after.each { |p| self.instance_exec(&p) }
|
71
|
+
end unless after.empty?
|
72
|
+
# 接着应用洋葱圈模型,依次构建 around 序列、action
|
73
|
+
around.each { |p| builder.around(&p) }
|
74
|
+
builder.around { self.instance_exec(&action) } unless action.nil?
|
75
|
+
|
76
|
+
builder.build
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -7,6 +7,7 @@ require_relative 'helpers'
|
|
7
7
|
require_relative 'chain_builder'
|
8
8
|
require_relative 'action_builder'
|
9
9
|
require_relative 'meta_builder'
|
10
|
+
require_relative 'around_action_builder'
|
10
11
|
|
11
12
|
module Meta
|
12
13
|
module RouteDSL
|
@@ -24,7 +25,7 @@ module Meta
|
|
24
25
|
instance_exec &block if block_given?
|
25
26
|
end
|
26
27
|
|
27
|
-
def build(parent_path: '', meta: {},
|
28
|
+
def build(parent_path: '', meta: {}, callbacks: {})
|
28
29
|
# 合并 meta 时不仅仅是覆盖,比如 parameters 参数需要合并
|
29
30
|
meta2 = (meta || {}).merge(@meta_builder.build)
|
30
31
|
if meta[:parameters] && meta2[:parameters]
|
@@ -45,15 +46,23 @@ module Meta
|
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
48
|
-
#
|
49
|
-
|
50
|
-
|
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
|
51
60
|
|
52
61
|
Route.new(
|
53
62
|
path: @path,
|
54
63
|
method: @method,
|
55
64
|
meta: meta2,
|
56
|
-
|
65
|
+
action: action
|
57
66
|
)
|
58
67
|
end
|
59
68
|
|
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.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yetrun
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: 一个 Web API 框架,该框架采用定义元信息的方式编写 API,并同步生成 API 文档
|
14
14
|
email:
|
@@ -81,6 +81,7 @@ files:
|
|
81
81
|
- lib/meta/application.rb
|
82
82
|
- lib/meta/application/application.rb
|
83
83
|
- lib/meta/application/execution.rb
|
84
|
+
- lib/meta/application/linked_action.rb
|
84
85
|
- lib/meta/application/metadata.rb
|
85
86
|
- lib/meta/application/parameters.rb
|
86
87
|
- lib/meta/application/path_matching_mod.rb
|
@@ -110,6 +111,7 @@ files:
|
|
110
111
|
- lib/meta/rails.rb
|
111
112
|
- lib/meta/route_dsl/action_builder.rb
|
112
113
|
- lib/meta/route_dsl/application_builder.rb
|
114
|
+
- lib/meta/route_dsl/around_action_builder.rb
|
113
115
|
- lib/meta/route_dsl/chain_builder.rb
|
114
116
|
- lib/meta/route_dsl/helpers.rb
|
115
117
|
- lib/meta/route_dsl/meta_builder.rb
|