meta-api 0.0.4 → 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 +5 -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 +8 -9
- data/examples/rails_app/Gemfile.lock +1 -1
- data/lib/meta/application/execution.rb +1 -1
- data/lib/meta/application/metadata.rb +1 -1
- data/lib/meta/application/parameters.rb +2 -2
- data/lib/meta/json_schema/support/type_converter.rb +13 -11
- data/lib/meta/route_dsl/application_builder.rb +18 -10
- data/lib/meta/route_dsl/around_action_builder.rb +30 -3
- data/lib/meta/route_dsl/route_builder.rb +10 -4
- data/meta-api.gemspec +1 -1
- metadata +2 -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
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}`"
|
@@ -147,14 +147,14 @@ end
|
|
147
147
|
```ruby
|
148
148
|
class DemoApp < Meta::Application
|
149
149
|
before { puts 1 }
|
150
|
-
before { puts 2 }
|
151
150
|
around { |next_action|
|
152
|
-
puts
|
151
|
+
puts 2
|
153
152
|
next_action.execute(self)
|
154
|
-
puts
|
153
|
+
puts 7
|
155
154
|
}
|
155
|
+
before { puts 3 }
|
156
|
+
after { puts 5 }
|
156
157
|
after { puts 6 }
|
157
|
-
after { puts 7 }
|
158
158
|
|
159
159
|
get '/request' do
|
160
160
|
puts 4
|
@@ -164,11 +164,10 @@ end
|
|
164
164
|
|
165
165
|
所有钩子的执行顺序是:
|
166
166
|
|
167
|
-
1.
|
168
|
-
2.
|
169
|
-
3.
|
170
|
-
4.
|
171
|
-
5. 最后执行 `after` 钩子,按照定义的顺序执行。
|
167
|
+
1. `before` 钩子和 `around` 钩子的前半部分,按照定义的顺序执行
|
168
|
+
2. 然后执行路由方法。
|
169
|
+
3. 然后执行 `after` 钩子,按照定义的顺序执行。
|
170
|
+
4. 最后执行 `around` 钩子的后半部分,按照定义的逆序执行。
|
172
171
|
|
173
172
|
### 异常拦截
|
174
173
|
|
@@ -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
|
@@ -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
|
@@ -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,7 +10,7 @@ module Meta
|
|
10
10
|
|
11
11
|
def initialize(prefix = nil, &block)
|
12
12
|
@mod_prefix = prefix
|
13
|
-
@callbacks =
|
13
|
+
@callbacks = []
|
14
14
|
@error_guards = []
|
15
15
|
@meta_builder = MetaBuilder.new
|
16
16
|
@mod_builders = []
|
@@ -19,7 +19,7 @@ module Meta
|
|
19
19
|
instance_exec &block if block_given?
|
20
20
|
end
|
21
21
|
|
22
|
-
def build(parent_path: '', meta: {}, callbacks:
|
22
|
+
def build(parent_path: '', meta: {}, callbacks: [])
|
23
23
|
# 合并 meta 时不仅仅是覆盖,比如 parameters 参数需要合并
|
24
24
|
meta2 = (meta || {}).merge(@meta_builder.build)
|
25
25
|
if meta[:parameters] && meta2[:parameters]
|
@@ -27,11 +27,10 @@ module Meta
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# 构建子模块
|
30
|
-
callbacks
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
}
|
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
|
35
34
|
mods = @mod_builders.map { |builder| builder.build(parent_path: Utils::Path.join(parent_path, @mod_prefix), meta: meta2, callbacks: callbacks) }
|
36
35
|
|
37
36
|
Application.new(
|
@@ -66,15 +65,24 @@ module Meta
|
|
66
65
|
|
67
66
|
# 定义模块内的公共逻辑
|
68
67
|
def before(&block)
|
69
|
-
@callbacks
|
68
|
+
@callbacks << {
|
69
|
+
lifecycle: :before,
|
70
|
+
proc: block
|
71
|
+
}
|
70
72
|
end
|
71
73
|
|
72
74
|
def after(&block)
|
73
|
-
@callbacks
|
75
|
+
@callbacks << {
|
76
|
+
lifecycle: :after,
|
77
|
+
proc: block
|
78
|
+
}
|
74
79
|
end
|
75
80
|
|
76
81
|
def around(&block)
|
77
|
-
@callbacks
|
82
|
+
@callbacks << {
|
83
|
+
lifecycle: :around,
|
84
|
+
proc: block
|
85
|
+
}
|
78
86
|
end
|
79
87
|
|
80
88
|
def rescue_error(error_class, &block)
|
@@ -1,26 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
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
|
+
|
3
12
|
require_relative '../application/linked_action'
|
4
13
|
|
5
14
|
module Meta
|
6
15
|
module RouteDSL
|
7
16
|
class AroundActionBuilder
|
8
17
|
def initialize
|
9
|
-
@
|
18
|
+
@before = []
|
19
|
+
@after = []
|
10
20
|
end
|
11
21
|
|
12
22
|
def around(&block)
|
13
|
-
@
|
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)
|
14
39
|
end
|
15
40
|
|
16
41
|
def build
|
17
42
|
# 从后向前构建
|
18
|
-
@
|
43
|
+
(@before + @after).reverse.reduce(nil) do |following, p|
|
19
44
|
LinkedAction.new(p, following)
|
20
45
|
end
|
21
46
|
end
|
22
47
|
|
23
48
|
# 使用 before、after、around 系列和当前 action 共同构建洋葱圈模型。
|
49
|
+
# Note: 该方法可能被废弃!
|
50
|
+
#
|
24
51
|
# 构建成功后,执行顺序是:
|
25
52
|
#
|
26
53
|
# - before 序列
|
@@ -47,10 +47,16 @@ module Meta
|
|
47
47
|
end
|
48
48
|
|
49
49
|
# 构建洋葱圈模型的 LinkedAction
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
54
60
|
|
55
61
|
Route.new(
|
56
62
|
path: @path,
|
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:
|