meta-api 0.0.8 → 0.1.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 +4 -4
- data/CHANGELOG.md +22 -0
- data/lib/meta/application/execution.rb +19 -49
- data/lib/meta/application/metadata.rb +68 -15
- data/lib/meta/application/responses.rb +32 -0
- data/lib/meta/application/route.rb +4 -26
- data/lib/meta/config.rb +2 -3
- data/lib/meta/entity.rb +2 -3
- data/lib/meta/json_schema/builders/object_schema_builder.rb +45 -40
- data/lib/meta/json_schema/builders/schema_builder_tool.rb +2 -5
- data/lib/meta/json_schema/schemas/array_schema.rb +24 -14
- data/lib/meta/json_schema/schemas/base_schema.rb +107 -37
- data/lib/meta/json_schema/schemas/named_properties.rb +37 -0
- data/lib/meta/json_schema/schemas/object_schema.rb +58 -20
- data/lib/meta/json_schema/schemas/properties.rb +27 -76
- data/lib/meta/json_schema/schemas/ref_schema.rb +36 -9
- data/lib/meta/json_schema/schemas/scoping_schema.rb +38 -0
- data/lib/meta/json_schema/schemas/staging_schema.rb +59 -0
- data/lib/meta/json_schema/schemas/unsupported_schema.rb +22 -0
- data/lib/meta/json_schema/support/errors.rb +3 -0
- data/lib/meta/json_schema/support/json_object.rb +37 -0
- data/lib/meta/json_schema/support/schema_options.rb +0 -9
- data/lib/meta/json_schema/support/scope_matcher.rb +29 -0
- data/lib/meta/json_schema/support/type_converter.rb +3 -24
- data/lib/meta/route_dsl/meta_builder.rb +2 -2
- data/lib/meta/swagger_doc.rb +2 -25
- data/lib/meta/utils/kwargs/builder.rb +5 -3
- data/lib/meta/utils/route_dsl_builders.rb +2 -1
- data/meta-api.gemspec +6 -5
- metadata +75 -111
- data/.autoenv.zsh +0 -1
- data/.gitignore +0 -7
- data/.rubocop.yml +0 -28
- data/Gemfile +0 -19
- data/Gemfile.lock +0 -68
- data/README.md +0 -166
- data/Rakefile +0 -3
- data/docs/Rails.md +0 -61
- data/docs//345/220/215/347/247/260/347/224/261/346/235/245.md +0 -7
- data/docs//345/246/202/344/275/225/350/264/241/347/214/256.md +0 -10
- data/docs//346/225/231/347/250/213.md +0 -1708
- data/docs//347/264/242/345/274/225.md +0 -183
- data/examples/lobster.rb +0 -71
- data/examples/rack_app/README.md +0 -3
- data/examples/rack_app/config.ru +0 -6
- data/examples/rack_app/hello.rb +0 -6
- data/examples/rack_app/timing.rb +0 -15
- data/examples/rails_app/.gitattributes +0 -5
- data/examples/rails_app/.gitignore +0 -23
- data/examples/rails_app/.rspec +0 -1
- data/examples/rails_app/.ruby-version +0 -1
- data/examples/rails_app/Gemfile +0 -29
- data/examples/rails_app/Gemfile.lock +0 -190
- data/examples/rails_app/README.md +0 -11
- data/examples/rails_app/Rakefile +0 -6
- data/examples/rails_app/app/controllers/application_controller.rb +0 -7
- data/examples/rails_app/app/controllers/concerns/.keep +0 -0
- data/examples/rails_app/app/controllers/data_controller.rb +0 -63
- data/examples/rails_app/app/controllers/swagger_controller.rb +0 -13
- data/examples/rails_app/app/models/concerns/.keep +0 -0
- data/examples/rails_app/bin/rails +0 -4
- data/examples/rails_app/bin/rake +0 -4
- data/examples/rails_app/bin/setup +0 -25
- data/examples/rails_app/config/application.rb +0 -39
- data/examples/rails_app/config/boot.rb +0 -3
- data/examples/rails_app/config/credentials.yml.enc +0 -1
- data/examples/rails_app/config/environment.rb +0 -5
- data/examples/rails_app/config/environments/development.rb +0 -51
- data/examples/rails_app/config/environments/production.rb +0 -65
- data/examples/rails_app/config/environments/test.rb +0 -50
- data/examples/rails_app/config/initializers/cors.rb +0 -16
- data/examples/rails_app/config/initializers/filter_parameter_logging.rb +0 -8
- data/examples/rails_app/config/initializers/inflections.rb +0 -16
- data/examples/rails_app/config/initializers/meta_rails_plugin.rb +0 -3
- data/examples/rails_app/config/locales/en.yml +0 -33
- data/examples/rails_app/config/puma.rb +0 -43
- data/examples/rails_app/config/routes.rb +0 -13
- data/examples/rails_app/config.ru +0 -6
- data/examples/rails_app/lib/tasks/.keep +0 -0
- data/examples/rails_app/log/.keep +0 -0
- data/examples/rails_app/public/robots.txt +0 -1
- data/examples/rails_app/spec/data_controller_spec.rb +0 -60
- data/examples/rails_app/spec/rails_helper.rb +0 -55
- data/examples/rails_app/spec/spec_helper.rb +0 -94
- data/examples/rails_app/spec/swagger_controller_spec.rb +0 -13
- data/examples/rails_app/tmp/.keep +0 -0
- data/examples/rails_app/tmp/pids/.keep +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf3fb639df71a5353328e898711c9758c5e5fd3dd1760409ae823fbd0a7852e2
|
4
|
+
data.tar.gz: ea5dd728c3691fcf542d1085fd21d787d727c97b588652de3ffdd53b67512437
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a9f0a02d8091f77e7f9c2ea0ea67b86f1877327a7b75a7f4342fecd7f2822f9bde2ded59c20cb1c3227eb88a4b6b098c9438710a6a8eedcf4c8defb2f37c572
|
7
|
+
data.tar.gz: 2e7911858914bd1b34ff60007f48757f6afb1183c59d15d9e490fb5fe781244522d5500251dcbdbb02fc5f26a17d48ab82bbc2f6dd4368c2540f97bb351f6da3
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
# 更新日志
|
2
2
|
|
3
|
+
## 0.1.0(2023 年 8 月 5 日)
|
4
|
+
|
5
|
+
1. 删除 `on:` 选项。
|
6
|
+
2. `type_conversion` 为 `false` 时不影响对象和数组类型的转化。
|
7
|
+
3. 修复 `ref:` 嵌套造成的文档问题。
|
8
|
+
4. 将 HTTP Method 的 scope 添加 `$` 符号前缀,如 `$get`、`$post` 等。
|
9
|
+
5. `Meta.config` 去掉了 `default_locked_scope` 的配置项。
|
10
|
+
|
11
|
+
## 0.0.9(2023 年 7 月 22 日)
|
12
|
+
|
13
|
+
1. JsonSchema 添加 before:、after: 选项,用于在过滤前后执行一些操作。
|
14
|
+
2. 新添加一个 if: 选项用在属性上,它能够作为一个条件,当条件为 false 时,该属性不会被包含在结果内。
|
15
|
+
3. 属性的 scope: 选项改名为 on: 选项。
|
16
|
+
4. MetaBuilder 添加一个 scope 宏。
|
17
|
+
5. Meta.config 的 JsonSchema 相关配置项改名。
|
18
|
+
6. 优化异常的捕获。
|
19
|
+
|
20
|
+
## 0.0.8(2023 年 7 月 17 日)
|
21
|
+
|
22
|
+
1. `Meta.config` 添加 `initialize_configuration` 方法,用于接受若干个 Hash 初始化配置。
|
23
|
+
2. 修复 GET 请求下会将 `header` 参数覆盖为 `query` 参数的 bug.
|
24
|
+
|
3
25
|
## 0.0.7(2023 年 7 月 14 日)
|
4
26
|
|
5
27
|
1. 定义 parameters 宏时能够自动识别 `path` 参数。
|
@@ -4,8 +4,8 @@ require 'rack'
|
|
4
4
|
|
5
5
|
module Meta
|
6
6
|
class Execution
|
7
|
-
attr_reader :request, :response
|
8
|
-
attr_accessor :
|
7
|
+
attr_reader :request, :response
|
8
|
+
attr_accessor :route_meta
|
9
9
|
|
10
10
|
def initialize(request)
|
11
11
|
@request = request
|
@@ -13,6 +13,10 @@ module Meta
|
|
13
13
|
@parameters = {}
|
14
14
|
end
|
15
15
|
|
16
|
+
def parameters
|
17
|
+
@_parameters || @_parameters = route_meta.parse_parameters(self)
|
18
|
+
end
|
19
|
+
|
16
20
|
# 调用方式:
|
17
21
|
#
|
18
22
|
# - `request_body`:等价于 request_body(:keep_missing)
|
@@ -23,9 +27,9 @@ module Meta
|
|
23
27
|
|
24
28
|
case mode
|
25
29
|
when :keep_missing
|
26
|
-
@_request_body[:keep_missing] || @_request_body[:keep_missing] =
|
30
|
+
@_request_body[:keep_missing] || @_request_body[:keep_missing] = route_meta.parse_request_body(self).freeze
|
27
31
|
when :discard_missing
|
28
|
-
@_request_body[:discard_missing] || @_request_body[:discard_missing] =
|
32
|
+
@_request_body[:discard_missing] || @_request_body[:discard_missing] = route_meta.parse_request_body(self, discard_missing: true).freeze
|
29
33
|
else
|
30
34
|
raise NameError, "未知的 mode 参数:#{mode}"
|
31
35
|
end
|
@@ -45,7 +49,7 @@ module Meta
|
|
45
49
|
@_params[:raw] = parse_raw_params.freeze
|
46
50
|
else
|
47
51
|
params = parameters
|
48
|
-
params = params.merge(request_body(mode) || {}) if
|
52
|
+
params = params.merge(request_body(mode) || {}) if route_meta.request_body
|
49
53
|
@_params[mode] = params
|
50
54
|
end
|
51
55
|
|
@@ -67,16 +71,14 @@ module Meta
|
|
67
71
|
end
|
68
72
|
|
69
73
|
@renders ||= {}
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
74
|
+
raise '同一种 render 方式只能调用一次,请检查是否重复使用相同的 key 渲染,或者重复调用 render(value) 的方式' if @renders.key?(key)
|
75
|
+
if key == :__root__ && @renders.keys.any? { |key| key != :__root__ }
|
76
|
+
raise '已使用 render(:key, value) 的方式调用过,不能再使用 render(value) 的方式'
|
77
|
+
elsif key != :__root__ && @renders.key?(:__root__)
|
78
|
+
raise '已使用 render(value) 的方式调用过,不能再使用 render(:key, value) 的方式'
|
79
|
+
end
|
77
80
|
|
78
|
-
|
79
|
-
@request_body_schema = schema
|
81
|
+
@renders[key] = { value: value, options: options || {} }
|
80
82
|
end
|
81
83
|
|
82
84
|
def render_entity(entity_schema)
|
@@ -94,12 +96,7 @@ module Meta
|
|
94
96
|
options = {}
|
95
97
|
end
|
96
98
|
|
97
|
-
new_hash =
|
98
|
-
**Meta.config.json_schema_user_options,
|
99
|
-
**Meta.config.json_schema_render_stage_options,
|
100
|
-
**options,
|
101
|
-
execution: self, stage: :render
|
102
|
-
)
|
99
|
+
new_hash = route_meta.render_entity(self, entity_schema, hash, options)
|
103
100
|
response.content_type = 'application/json' if response.content_type.nil?
|
104
101
|
response.body = [JSON.generate(new_hash)]
|
105
102
|
else
|
@@ -108,13 +105,8 @@ module Meta
|
|
108
105
|
final_value = {}
|
109
106
|
renders.each do |key, render_content|
|
110
107
|
raise Errors::RenderingError, "渲染的键名 `#{key}` 不存在,请检查实体定义以确认是否有拼写错误" unless entity_schema.properties.key?(key)
|
111
|
-
schema = entity_schema.properties[key].
|
112
|
-
final_value[key] =
|
113
|
-
**Meta.config.json_schema_user_options,
|
114
|
-
**Meta.config.json_schema_render_stage_options,
|
115
|
-
**render_content[:options],
|
116
|
-
execution: self, stage: :render
|
117
|
-
)
|
108
|
+
schema = entity_schema.properties[key].staged(:render)
|
109
|
+
final_value[key] = route_meta.render_entity(self, schema, render_content[:value], render_content[:options])
|
118
110
|
rescue JsonSchema::ValidationErrors => e
|
119
111
|
# 错误信息再度绑定 key
|
120
112
|
errors.merge! e.errors.transform_keys! { |k| k.empty? ? key : "#{key}.#{k}" }
|
@@ -152,28 +144,6 @@ module Meta
|
|
152
144
|
json
|
153
145
|
end
|
154
146
|
|
155
|
-
def parse_request_body_for_replacing
|
156
|
-
request_body_schema.filter(
|
157
|
-
params(:raw),
|
158
|
-
**Meta.config.json_schema_user_options,
|
159
|
-
**Meta.config.json_schema_param_stage_options,
|
160
|
-
execution: self, stage: :param
|
161
|
-
)
|
162
|
-
rescue JsonSchema::ValidationErrors => e
|
163
|
-
raise Errors::ParameterInvalid.new(e.errors)
|
164
|
-
end
|
165
|
-
|
166
|
-
def parse_request_body_for_updating
|
167
|
-
request_body_schema.filter(
|
168
|
-
params(:raw),
|
169
|
-
**Meta.config.json_schema_user_options,
|
170
|
-
**Meta.config.json_schema_param_stage_options,
|
171
|
-
execution: self, stage: :param, discard_missing: true
|
172
|
-
)
|
173
|
-
rescue JsonSchema::ValidationErrors => e
|
174
|
-
raise Errors::ParameterInvalid.new(e.errors)
|
175
|
-
end
|
176
|
-
|
177
147
|
class Abort < Exception
|
178
148
|
end
|
179
149
|
|
@@ -1,24 +1,86 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative 'parameters'
|
4
|
+
require_relative 'responses'
|
3
5
|
|
4
6
|
module Meta
|
5
7
|
class Metadata
|
6
|
-
|
8
|
+
module ExecutionMethods
|
9
|
+
def parse_parameters(execution)
|
10
|
+
parameters.filter(execution.request)
|
11
|
+
rescue JsonSchema::ValidationErrors => e
|
12
|
+
raise Errors::ParameterInvalid.new(e.errors)
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse_request_body(execution, discard_missing: false)
|
16
|
+
method = execution.request.request_method.downcase.to_sym
|
17
|
+
request_body.filter(
|
18
|
+
execution.params(:raw),
|
19
|
+
**Meta.config.json_schema_user_options,
|
20
|
+
**Meta.config.json_schema_param_stage_user_options,
|
21
|
+
**{ execution: execution, stage: :param, scope: @scope.concat(["$#{method}"]), discard_missing: discard_missing }.compact
|
22
|
+
)
|
23
|
+
rescue JsonSchema::ValidationErrors => e
|
24
|
+
raise Errors::ParameterInvalid.new(e.errors)
|
25
|
+
end
|
26
|
+
|
27
|
+
def render_entity(execution, entity_schema, value, user_options)
|
28
|
+
method = execution.request.request_method.downcase.to_sym
|
29
|
+
entity_schema.filter(value,
|
30
|
+
**Meta.config.json_schema_user_options,
|
31
|
+
**Meta.config.json_schema_render_stage_user_options,
|
32
|
+
**{ execution: execution, stage: :render, scope: @scope.concat([method]) }.compact,
|
33
|
+
**user_options,
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_response(execution)
|
38
|
+
set_status(execution)
|
39
|
+
render_response_body(execution) if self.responses
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def set_status(execution)
|
45
|
+
response_definitions = self.responses
|
46
|
+
response = execution.response
|
47
|
+
if response.status == 0
|
48
|
+
response.status = (response_definitions&.length > 0) ? response_definitions.keys[0] : 200
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def render_response_body(execution)
|
53
|
+
response_definitions = self[:responses]
|
54
|
+
return if response_definitions.empty? # 未设定 status 宏时不需要执行 render_entity 方法
|
55
|
+
|
56
|
+
# 查找 entity schema
|
57
|
+
entity_schema = response_definitions[execution.response.status]
|
58
|
+
return if entity_schema.nil?
|
59
|
+
|
60
|
+
# 执行面向 schema 的渲染
|
61
|
+
execution.render_entity(entity_schema)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
include ExecutionMethods
|
7
66
|
|
8
|
-
|
67
|
+
attr_reader :title, :description, :tags, :parameters, :request_body, :responses, :scope
|
68
|
+
|
69
|
+
def initialize(title: nil, description: nil, tags: [], parameters: {}, request_body: nil, responses: nil, scope: nil)
|
9
70
|
@title = title
|
10
71
|
@description = description
|
11
72
|
@tags = tags
|
12
73
|
@parameters = parameters.is_a?(Parameters) ? parameters : Parameters.new(parameters)
|
13
74
|
@request_body = request_body
|
14
|
-
@responses = responses
|
75
|
+
@responses = responses.is_a?(Responses) ? responses : Responses.new(responses)
|
76
|
+
@scope = scope
|
15
77
|
end
|
16
78
|
|
17
79
|
def [](key)
|
18
80
|
send(key)
|
19
81
|
end
|
20
82
|
|
21
|
-
def generate_operation_doc(schemas)
|
83
|
+
def generate_operation_doc(schemas, scope: [])
|
22
84
|
operation_object = {}
|
23
85
|
|
24
86
|
operation_object[:summary] = title if title
|
@@ -28,7 +90,7 @@ module Meta
|
|
28
90
|
operation_object[:parameters] = parameters.to_swagger_doc
|
29
91
|
|
30
92
|
if request_body
|
31
|
-
schema = request_body.to_schema_doc(stage: :param,
|
93
|
+
schema = request_body.to_schema_doc(stage: :param, scope: self.scope + scope, schema_docs_mapping: schemas)
|
32
94
|
if schema || true
|
33
95
|
operation_object[:requestBody] = {
|
34
96
|
content: {
|
@@ -40,16 +102,7 @@ module Meta
|
|
40
102
|
end
|
41
103
|
end
|
42
104
|
|
43
|
-
operation_object[:responses] = responses.
|
44
|
-
{
|
45
|
-
description: '', # description 属性必须存在
|
46
|
-
content: schema ? {
|
47
|
-
'application/json' => {
|
48
|
-
schema: schema.to_schema_doc(stage: :render, schemas: schemas)
|
49
|
-
}
|
50
|
-
} : nil
|
51
|
-
}.compact
|
52
|
-
end unless responses.empty?
|
105
|
+
operation_object[:responses] = responses.to_swagger_doc(schemas, scope: self.scope + scope)
|
53
106
|
|
54
107
|
operation_object.compact
|
55
108
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Meta
|
4
|
+
class Responses
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :responses
|
8
|
+
|
9
|
+
def initialize(responses = {})
|
10
|
+
@responses = responses || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def_delegators :@responses, :[], :empty?, :length, :keys
|
14
|
+
|
15
|
+
def to_swagger_doc(schemas, scope:)
|
16
|
+
if responses.empty?
|
17
|
+
{ '200' => { description: '' } }
|
18
|
+
else
|
19
|
+
responses.transform_values do |schema|
|
20
|
+
{
|
21
|
+
description: '', # description 属性必须存在
|
22
|
+
content: schema ? {
|
23
|
+
'application/json' => {
|
24
|
+
schema: schema.to_schema_doc(stage: :render, scope: scope, schema_docs_mapping: schemas)
|
25
|
+
}
|
26
|
+
} : nil
|
27
|
+
}.compact
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -21,13 +21,9 @@ module Meta
|
|
21
21
|
def execute(execution, remaining_path)
|
22
22
|
path_matching.merge_path_params(remaining_path, execution.request)
|
23
23
|
|
24
|
-
execution.
|
25
|
-
execution.parse_request_body(@meta[:request_body]) if @meta[:request_body]
|
26
|
-
|
24
|
+
execution.route_meta = @meta # 解析参数的准备
|
27
25
|
action.execute(execution) if action
|
28
|
-
|
29
|
-
set_status(execution)
|
30
|
-
render_entity(execution) if @meta[:responses]
|
26
|
+
@meta.set_response(execution) if @meta.responses
|
31
27
|
rescue Execution::Abort
|
32
28
|
execution.response.status = 200 if execution.response.status == 0
|
33
29
|
end
|
@@ -42,26 +38,8 @@ module Meta
|
|
42
38
|
return true
|
43
39
|
end
|
44
40
|
|
45
|
-
|
46
|
-
|
47
|
-
def set_status(execution)
|
48
|
-
response_definitions = @meta[:responses]
|
49
|
-
response = execution.response
|
50
|
-
if response.status == 0
|
51
|
-
response.status = (response_definitions&.length > 0) ? response_definitions.keys[0] : 200
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def render_entity(execution)
|
56
|
-
response_definitions = @meta[:responses]
|
57
|
-
return if response_definitions.empty? # 未设定 status 宏时不需要执行 render_entity 方法
|
58
|
-
|
59
|
-
# 查找 entity schema
|
60
|
-
entity_schema = response_definitions[execution.response.status]
|
61
|
-
return if entity_schema.nil?
|
62
|
-
|
63
|
-
# 执行面向 schema 的渲染
|
64
|
-
execution.render_entity(entity_schema)
|
41
|
+
def generate_operation_doc(schemas)
|
42
|
+
meta.generate_operation_doc(schemas, scope: ["$#{method}"])
|
65
43
|
end
|
66
44
|
end
|
67
45
|
end
|
data/lib/meta/config.rb
CHANGED
@@ -4,10 +4,9 @@ require 'hash_to_struct'
|
|
4
4
|
|
5
5
|
module Meta
|
6
6
|
DEFAULT_OPTIONS = {
|
7
|
-
default_locked_scope: nil,
|
8
7
|
json_schema_user_options: {},
|
9
|
-
|
10
|
-
|
8
|
+
json_schema_param_stage_user_options: {},
|
9
|
+
json_schema_render_stage_user_options: {}
|
11
10
|
}
|
12
11
|
|
13
12
|
class << self
|
data/lib/meta/entity.rb
CHANGED
@@ -14,9 +14,7 @@ module Meta
|
|
14
14
|
def inherited(base)
|
15
15
|
base.instance_eval do
|
16
16
|
@schema_builder = JsonSchema::ObjectSchemaBuilder.new
|
17
|
-
@schema_builder.schema_name(
|
18
|
-
generate_schema_name(locked_scope, stage)
|
19
|
-
})
|
17
|
+
@schema_builder.schema_name(self.name) if self.name
|
20
18
|
end
|
21
19
|
end
|
22
20
|
|
@@ -32,6 +30,7 @@ module Meta
|
|
32
30
|
|
33
31
|
private
|
34
32
|
|
33
|
+
# TODO: 不需要在 Entity 内自动生成名称了,交给 ObjectSchema::Naming 去做吧
|
35
34
|
def generate_schema_name(stage, locked_scopes)
|
36
35
|
# 匿名类不考虑自动生成名称
|
37
36
|
return nil unless self.name
|
@@ -5,6 +5,30 @@ require_relative '../schemas/properties'
|
|
5
5
|
module Meta
|
6
6
|
module JsonSchema
|
7
7
|
class ObjectSchemaBuilder
|
8
|
+
module LockedMethodAlias
|
9
|
+
# 我在这里说明一下 lock_scope 的逻辑。
|
10
|
+
# 1. lock_scope 实际上是将 scope 传递到当前的 ObjectSchema 和它的子 Schema 中。
|
11
|
+
# 2. lock_scope 会叠加,也就是如果子 schema 也有 lock_scope,那么子 Schema 会将两个 Schema 合并起来。
|
12
|
+
# 3. 调用 filter(scope:) 和 to_schema_doc(scope:) 时,可以传递 scope 参数,这个 scope 遇到 lock_scope 时会合并。
|
13
|
+
# 4. 这也就是说,在路由级别定义的 scope 宏会传递到下面的 Schema 中去。
|
14
|
+
def add_scope(scope)
|
15
|
+
lock_scope(scope)
|
16
|
+
end
|
17
|
+
|
18
|
+
def lock(key, value)
|
19
|
+
locked(key => value)
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing(method, *args)
|
23
|
+
if method =~ /^lock_(\w+)$/
|
24
|
+
key = Regexp.last_match(1)
|
25
|
+
lock(key.to_sym, *args)
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
8
32
|
def initialize(options = {}, &)
|
9
33
|
raise 'type 选项必须是 object' if !options[:type].nil? && options[:type] != 'object'
|
10
34
|
|
@@ -23,23 +47,10 @@ module Meta
|
|
23
47
|
instance_exec(&) if block_given?
|
24
48
|
end
|
25
49
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
# 三、可以传递一个字符串。
|
31
|
-
def schema_name(schema_name_resolver)
|
32
|
-
if schema_name_resolver.is_a?(Proc)
|
33
|
-
@schema_name_resolver = schema_name_resolver
|
34
|
-
elsif schema_name_resolver.is_a?(Hash)
|
35
|
-
@schema_name_resolver = proc { |stage, locked_scopes| schema_name_resolver[stage] }
|
36
|
-
elsif schema_name_resolver.is_a?(String)
|
37
|
-
@schema_name_resolver = proc { |stage, locked_scopes| schema_name_resolver }
|
38
|
-
elsif schema_name_resolver.nil?
|
39
|
-
@schema_name_resolver = proc { nil }
|
40
|
-
else
|
41
|
-
raise TypeError, "schema_name_resolver 必须是一个 Proc、Hash 或 String,当前是:#{schema_name_resolver.class}"
|
42
|
-
end
|
50
|
+
def schema_name(schema_base_name)
|
51
|
+
raise TypeError, "schema_base_name 必须是一个 String,当前是:#{schema_base_name.class}" unless schema_base_name.is_a?(String)
|
52
|
+
|
53
|
+
@schema_name = schema_base_name
|
43
54
|
end
|
44
55
|
|
45
56
|
def property(name, options = {}, &block)
|
@@ -56,16 +67,14 @@ module Meta
|
|
56
67
|
end
|
57
68
|
|
58
69
|
def to_schema(locked_options = nil)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
def lock(key, value)
|
63
|
-
locked(key => value)
|
70
|
+
properties = @schema_name ? NamedProperties.new(@properties, @schema_name) : Properties.new(@properties)
|
71
|
+
ObjectSchema.new(properties: properties, options: @options, locked_options: locked_options)
|
64
72
|
end
|
65
73
|
|
66
74
|
def locked(options)
|
67
|
-
Locked.new(self, options)
|
75
|
+
Locked.new(self, **options)
|
68
76
|
end
|
77
|
+
include LockedMethodAlias
|
69
78
|
|
70
79
|
private
|
71
80
|
|
@@ -77,29 +86,25 @@ module Meta
|
|
77
86
|
(options[:type] == 'object' || block) && (options[:properties] || block)
|
78
87
|
end
|
79
88
|
|
80
|
-
def method_missing(method, *args)
|
81
|
-
if method =~ /^lock_(\w+)$/
|
82
|
-
key = Regexp.last_match(1)
|
83
|
-
lock(key.to_sym, *args)
|
84
|
-
else
|
85
|
-
super
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
89
|
class Locked
|
90
|
-
attr_reader :
|
90
|
+
attr_reader :object_schema_builder, :locked_options
|
91
91
|
|
92
|
-
|
93
|
-
|
94
|
-
@
|
92
|
+
# locked_options 是用户传递的参数,这个参数会被合并到 object_schema_builder 的 locked_options 中。
|
93
|
+
def initialize(builder, scope: nil, discard_missing: nil, exclude: nil)
|
94
|
+
@object_schema_builder = builder
|
95
|
+
@locked_options = ObjectSchema::USER_OPTIONS_CHECKER.check({ scope: scope, discard_missing: discard_missing, exclude: exclude }.compact)
|
95
96
|
end
|
96
97
|
|
97
|
-
# 当调用 Entity.locked 方法后,生成 schema 的方法会掉到这里面来。
|
98
|
-
# 在生成 schema 时,locked_options 会覆盖;当生成 schema 文档时,由于缺失 schema_name 的
|
99
|
-
# 信息,故而 schema_name 相关的影响就消失不见了。
|
100
98
|
def to_schema
|
101
|
-
|
99
|
+
object_schema_builder.to_schema(locked_options)
|
100
|
+
end
|
101
|
+
|
102
|
+
def locked(options)
|
103
|
+
options = ObjectSchema::USER_OPTIONS_CHECKER.check(options)
|
104
|
+
options = ObjectSchema.merge_user_options(locked_options, options)
|
105
|
+
Locked.new(self.object_schema_builder, **options)
|
102
106
|
end
|
107
|
+
include LockedMethodAlias
|
103
108
|
end
|
104
109
|
end
|
105
110
|
end
|
@@ -4,6 +4,7 @@ require_relative 'ref_schema_builder'
|
|
4
4
|
require_relative 'dynamic_schema_builder'
|
5
5
|
require_relative 'array_schema_builder'
|
6
6
|
require_relative 'object_schema_builder'
|
7
|
+
require_relative '../schemas/staging_schema'
|
7
8
|
|
8
9
|
module Meta
|
9
10
|
module JsonSchema
|
@@ -13,11 +14,7 @@ module Meta
|
|
13
14
|
permit_extras true
|
14
15
|
|
15
16
|
key :ref, alias_names: [:using], normalizer: ->(entity) {
|
16
|
-
|
17
|
-
entity.locked(scope: Meta.config.default_locked_scope)
|
18
|
-
else
|
19
|
-
entity
|
20
|
-
end
|
17
|
+
entity
|
21
18
|
}
|
22
19
|
key :dynamic_ref, alias_names: [:dynamic_using], normalizer: ->(value) { value.is_a?(Proc) ? { resolve: value } : value }
|
23
20
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module Meta
|
4
4
|
module JsonSchema
|
5
5
|
class ArraySchema < BaseSchema
|
6
|
+
extend Forwardable
|
7
|
+
|
6
8
|
attr_reader :items
|
7
9
|
|
8
10
|
def initialize(items, options = {})
|
@@ -11,20 +13,6 @@ module Meta
|
|
11
13
|
@items = items
|
12
14
|
end
|
13
15
|
|
14
|
-
def filter(array_value, options = {})
|
15
|
-
array_value = super(array_value, options)
|
16
|
-
return nil if array_value.nil?
|
17
|
-
raise ValidationError.new('参数应该传递一个数组') unless array_value.respond_to?(:each_with_index)
|
18
|
-
|
19
|
-
array_value.each_with_index.map do |item, index|
|
20
|
-
begin
|
21
|
-
@items.filter(item, **options)
|
22
|
-
rescue ValidationErrors => e
|
23
|
-
raise e.prepend_root("[#{index}]")
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
16
|
def to_schema_doc(**user_options)
|
29
17
|
stage_options = options
|
30
18
|
|
@@ -35,6 +23,28 @@ module Meta
|
|
35
23
|
schema[:description] = stage_options[:description] if stage_options[:description]
|
36
24
|
schema
|
37
25
|
end
|
26
|
+
|
27
|
+
def_delegator :@items, :defined_scopes
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def filter_internal(array_value, user_options)
|
32
|
+
if array_value.respond_to?(:each_with_index)
|
33
|
+
array_value = array_value
|
34
|
+
elsif array_value.respond_to?(:to_a)
|
35
|
+
array_value = array_value.to_a
|
36
|
+
else
|
37
|
+
raise ValidationError.new('参数应该传递一个数组或者数组 Like 的对象(实现了 each_with_index 或者 to_a 方法)')
|
38
|
+
end
|
39
|
+
|
40
|
+
array_value.each_with_index.map do |item, index|
|
41
|
+
begin
|
42
|
+
@items.filter(item, user_options)
|
43
|
+
rescue ValidationErrors => e
|
44
|
+
raise e.prepend_root("[#{index}]")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
38
48
|
end
|
39
49
|
end
|
40
50
|
end
|