meta-api 0.1.0 → 0.1.1
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 +6 -0
- data/lib/meta/entity.rb +3 -34
- data/lib/meta/json_schema/builders/object_schema_builder.rb +88 -27
- data/lib/meta/json_schema/schemas/base_schema.rb +9 -6
- data/lib/meta/json_schema/schemas/object_schema.rb +4 -11
- data/lib/meta/json_schema/schemas/properties.rb +13 -1
- data/lib/meta/json_schema/schemas/ref_schema.rb +5 -2
- data/lib/meta/json_schema/schemas/scoping_schema.rb +3 -1
- data/lib/meta/route_dsl/meta_builder.rb +10 -1
- 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: f5f1c4106f4106c1a3e69249e35f4efc02d4ca5d5981d4e32442c380f4228556
|
4
|
+
data.tar.gz: 7dc942b0a36759a0953a255b18a7c6c6312159365e9b829b48f223d0d83cd685
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 275731f780c2e5eb3dcae314a5b7832504f053092eee8aa3eb1e8b85d7cce0f4fec33bf8f46626cb307d6c7c98e6ff0ee13bf100d6a4610099323d069968d7ae
|
7
|
+
data.tar.gz: f3f21b6e62503946e26471f9199809247d0ee3fafa13a2190e57aadb038b47cb19757ed08be4f2054e2b0151c2ed9f1cf4d737c007e133479395aa936d2a338b
|
data/CHANGELOG.md
CHANGED
data/lib/meta/entity.rb
CHANGED
@@ -18,40 +18,9 @@ module Meta
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
def method_missing(method, *args)
|
24
|
-
if method =~ /^lock_(\w+)$/
|
25
|
-
schema_builder.send(method, *args)
|
26
|
-
else
|
27
|
-
super
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
# TODO: 不需要在 Entity 内自动生成名称了,交给 ObjectSchema::Naming 去做吧
|
34
|
-
def generate_schema_name(stage, locked_scopes)
|
35
|
-
# 匿名类不考虑自动生成名称
|
36
|
-
return nil unless self.name
|
37
|
-
|
38
|
-
schema_name = self.name.gsub('::', '_')
|
39
|
-
schema_name = schema_name.delete_suffix('Entity') unless schema_name == 'Entity'
|
40
|
-
|
41
|
-
# 先考虑 stage
|
42
|
-
case stage
|
43
|
-
when :param
|
44
|
-
schema_name += 'Params'
|
45
|
-
when :render
|
46
|
-
schema_name += 'Entity'
|
47
|
-
end
|
48
|
-
|
49
|
-
# 再考虑 locked_scope
|
50
|
-
scope_suffix = locked_scopes.join('_')
|
51
|
-
schema_name = "#{schema_name}_#{scope_suffix}" unless scope_suffix.empty?
|
52
|
-
|
53
|
-
schema_name
|
21
|
+
def method_missing(method, *args, **kwargs, &)
|
22
|
+
schema_builder.send(method, *args, **kwargs, &)
|
54
23
|
end
|
55
24
|
end
|
56
25
|
end
|
57
|
-
end
|
26
|
+
end
|
@@ -5,6 +5,8 @@ require_relative '../schemas/properties'
|
|
5
5
|
module Meta
|
6
6
|
module JsonSchema
|
7
7
|
class ObjectSchemaBuilder
|
8
|
+
extend Forwardable
|
9
|
+
|
8
10
|
module LockedMethodAlias
|
9
11
|
# 我在这里说明一下 lock_scope 的逻辑。
|
10
12
|
# 1. lock_scope 实际上是将 scope 传递到当前的 ObjectSchema 和它的子 Schema 中。
|
@@ -29,10 +31,49 @@ module Meta
|
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
34
|
+
class Locked
|
35
|
+
attr_reader :object_schema_builder, :locked_options
|
36
|
+
|
37
|
+
# locked_options 是用户传递的参数,这个参数会被合并到 object_schema_builder 的 locked_options 中。
|
38
|
+
def initialize(builder, scope: nil, discard_missing: nil, exclude: nil)
|
39
|
+
@object_schema_builder = builder
|
40
|
+
@locked_options = ObjectSchema::USER_OPTIONS_CHECKER.check({ scope: scope, discard_missing: discard_missing, exclude: exclude }.compact)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_schema
|
44
|
+
object_schema_builder.to_schema(locked_options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def locked(options)
|
48
|
+
options = ObjectSchema::USER_OPTIONS_CHECKER.check(options)
|
49
|
+
options = ObjectSchema.merge_user_options(locked_options, options)
|
50
|
+
Locked.new(self.object_schema_builder, **options)
|
51
|
+
end
|
52
|
+
include LockedMethodAlias
|
53
|
+
end
|
54
|
+
|
55
|
+
class WithCommonOptions
|
56
|
+
attr_reader :object_schema_builder, :common_options
|
57
|
+
|
58
|
+
def initialize(builder, common_options, &)
|
59
|
+
@object_schema_builder = builder
|
60
|
+
@common_options = common_options
|
61
|
+
|
62
|
+
instance_exec(&) if block_given?
|
63
|
+
end
|
64
|
+
|
65
|
+
def property(name, options = {}, &block)
|
66
|
+
options = common_options.merge(options)
|
67
|
+
object_schema_builder.property(name, options, &block)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_reader :properties
|
72
|
+
|
32
73
|
def initialize(options = {}, &)
|
33
74
|
raise 'type 选项必须是 object' if !options[:type].nil? && options[:type] != 'object'
|
34
75
|
|
35
|
-
@properties = {}
|
76
|
+
@properties = {} # 所有的属性已经生成
|
36
77
|
@required = []
|
37
78
|
@validations = {}
|
38
79
|
|
@@ -47,10 +88,13 @@ module Meta
|
|
47
88
|
instance_exec(&) if block_given?
|
48
89
|
end
|
49
90
|
|
50
|
-
def schema_name(schema_base_name)
|
51
|
-
|
52
|
-
|
53
|
-
|
91
|
+
def schema_name(schema_base_name = nil)
|
92
|
+
if schema_base_name
|
93
|
+
raise TypeError, "schema_base_name 必须是一个 String,当前是:#{schema_base_name.class}" unless schema_base_name.is_a?(String)
|
94
|
+
@schema_name = schema_base_name
|
95
|
+
else
|
96
|
+
@schema_name
|
97
|
+
end
|
54
98
|
end
|
55
99
|
|
56
100
|
def property(name, options = {}, &block)
|
@@ -66,12 +110,50 @@ module Meta
|
|
66
110
|
instance_exec(&proc)
|
67
111
|
end
|
68
112
|
|
113
|
+
def with_common_options(common_options, &block)
|
114
|
+
WithCommonOptions.new(self, common_options, &block)
|
115
|
+
end
|
116
|
+
|
117
|
+
def scope(scope, options = {}, &)
|
118
|
+
with_common_options(**options, scope: scope, &)
|
119
|
+
end
|
120
|
+
|
121
|
+
def params(options = {}, &block)
|
122
|
+
with_common_options(**options, render: false, &block)
|
123
|
+
end
|
124
|
+
|
125
|
+
def render(options = {}, &block)
|
126
|
+
with_common_options(**options, params: false, &block)
|
127
|
+
end
|
128
|
+
|
129
|
+
def merge(schema_builder)
|
130
|
+
schema_builder = schema_builder.schema_builder if schema_builder.respond_to?(:schema_builder)
|
131
|
+
|
132
|
+
@properties.merge!(schema_builder.properties)
|
133
|
+
end
|
134
|
+
|
69
135
|
def to_schema(locked_options = nil)
|
70
136
|
properties = @schema_name ? NamedProperties.new(@properties, @schema_name) : Properties.new(@properties)
|
71
137
|
ObjectSchema.new(properties: properties, options: @options, locked_options: locked_options)
|
72
138
|
end
|
73
139
|
|
74
140
|
def locked(options)
|
141
|
+
defined_scopes_mapping = {}
|
142
|
+
# TODO: 将 properties 搞成 Properties 可以吗?
|
143
|
+
defined_scopes = properties.map do |key, property|
|
144
|
+
property.defined_scopes(stage: :param, defined_scopes_mapping: defined_scopes_mapping)
|
145
|
+
end.flatten.uniq
|
146
|
+
|
147
|
+
user_scopes = options[:scope] || []
|
148
|
+
user_scopes = [user_scopes] unless user_scopes.is_a?(Array)
|
149
|
+
|
150
|
+
# 判断 user_scopes 中提供的局部 scope 是否在 defined_scopes 中
|
151
|
+
local_user_scopes = user_scopes.reject { |scope| scope.start_with?('$') }
|
152
|
+
if (local_user_scopes - defined_scopes).any?
|
153
|
+
extra_scopes = local_user_scopes - defined_scopes
|
154
|
+
raise ArgumentError, "scope #{extra_scopes.join(',')} 未在实体中定义"
|
155
|
+
end
|
156
|
+
|
75
157
|
Locked.new(self, **options)
|
76
158
|
end
|
77
159
|
include LockedMethodAlias
|
@@ -85,27 +167,6 @@ module Meta
|
|
85
167
|
def apply_object_scope?(options, block)
|
86
168
|
(options[:type] == 'object' || block) && (options[:properties] || block)
|
87
169
|
end
|
88
|
-
|
89
|
-
class Locked
|
90
|
-
attr_reader :object_schema_builder, :locked_options
|
91
|
-
|
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)
|
96
|
-
end
|
97
|
-
|
98
|
-
def to_schema
|
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)
|
106
|
-
end
|
107
|
-
include LockedMethodAlias
|
108
|
-
end
|
109
170
|
end
|
110
171
|
end
|
111
|
-
end
|
172
|
+
end
|
@@ -89,7 +89,7 @@ module Meta
|
|
89
89
|
end
|
90
90
|
|
91
91
|
# 返回能够处理 scope 和 stage 的 schema(可以是 self),否则应返回 UnsupportedStageSchema 或 nil.
|
92
|
-
def find_schema(
|
92
|
+
def find_schema(stage:, scope:)
|
93
93
|
staged(stage)&.scoped(scope)
|
94
94
|
end
|
95
95
|
|
@@ -103,19 +103,22 @@ module Meta
|
|
103
103
|
self
|
104
104
|
end
|
105
105
|
|
106
|
+
# defined_scopes_mapping 是一个 Hash,用于缓存已经计算出的 scopes,用于避免重复计算。其主要针对的是具有命名系统的 Schema,如 Meta::Entity
|
106
107
|
def defined_scopes(stage:, defined_scopes_mapping:)
|
107
108
|
[]
|
108
109
|
end
|
109
110
|
|
110
111
|
# 执行 if: 选项,返回 true 或 false
|
111
|
-
def if?(
|
112
|
-
|
112
|
+
def if?(object_value, execution = nil)
|
113
|
+
if_block = options[:if]
|
114
|
+
return true if if_block.nil?
|
113
115
|
|
114
|
-
|
116
|
+
args_length = if_block.lambda? ? if_block.arity : 1
|
117
|
+
args = args_length > 0 ? [object_value] : []
|
115
118
|
if execution
|
116
|
-
execution.instance_exec(&options[:if])
|
119
|
+
execution.instance_exec(*args, &options[:if])
|
117
120
|
else
|
118
|
-
options[:if]&.call
|
121
|
+
options[:if]&.call(*args)
|
119
122
|
end
|
120
123
|
end
|
121
124
|
|
@@ -42,17 +42,6 @@ module Meta
|
|
42
42
|
@locked_options = USER_OPTIONS_CHECKER.check(locked_options || {})
|
43
43
|
end
|
44
44
|
|
45
|
-
# 复制一个新的 ObjectSchema,只有 options 不同
|
46
|
-
def dup(options)
|
47
|
-
raise UnsupportedError, 'dup 不应该再执行了'
|
48
|
-
|
49
|
-
self.class.new(
|
50
|
-
properties: properties,
|
51
|
-
options: options,
|
52
|
-
locked_options: locked_options,
|
53
|
-
)
|
54
|
-
end
|
55
|
-
|
56
45
|
def filter(object_value, user_options = {})
|
57
46
|
# 合并 user_options
|
58
47
|
user_options = USER_OPTIONS_CHECKER.check(user_options)
|
@@ -64,6 +53,10 @@ module Meta
|
|
64
53
|
properties.is_a?(NamedProperties)
|
65
54
|
end
|
66
55
|
|
56
|
+
def defined_scopes(stage:, defined_scopes_mapping:)
|
57
|
+
properties.defined_scopes(stage: stage, defined_scopes_mapping: defined_scopes_mapping)
|
58
|
+
end
|
59
|
+
|
67
60
|
def resolve_name(stage, user_scopes, defined_scopes)
|
68
61
|
raise ArgumentError, 'stage 不能为 nil' if stage.nil?
|
69
62
|
|
@@ -30,7 +30,7 @@ module Meta
|
|
30
30
|
next false if exclude && exclude.include?(name)
|
31
31
|
|
32
32
|
# 通过 if 选项过滤
|
33
|
-
next false unless property_schema.if?(user_options)
|
33
|
+
next false unless property_schema.if?(object_value, user_options[:execution])
|
34
34
|
|
35
35
|
# 默认返回 true
|
36
36
|
next true
|
@@ -44,6 +44,12 @@ module Meta
|
|
44
44
|
value = resolve_property_value(object_value, name, property_schema)
|
45
45
|
|
46
46
|
begin
|
47
|
+
# 如果 property_schema 是 RefSchema,则局部的 scope 不会传递下去
|
48
|
+
if property_schema.is_a?(RefSchema)
|
49
|
+
# 只接受全局的 scope,其以 $ 符合开头
|
50
|
+
new_scopes = user_options[:scope].find_all { |scope| scope.start_with?('$') }
|
51
|
+
user_options = user_options.merge(scope: new_scopes)
|
52
|
+
end
|
47
53
|
object[name] = property_schema.filter(value, **user_options, object_value: object_value)
|
48
54
|
rescue JsonSchema::ValidationErrors => e
|
49
55
|
cause = e.cause || e if cause.nil? # 将第一次出现的错误作为 cause
|
@@ -69,6 +75,12 @@ module Meta
|
|
69
75
|
end
|
70
76
|
end
|
71
77
|
|
78
|
+
def defined_scopes(stage:, defined_scopes_mapping:)
|
79
|
+
@properties.each_with_object([]) do |(name, property), defined_scopes|
|
80
|
+
defined_scopes.concat(property.defined_scopes(stage: stage, defined_scopes_mapping: defined_scopes_mapping))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
72
84
|
# user_options 包括 stage, scope, schema_docs_mapping, defined_scopes_mapping
|
73
85
|
def to_swagger_doc(scope: [], stage: nil, **user_options)
|
74
86
|
locked_scopes = scope
|
@@ -40,8 +40,11 @@ module Meta
|
|
40
40
|
def defined_scopes(stage:, defined_scopes_mapping:)
|
41
41
|
defined_scopes_mapping ||= {}
|
42
42
|
|
43
|
-
|
44
|
-
|
43
|
+
if object_schema.properties.respond_to?(:schema_name)
|
44
|
+
# 只有命名实体才会被缓存
|
45
|
+
schema_name = object_schema.properties.schema_name(stage)
|
46
|
+
return defined_scopes_mapping[schema_name] if defined_scopes_mapping.key?(schema_name)
|
47
|
+
end
|
45
48
|
|
46
49
|
defined_scopes_mapping[schema_name] = []
|
47
50
|
defined_scopes = object_schema.properties.each.map do |name, property|
|
@@ -43,7 +43,16 @@ module Meta
|
|
43
43
|
codes.each { |code| @meta[:responses][code] = entity_schema }
|
44
44
|
end
|
45
45
|
|
46
|
-
|
46
|
+
def scope(scope)
|
47
|
+
scope = [scope] unless scope.is_a?(Array)
|
48
|
+
unless scope.all? { |s| s.start_with?('$') }
|
49
|
+
raise ArgumentError, 'namespace 和 route 中声明的 scope 必须是全局 scope(以 $ 开头)'
|
50
|
+
end
|
51
|
+
|
52
|
+
@meta[:scope] = scope
|
53
|
+
end
|
54
|
+
|
55
|
+
[:tags, :title, :description].each do |method_name|
|
47
56
|
define_method(method_name) do |value|
|
48
57
|
@meta[method_name] = value
|
49
58
|
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.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yetrun
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hash_to_struct
|