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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/lib/meta/application/execution.rb +19 -49
  4. data/lib/meta/application/metadata.rb +68 -15
  5. data/lib/meta/application/responses.rb +32 -0
  6. data/lib/meta/application/route.rb +4 -26
  7. data/lib/meta/config.rb +2 -3
  8. data/lib/meta/entity.rb +2 -3
  9. data/lib/meta/json_schema/builders/object_schema_builder.rb +45 -40
  10. data/lib/meta/json_schema/builders/schema_builder_tool.rb +2 -5
  11. data/lib/meta/json_schema/schemas/array_schema.rb +24 -14
  12. data/lib/meta/json_schema/schemas/base_schema.rb +107 -37
  13. data/lib/meta/json_schema/schemas/named_properties.rb +37 -0
  14. data/lib/meta/json_schema/schemas/object_schema.rb +58 -20
  15. data/lib/meta/json_schema/schemas/properties.rb +27 -76
  16. data/lib/meta/json_schema/schemas/ref_schema.rb +36 -9
  17. data/lib/meta/json_schema/schemas/scoping_schema.rb +38 -0
  18. data/lib/meta/json_schema/schemas/staging_schema.rb +59 -0
  19. data/lib/meta/json_schema/schemas/unsupported_schema.rb +22 -0
  20. data/lib/meta/json_schema/support/errors.rb +3 -0
  21. data/lib/meta/json_schema/support/json_object.rb +37 -0
  22. data/lib/meta/json_schema/support/schema_options.rb +0 -9
  23. data/lib/meta/json_schema/support/scope_matcher.rb +29 -0
  24. data/lib/meta/json_schema/support/type_converter.rb +3 -24
  25. data/lib/meta/route_dsl/meta_builder.rb +2 -2
  26. data/lib/meta/swagger_doc.rb +2 -25
  27. data/lib/meta/utils/kwargs/builder.rb +5 -3
  28. data/lib/meta/utils/route_dsl_builders.rb +2 -1
  29. data/meta-api.gemspec +6 -5
  30. metadata +75 -111
  31. data/.autoenv.zsh +0 -1
  32. data/.gitignore +0 -7
  33. data/.rubocop.yml +0 -28
  34. data/Gemfile +0 -19
  35. data/Gemfile.lock +0 -68
  36. data/README.md +0 -166
  37. data/Rakefile +0 -3
  38. data/docs/Rails.md +0 -61
  39. data/docs//345/220/215/347/247/260/347/224/261/346/235/245.md +0 -7
  40. data/docs//345/246/202/344/275/225/350/264/241/347/214/256.md +0 -10
  41. data/docs//346/225/231/347/250/213.md +0 -1708
  42. data/docs//347/264/242/345/274/225.md +0 -183
  43. data/examples/lobster.rb +0 -71
  44. data/examples/rack_app/README.md +0 -3
  45. data/examples/rack_app/config.ru +0 -6
  46. data/examples/rack_app/hello.rb +0 -6
  47. data/examples/rack_app/timing.rb +0 -15
  48. data/examples/rails_app/.gitattributes +0 -5
  49. data/examples/rails_app/.gitignore +0 -23
  50. data/examples/rails_app/.rspec +0 -1
  51. data/examples/rails_app/.ruby-version +0 -1
  52. data/examples/rails_app/Gemfile +0 -29
  53. data/examples/rails_app/Gemfile.lock +0 -190
  54. data/examples/rails_app/README.md +0 -11
  55. data/examples/rails_app/Rakefile +0 -6
  56. data/examples/rails_app/app/controllers/application_controller.rb +0 -7
  57. data/examples/rails_app/app/controllers/concerns/.keep +0 -0
  58. data/examples/rails_app/app/controllers/data_controller.rb +0 -63
  59. data/examples/rails_app/app/controllers/swagger_controller.rb +0 -13
  60. data/examples/rails_app/app/models/concerns/.keep +0 -0
  61. data/examples/rails_app/bin/rails +0 -4
  62. data/examples/rails_app/bin/rake +0 -4
  63. data/examples/rails_app/bin/setup +0 -25
  64. data/examples/rails_app/config/application.rb +0 -39
  65. data/examples/rails_app/config/boot.rb +0 -3
  66. data/examples/rails_app/config/credentials.yml.enc +0 -1
  67. data/examples/rails_app/config/environment.rb +0 -5
  68. data/examples/rails_app/config/environments/development.rb +0 -51
  69. data/examples/rails_app/config/environments/production.rb +0 -65
  70. data/examples/rails_app/config/environments/test.rb +0 -50
  71. data/examples/rails_app/config/initializers/cors.rb +0 -16
  72. data/examples/rails_app/config/initializers/filter_parameter_logging.rb +0 -8
  73. data/examples/rails_app/config/initializers/inflections.rb +0 -16
  74. data/examples/rails_app/config/initializers/meta_rails_plugin.rb +0 -3
  75. data/examples/rails_app/config/locales/en.yml +0 -33
  76. data/examples/rails_app/config/puma.rb +0 -43
  77. data/examples/rails_app/config/routes.rb +0 -13
  78. data/examples/rails_app/config.ru +0 -6
  79. data/examples/rails_app/lib/tasks/.keep +0 -0
  80. data/examples/rails_app/log/.keep +0 -0
  81. data/examples/rails_app/public/robots.txt +0 -1
  82. data/examples/rails_app/spec/data_controller_spec.rb +0 -60
  83. data/examples/rails_app/spec/rails_helper.rb +0 -55
  84. data/examples/rails_app/spec/spec_helper.rb +0 -94
  85. data/examples/rails_app/spec/swagger_controller_spec.rb +0 -13
  86. data/examples/rails_app/tmp/.keep +0 -0
  87. data/examples/rails_app/tmp/pids/.keep +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b35c78915baab28213ceb9f2b6184c8ae177742ce0524529c681132fa6e0390
4
- data.tar.gz: bc4ca96b46e0284f10fa41360ef4308aa7378121ead9f0cd1a536ac027cd590e
3
+ metadata.gz: bf3fb639df71a5353328e898711c9758c5e5fd3dd1760409ae823fbd0a7852e2
4
+ data.tar.gz: ea5dd728c3691fcf542d1085fd21d787d727c97b588652de3ffdd53b67512437
5
5
  SHA512:
6
- metadata.gz: c5cf6b6c1ee9ba5af2826b6682e57a357bbd74caf7807a5f3f9cbd124b99bdce4bcc7cba7040b45b2c1d62b9abfda1e4ee6c47853c2ebcb57bfd2d8a224db622
7
- data.tar.gz: 5043cc4c8516d7c6bc827d32889e26a2164299ba0a71b1439bd1594aabdd1975bbd1994139fed2ffca93409e9c1f2118157301b3cef502767750c52b061c7367
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, :params_schema, :request_body_schema
8
- attr_accessor :parameters
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] = parse_request_body_for_replacing.freeze
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] = parse_request_body_for_updating.freeze
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 @request_body_schema
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
- @renders[key] = { value: value, options: options || {} }
71
- end
72
-
73
- # 运行过程中首先会解析参数
74
- def parse_parameters(parameters_meta)
75
- self.parameters = parameters_meta.filter(request).freeze
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
- def parse_request_body(schema)
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 = entity_schema.filter(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].schema(:render)
112
- final_value[key] = schema.filter(render_content[:value],
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
- attr_reader :title, :description, :tags, :parameters, :request_body, :responses
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
- def initialize(title: nil, description: nil, tags: [], parameters: {}, request_body: nil, responses: nil)
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 || {} # || { 204 => nil }
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, schemas: schemas)
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.transform_values do |schema|
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.parse_parameters(@meta[:parameters]) if @meta[:parameters]
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
- private
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
- json_schema_param_stage_options: {},
10
- json_schema_render_stage_options: {}
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(proc { |locked_scope, stage|
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
- # 设置 schema_name.
27
- #
28
- # 一、可以传递一个块,该块会接收 locked_scope 参数,需要返回一个带有 param 和 render 键的 Hash.
29
- # 二、可以传递一个 Hash,它包含 param 和 render 键。
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
- ObjectSchema.new(properties: @properties, options: @options, locked_options: locked_options, schema_name_resolver: @schema_name_resolver)
60
- end
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 :builder, :locked_options
90
+ attr_reader :object_schema_builder, :locked_options
91
91
 
92
- def initialize(builder, locked_options)
93
- @builder = builder
94
- @locked_options = locked_options
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
- builder.to_schema(locked_options)
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
- if Meta.config.default_locked_scope && entity.is_a?(Class) && entity < Meta::Entity
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