meta-api 0.0.1 → 0.0.2
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/.gitignore +1 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/README.md +6 -2
- data/docs//346/225/231/347/250/213.md +32 -2
- data/lib/meta/api.rb +1 -0
- data/lib/meta/application/execution.rb +2 -2
- data/lib/meta/config.rb +17 -0
- data/lib/meta/entity.rb +0 -1
- data/lib/meta/json_schema/builders/object_schema_builder.rb +7 -7
- data/lib/meta/json_schema/schemas/base_schema.rb +33 -3
- data/lib/meta/json_schema/schemas/object_schema.rb +7 -1
- data/lib/meta/json_schema/support/schema_options.rb +3 -1
- data/lib/meta/utils/kwargs.rb +91 -0
- data/meta-api.gemspec +1 -1
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7364c618306dc9d04e6e08784670157e64b57f3f90b1828a259ba713268bb281
|
4
|
+
data.tar.gz: 00d25192ac7e4b4c733f1aa0d44a2424290d2bc4e290ca99d810686b744ec102
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62eabc56bc4ef76f76cb131f5b901a32875e12b8905857529307dee1acd0a4a354e2822f622f9ef43939444e89e08933e025404050a4347a77fbf169f8a7fe9f
|
7
|
+
data.tar.gz: 6692c9733a12d3acccc18d1cf6227a084c926dfd69b4acaae872e553683b4e70ca85d1aca874d87c169be1c2a68ca51e62ede1c142ef070829f508b7c2820426
|
data/CHANGELOG.md
ADDED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -9,7 +9,7 @@ Meta 框架是一个适用于 Web API 的后端框架,采用 Ruby 语言编写
|
|
9
9
|
你可直接使用我的脚手架项目上手体验:
|
10
10
|
|
11
11
|
```bash
|
12
|
-
$ git clone https://github.com/yetrun/web-frame-example
|
12
|
+
$ git clone https://github.com/yetrun/web-frame-example.git
|
13
13
|
```
|
14
14
|
|
15
15
|
## 安装
|
@@ -17,7 +17,7 @@ $ git clone https://github.com/yetrun/web-frame-example
|
|
17
17
|
在 Gemfile 中添加:
|
18
18
|
|
19
19
|
```ruby
|
20
|
-
gem 'meta-api',
|
20
|
+
gem 'meta-api', '~> 0.0.1' # Meta 框架处于快速开发阶段,引入时应尽量固定版本
|
21
21
|
```
|
22
22
|
|
23
23
|
然后在 Ruby 代码中引用:
|
@@ -144,6 +144,10 @@ run NotesAPI
|
|
144
144
|
- [教程](docs/教程.md)
|
145
145
|
- [索引](docs/索引.md)
|
146
146
|
|
147
|
+
## 支持
|
148
|
+
|
149
|
+
加 QQ 群(489579810)可获得实时答疑。
|
150
|
+
|
147
151
|
## License
|
148
152
|
|
149
153
|
LGPL-2.1
|
@@ -1032,6 +1032,27 @@ action do
|
|
1032
1032
|
end
|
1033
1033
|
```
|
1034
1034
|
|
1035
|
+
### 多态参数和返回值
|
1036
|
+
|
1037
|
+
定义属性时可定义多态类型,`using` 选项可接受一个块,它根据值来返回指定的类型:
|
1038
|
+
|
1039
|
+
```ruby
|
1040
|
+
property :target, using: ->(value) {
|
1041
|
+
# 根据 value.target_type 值返回实体类型
|
1042
|
+
# 例如,value.target_type == 'UserEntity',将返回 UserEntity 类
|
1043
|
+
value.target_type.constantize
|
1044
|
+
}
|
1045
|
+
```
|
1046
|
+
|
1047
|
+
或者接受一个 Hash,这时可提供 `one_of` 选项为文档生成提供加成:
|
1048
|
+
|
1049
|
+
```ruby
|
1050
|
+
property :animal, using: {
|
1051
|
+
one_of: [CatEntity, DogEntity, PigEntity],
|
1052
|
+
resolve: ->(value) { value.animal_type.constantize }
|
1053
|
+
}
|
1054
|
+
```
|
1055
|
+
|
1035
1056
|
## 生成文档
|
1036
1057
|
|
1037
1058
|
应用模块提供一个 `to_swagger_doc` 方法生成 Open API 规格文档,该文档可被 Swagger UI 或基于 Swagger UI 的引擎渲染。
|
@@ -1058,9 +1079,18 @@ DemoApp.to_swagger_doc(
|
|
1058
1079
|
>
|
1059
1080
|
> 了解 [Swagger UI](https://swagger.io/tools/swagger-ui/).
|
1060
1081
|
|
1061
|
-
##
|
1082
|
+
## 全局配置
|
1083
|
+
|
1084
|
+
### 关闭渲染时验证
|
1085
|
+
|
1086
|
+
默认情况下渲染时也会执行验证,以保证渲染的数据有效性,尽快发现错误。这在开发环境有用,但在生产环境没有必要。可使用下面的代码关闭渲染时的验证:
|
1062
1087
|
|
1063
|
-
|
1088
|
+
```ruby
|
1089
|
+
Meta.config.render_type_conversion = false # 渲染时不执行类型转换
|
1090
|
+
Meta.config.render_validation = false # 渲染时不执行数据验证
|
1091
|
+
```
|
1092
|
+
|
1093
|
+
## 特殊用法举例
|
1064
1094
|
|
1065
1095
|
### 路由中实体定义的特殊用法
|
1066
1096
|
|
data/lib/meta/api.rb
CHANGED
@@ -83,7 +83,7 @@ module Meta
|
|
83
83
|
end.to_h.freeze
|
84
84
|
end
|
85
85
|
|
86
|
-
#
|
86
|
+
# parse_params 不再解析参数了,而只是设置 @params_schema,并清理父路由解析的变量
|
87
87
|
def parse_params(params_schema)
|
88
88
|
@params_schema = params_schema
|
89
89
|
end
|
@@ -107,7 +107,7 @@ module Meta
|
|
107
107
|
options = {}
|
108
108
|
end
|
109
109
|
|
110
|
-
new_hash = entity_schema.filter(hash, **options, execution: self, stage: :render)
|
110
|
+
new_hash = entity_schema.filter(hash, **options, execution: self, stage: :render, validation: ::Meta.config.render_validation, type_conversion: ::Meta.config.render_type_conversion)
|
111
111
|
response.body = [JSON.generate(new_hash)]
|
112
112
|
else
|
113
113
|
# 渲染多键值结点
|
data/lib/meta/config.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Meta
|
4
|
+
class Config
|
5
|
+
attr_accessor :render_validation, :render_type_conversion
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@render_type_conversion = true
|
9
|
+
@render_validation = true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
@config = Config.new
|
14
|
+
class << self
|
15
|
+
attr_reader :config
|
16
|
+
end
|
17
|
+
end
|
data/lib/meta/entity.rb
CHANGED
@@ -42,21 +42,21 @@ module Meta
|
|
42
42
|
|
43
43
|
def property(name, options = {}, &block)
|
44
44
|
name = name.to_sym
|
45
|
+
# REVIEW: 为何要 dup,删掉试试
|
45
46
|
options = options.dup
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
@properties[name] = SchemaBuilderTool.build(options, &block)
|
51
|
-
elsif block.respond_to?(:to_schema)
|
52
|
-
schema = block.to_schema
|
48
|
+
using = options[:using]
|
49
|
+
if using.respond_to?(:to_schema)
|
50
|
+
schema = using.to_schema
|
53
51
|
if options[:type] == 'array'
|
54
52
|
@properties[name] = ArraySchema.new(schema, options)
|
55
53
|
else
|
56
54
|
@properties[name] = schema.dup(options)
|
57
55
|
end
|
56
|
+
elsif using.is_a?(Proc) || using.is_a?(Hash) || using.nil?
|
57
|
+
@properties[name] = SchemaBuilderTool.build(options, &block)
|
58
58
|
else
|
59
|
-
raise "
|
59
|
+
raise "非法的 `using` 选项,应传递具有 `to_schema` 方法(如 Entity、Schema 等)或 Hash、Proc(动态生成 Schema)。当前传递:#{block}"
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -43,6 +43,22 @@ module Meta
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def filter(value, user_options = {})
|
46
|
+
user_options = Utils::KeywordArgs.check(
|
47
|
+
args: user_options,
|
48
|
+
schema: {
|
49
|
+
stage: nil,
|
50
|
+
execution: nil,
|
51
|
+
object_value: nil,
|
52
|
+
type_conversion: true,
|
53
|
+
validation: true,
|
54
|
+
|
55
|
+
# 以下三个是 ObjectSchema 需要的选项
|
56
|
+
discard_missing: false,
|
57
|
+
exclude: [],
|
58
|
+
scope: []
|
59
|
+
}
|
60
|
+
)
|
61
|
+
|
46
62
|
stage_options = options(user_options[:stage])
|
47
63
|
|
48
64
|
value = resolve_value(user_options) if stage_options[:value]
|
@@ -50,9 +66,10 @@ module Meta
|
|
50
66
|
value = stage_options[:default] if value.nil? && stage_options.key?(:default)
|
51
67
|
value = stage_options[:convert].call(value) if stage_options[:convert]
|
52
68
|
|
53
|
-
#
|
69
|
+
# 第一步,转化值。
|
70
|
+
# 需要注意的是,对象也可能被转换,因为并没有深层次的结构被声明。
|
54
71
|
type = stage_options[:type]
|
55
|
-
unless type.nil? || value.nil?
|
72
|
+
unless user_options[:type_conversion] == false || type.nil? || value.nil?
|
56
73
|
begin
|
57
74
|
value = JsonSchema::TypeConverter.convert_value(value, type)
|
58
75
|
rescue JsonSchema::TypeConvertError => e
|
@@ -60,7 +77,14 @@ module Meta
|
|
60
77
|
end
|
61
78
|
end
|
62
79
|
|
63
|
-
|
80
|
+
# 第二步,做校验。
|
81
|
+
validate!(value, stage_options) unless user_options[:validation] == false
|
82
|
+
|
83
|
+
# 第三步,如果使用了 using 块,需要进一步解析
|
84
|
+
if stage_options[:using] && stage_options[:using].is_a?(Hash)
|
85
|
+
schema = stage_options[:using][:resolve].call(value).to_schema
|
86
|
+
value = schema.filter(value, user_options)
|
87
|
+
end
|
64
88
|
|
65
89
|
value
|
66
90
|
end
|
@@ -89,6 +113,12 @@ module Meta
|
|
89
113
|
schema[:type] = stage_options[:type] if stage_options[:type]
|
90
114
|
schema[:description] = stage_options[:description] if stage_options[:description]
|
91
115
|
schema[:enum] = stage_options[:allowable] if stage_options[:allowable]
|
116
|
+
if stage_options[:using] && stage_options[:using].is_a?(Hash)
|
117
|
+
using_options = stage_options[:using]
|
118
|
+
schema[:oneOf] = using_options[:one_of].map do |schema|
|
119
|
+
schema.to_schema.to_schema_doc(user_options)
|
120
|
+
end if using_options[:one_of]
|
121
|
+
end
|
92
122
|
|
93
123
|
schema
|
94
124
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../../utils/kwargs'
|
4
|
+
|
3
5
|
module Meta
|
4
6
|
module JsonSchema
|
5
7
|
class ObjectSchema < BaseSchema
|
@@ -93,8 +95,12 @@ module Meta
|
|
93
95
|
# > 则该 Schema 生成文档时会使用 $ref 格式。除非 to_ref 选项设置为 false.
|
94
96
|
#
|
95
97
|
def to_schema_doc(user_options)
|
98
|
+
Utils::KeywordArgs.check(
|
99
|
+
args: user_options,
|
100
|
+
schema: { stage: nil, scope: nil, to_ref: false, schemas: nil }
|
101
|
+
)
|
102
|
+
|
96
103
|
stage = user_options[:stage]
|
97
|
-
# HACK: 标准化选项的工作进行得怎样?
|
98
104
|
locked_scopes = (locked_options || {})[:scope] || []
|
99
105
|
locked_scopes = [locked_scopes] unless locked_scope.nil? && locked_scopes.is_a?(Array)
|
100
106
|
schema_name = @schema_name_resolver.call(stage, locked_scopes)
|
@@ -8,7 +8,7 @@ module Meta
|
|
8
8
|
required: false
|
9
9
|
}
|
10
10
|
@allowable_options = (
|
11
|
-
%i[type description in value using default presenter convert scope items] +
|
11
|
+
%i[type description in value using default presenter convert scope items using] +
|
12
12
|
@default_options.keys +
|
13
13
|
JsonSchema::Validators.keys
|
14
14
|
).uniq
|
@@ -35,6 +35,8 @@ module Meta
|
|
35
35
|
# 只要 options 中设置为 nil 的选项没有明确的意义,则下行代码是永远有效的
|
36
36
|
options = (@default_options.compact).merge(options.compact)
|
37
37
|
options[:scope] = [options[:scope]] unless options[:scope].is_a?(Array)
|
38
|
+
# TODO: 更好的规范选项的方式,以及如何检查深层次嵌套下参数类型的错误
|
39
|
+
options[:using] = { resolve: options[:using] } if options[:using].is_a?(Proc)
|
38
40
|
if options[:using]
|
39
41
|
if options[:type].nil?
|
40
42
|
options[:type] = 'object'
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# 运行时检查关键字参数。
|
4
|
+
#
|
5
|
+
# 在 Ruby 3 中,关键字参数有所变化。简单来说,关键字参数和 Hash 类型不再自动转化,并且一般情况下推荐使用关键字参数。
|
6
|
+
# 但关键字参数还是有稍稍不足之处,比如在做一些复杂的关键字参数定义时。
|
7
|
+
#
|
8
|
+
# 这个文件编写了一个方法,帮助我们在运行时检查关键字参数。这样,我们就可以像下面这样笼统的方式定义参数,不必用明确的关
|
9
|
+
# 键字参数名称。
|
10
|
+
#
|
11
|
+
# def method_name(x, y, z, **kwargs); end
|
12
|
+
# def method_name(x, y, z, kwargs={}); end
|
13
|
+
#
|
14
|
+
# 使用示例:
|
15
|
+
#
|
16
|
+
# # 返回 { x: 1, y: 2, z: 3 }
|
17
|
+
# Meta::Utils::KeywordArgs.check(args: { x: 1, y: 2 }, schema: [:x, :y, { z: 3 }])
|
18
|
+
#
|
19
|
+
# # 返回 { x: 1, y: 2, z: 4 }
|
20
|
+
# Meta::Utils::KeywordArgs.check(args: { x: 1, y: 2, z: 4 }, schema: [:x, :y, { z: 3 }])
|
21
|
+
#
|
22
|
+
# # Error: `x` is required
|
23
|
+
# Meta::Utils::KeywordArgs.check(args: { y: 2, z: 3 }, schema: [:x, :y, { z: 3 }])
|
24
|
+
#
|
25
|
+
# # Error: `a` is not allowed
|
26
|
+
# Meta::Utils::KeywordArgs.check(args: { a: 1, y: 2, z: 3 }, schema: [:x, :y, { z: 3 }])
|
27
|
+
|
28
|
+
module Meta
|
29
|
+
module Utils
|
30
|
+
class KeywordArgs
|
31
|
+
class << self
|
32
|
+
def check(args:, schema:)
|
33
|
+
schemas = build_schemas(schema)
|
34
|
+
|
35
|
+
# 不接受额外的关键字参数
|
36
|
+
extras = args.keys - schemas.keys
|
37
|
+
raise "不接受额外的关键字参数:#{extras.join(', ')}" unless extras.empty?
|
38
|
+
|
39
|
+
# 通过 schema 导出关键字参数
|
40
|
+
missing = []
|
41
|
+
result = schemas.map do |name, spec|
|
42
|
+
if args.include?(name)
|
43
|
+
[name, args[name]]
|
44
|
+
elsif spec.include?(:default)
|
45
|
+
[name, spec[:default]]
|
46
|
+
else
|
47
|
+
missing << name
|
48
|
+
end
|
49
|
+
end.to_h
|
50
|
+
|
51
|
+
# 检查以上导出过程中是否找到缺失的参数
|
52
|
+
if missing.empty?
|
53
|
+
result
|
54
|
+
else
|
55
|
+
raise "缺失必要的关键字参数:#{missing.join(', ')}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def build_schemas(spec)
|
62
|
+
if spec.is_a?(Array)
|
63
|
+
build_schemas_from_array(spec)
|
64
|
+
elsif spec.is_a?(Hash)
|
65
|
+
build_schemas_from_hash(spec)
|
66
|
+
elsif spec.is_a?(Symbol)
|
67
|
+
build_schemas_from_symbol(spec)
|
68
|
+
else
|
69
|
+
raise "未知的参数类型:#{spec.class}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def build_schemas_from_array(spec_array)
|
74
|
+
spec_array.inject({}) do |accumulated, val|
|
75
|
+
accumulated.merge!(build_schemas(val))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def build_schemas_from_hash(spec_hash)
|
80
|
+
spec_hash.transform_values do |val|
|
81
|
+
{ default: val }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_schemas_from_symbol(spec_symbol)
|
86
|
+
{ spec_symbol => {} }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
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.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yetrun
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: 一个 Web API 框架,该框架采用定义元信息的方式编写 API,并同步生成 API 文档
|
14
14
|
email:
|
@@ -20,6 +20,7 @@ files:
|
|
20
20
|
- ".autoenv.zsh"
|
21
21
|
- ".gitignore"
|
22
22
|
- ".rubocop.yml"
|
23
|
+
- CHANGELOG.md
|
23
24
|
- Gemfile
|
24
25
|
- Gemfile.lock
|
25
26
|
- LICENSE.txt
|
@@ -41,6 +42,7 @@ files:
|
|
41
42
|
- lib/meta/application/meta.rb
|
42
43
|
- lib/meta/application/path_matching_mod.rb
|
43
44
|
- lib/meta/application/route.rb
|
45
|
+
- lib/meta/config.rb
|
44
46
|
- lib/meta/entity.rb
|
45
47
|
- lib/meta/errors.rb
|
46
48
|
- lib/meta/json_schema/builders/array_schema_builder.rb
|
@@ -65,6 +67,7 @@ files:
|
|
65
67
|
- lib/meta/route_dsl/route_builder.rb
|
66
68
|
- lib/meta/route_dsl/uniformed_params_builder.rb
|
67
69
|
- lib/meta/swagger_doc.rb
|
70
|
+
- lib/meta/utils/kwargs.rb
|
68
71
|
- lib/meta/utils/path.rb
|
69
72
|
- meta-api.gemspec
|
70
73
|
homepage: https://github.com/yetrun/web-frame
|
@@ -74,7 +77,7 @@ metadata:
|
|
74
77
|
allowed_push_host: https://rubygems.org
|
75
78
|
homepage_uri: https://github.com/yetrun/web-frame
|
76
79
|
source_code_uri: https://github.com/yetrun/web-frame.git
|
77
|
-
post_install_message:
|
80
|
+
post_install_message:
|
78
81
|
rdoc_options: []
|
79
82
|
require_paths:
|
80
83
|
- lib
|
@@ -89,8 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
92
|
- !ruby/object:Gem::Version
|
90
93
|
version: '0'
|
91
94
|
requirements: []
|
92
|
-
rubygems_version: 3.3.
|
93
|
-
signing_key:
|
95
|
+
rubygems_version: 3.3.7
|
96
|
+
signing_key:
|
94
97
|
specification_version: 4
|
95
98
|
summary: 一个 Web API 框架
|
96
99
|
test_files: []
|