meta-api 0.0.2 → 0.0.3

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -4
  3. data/CHANGELOG.md +6 -1
  4. data/Gemfile.lock +1 -1
  5. data/README.md +9 -7
  6. data/docs/Rails.md +61 -0
  7. data/docs//345/246/202/344/275/225/350/264/241/347/214/256.md +10 -0
  8. data/docs//346/225/231/347/250/213.md +21 -21
  9. data/examples/rails_app/.gitattributes +5 -0
  10. data/examples/rails_app/.gitignore +23 -0
  11. data/examples/rails_app/.rspec +1 -0
  12. data/examples/rails_app/.ruby-version +1 -0
  13. data/examples/rails_app/Gemfile +29 -0
  14. data/examples/rails_app/Gemfile.lock +190 -0
  15. data/examples/rails_app/README.md +11 -0
  16. data/examples/rails_app/Rakefile +6 -0
  17. data/examples/rails_app/app/controllers/application_controller.rb +7 -0
  18. data/examples/rails_app/app/controllers/concerns/.keep +0 -0
  19. data/examples/rails_app/app/controllers/data_controller.rb +63 -0
  20. data/examples/rails_app/app/controllers/swagger_controller.rb +13 -0
  21. data/examples/rails_app/app/models/concerns/.keep +0 -0
  22. data/examples/rails_app/bin/rails +4 -0
  23. data/examples/rails_app/bin/rake +4 -0
  24. data/examples/rails_app/bin/setup +25 -0
  25. data/examples/rails_app/config/application.rb +39 -0
  26. data/examples/rails_app/config/boot.rb +3 -0
  27. data/examples/rails_app/config/credentials.yml.enc +1 -0
  28. data/examples/rails_app/config/environment.rb +5 -0
  29. data/examples/rails_app/config/environments/development.rb +51 -0
  30. data/examples/rails_app/config/environments/production.rb +65 -0
  31. data/examples/rails_app/config/environments/test.rb +50 -0
  32. data/examples/rails_app/config/initializers/cors.rb +16 -0
  33. data/examples/rails_app/config/initializers/filter_parameter_logging.rb +8 -0
  34. data/examples/rails_app/config/initializers/inflections.rb +16 -0
  35. data/examples/rails_app/config/initializers/meta_rails_plugin.rb +3 -0
  36. data/examples/rails_app/config/locales/en.yml +33 -0
  37. data/examples/rails_app/config/puma.rb +43 -0
  38. data/examples/rails_app/config/routes.rb +13 -0
  39. data/examples/rails_app/config.ru +6 -0
  40. data/examples/rails_app/lib/tasks/.keep +0 -0
  41. data/examples/rails_app/log/.keep +0 -0
  42. data/examples/rails_app/public/robots.txt +1 -0
  43. data/examples/rails_app/spec/data_controller_spec.rb +60 -0
  44. data/examples/rails_app/spec/rails_helper.rb +55 -0
  45. data/examples/rails_app/spec/spec_helper.rb +94 -0
  46. data/examples/rails_app/spec/swagger_controller_spec.rb +13 -0
  47. data/examples/rails_app/tmp/.keep +0 -0
  48. data/examples/rails_app/tmp/pids/.keep +0 -0
  49. data/lib/meta/application/execution.rb +3 -11
  50. data/lib/meta/application/{meta.rb → metadata.rb} +5 -15
  51. data/lib/meta/application/parameters.rb +47 -0
  52. data/lib/meta/application/route.rb +2 -2
  53. data/lib/meta/json_schema/builders/array_schema_builder.rb +11 -10
  54. data/lib/meta/json_schema/builders/dynamic_schema_builder.rb +23 -0
  55. data/lib/meta/json_schema/builders/object_schema_builder.rb +4 -18
  56. data/lib/meta/json_schema/builders/ref_schema_builder.rb +19 -0
  57. data/lib/meta/json_schema/builders/schema_builder_tool.rb +26 -1
  58. data/lib/meta/json_schema/schemas/array_schema.rb +3 -3
  59. data/lib/meta/json_schema/schemas/base_schema.rb +29 -69
  60. data/lib/meta/json_schema/schemas/dynamic_schema.rb +29 -0
  61. data/lib/meta/json_schema/schemas/object_schema.rb +27 -113
  62. data/lib/meta/json_schema/schemas/properties.rb +157 -0
  63. data/lib/meta/json_schema/schemas/ref_schema.rb +35 -0
  64. data/lib/meta/json_schema/schemas.rb +0 -2
  65. data/lib/meta/json_schema/support/schema_options.rb +21 -12
  66. data/lib/meta/json_schema.rb +2 -0
  67. data/lib/meta/rails.rb +98 -0
  68. data/lib/meta/route_dsl/parameters_builder.rb +2 -1
  69. data/lib/meta/swagger_doc.rb +5 -2
  70. data/lib/meta/utils/kwargs/builder.rb +115 -0
  71. data/lib/meta/utils/{kwargs.rb → kwargs/check.rb} +22 -22
  72. data/meta-api.gemspec +1 -1
  73. metadata +55 -4
@@ -0,0 +1,94 @@
1
+ # This file was generated by the `rails generate rspec:install` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
+ RSpec.configure do |config|
17
+ # rspec-expectations config goes here. You can use an alternate
18
+ # assertion/expectation library such as wrong or the stdlib/minitest
19
+ # assertions if you prefer.
20
+ config.expect_with :rspec do |expectations|
21
+ # This option will default to `true` in RSpec 4. It makes the `description`
22
+ # and `failure_message` of custom matchers include text for helper methods
23
+ # defined using `chain`, e.g.:
24
+ # be_bigger_than(2).and_smaller_than(4).description
25
+ # # => "be bigger than 2 and smaller than 4"
26
+ # ...rather than:
27
+ # # => "be bigger than 2"
28
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29
+ end
30
+
31
+ # rspec-mocks config goes here. You can use an alternate test double
32
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
33
+ config.mock_with :rspec do |mocks|
34
+ # Prevents you from mocking or stubbing a method that does not exist on
35
+ # a real object. This is generally recommended, and will default to
36
+ # `true` in RSpec 4.
37
+ mocks.verify_partial_doubles = true
38
+ end
39
+
40
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41
+ # have no way to turn it off -- the option exists only for backwards
42
+ # compatibility in RSpec 3). It causes shared context metadata to be
43
+ # inherited by the metadata hash of host groups and examples, rather than
44
+ # triggering implicit auto-inclusion in groups with matching metadata.
45
+ config.shared_context_metadata_behavior = :apply_to_host_groups
46
+
47
+ # The settings below are suggested to provide a good initial experience
48
+ # with RSpec, but feel free to customize to your heart's content.
49
+ =begin
50
+ # This allows you to limit a spec run to individual examples or groups
51
+ # you care about by tagging them with `:focus` metadata. When nothing
52
+ # is tagged with `:focus`, all examples get run. RSpec also provides
53
+ # aliases for `it`, `describe`, and `context` that include `:focus`
54
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55
+ config.filter_run_when_matching :focus
56
+
57
+ # Allows RSpec to persist some state between runs in order to support
58
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
59
+ # you configure your source control system to ignore this file.
60
+ config.example_status_persistence_file_path = "spec/examples.txt"
61
+
62
+ # Limits the available syntax to the non-monkey patched syntax that is
63
+ # recommended. For more details, see:
64
+ # https://relishapp.com/rspec/rspec-core/docs/configuration/zero-monkey-patching-mode
65
+ config.disable_monkey_patching!
66
+
67
+ # Many RSpec users commonly either run the entire suite or an individual
68
+ # file, and it's useful to allow more verbose output when running an
69
+ # individual spec file.
70
+ if config.files_to_run.one?
71
+ # Use the documentation formatter for detailed output,
72
+ # unless a formatter has already been configured
73
+ # (e.g. via a command-line flag).
74
+ config.default_formatter = "doc"
75
+ end
76
+
77
+ # Print the 10 slowest examples and example groups at the
78
+ # end of the spec run, to help surface which specs are running
79
+ # particularly slow.
80
+ config.profile_examples = 10
81
+
82
+ # Run specs in random order to surface order dependencies. If you find an
83
+ # order dependency and want to debug it, you can fix the order by providing
84
+ # the seed, which is printed after each run.
85
+ # --seed 1234
86
+ config.order = :random
87
+
88
+ # Seed global randomization in this process using the `--seed` CLI option.
89
+ # Setting this allows you to use `--seed` to deterministically reproduce
90
+ # test failures related to randomization by passing the same `--seed` value
91
+ # as the one that triggered the failure.
92
+ Kernel.srand config.seed
93
+ =end
94
+ end
@@ -0,0 +1,13 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe "SwaggerController", type: :request do
4
+ describe '生成 Swagger 文档' do
5
+ it "生成文档" do
6
+ get '/swagger_doc'
7
+ expect(response.status).to eq(200)
8
+
9
+ response_body = JSON.parse(response.body)
10
+ expect(response_body['paths']).not_to be_empty
11
+ end
12
+ end
13
+ end
File without changes
File without changes
@@ -72,15 +72,7 @@ module Meta
72
72
 
73
73
  # 运行过程中首先会解析参数
74
74
  def parse_parameters(parameters_meta)
75
- self.parameters = parameters_meta.map do |name, options|
76
- schema = options[:schema]
77
- value = if options[:in] == 'header'
78
- schema.filter(request.get_header('HTTP_' + name.to_s.upcase.gsub('-', '_')))
79
- else
80
- schema.filter(request.params[name.to_s])
81
- end
82
- [name, value]
83
- end.to_h.freeze
75
+ self.parameters = parameters_meta.filter(request).freeze
84
76
  end
85
77
 
86
78
  # parse_params 不再解析参数了,而只是设置 @params_schema,并清理父路由解析的变量
@@ -112,8 +104,8 @@ module Meta
112
104
  else
113
105
  # 渲染多键值结点
114
106
  new_hash = renders.map do |key, render_content|
115
- schema = entity_schema.properties[key]
116
- raise Errors::RenderingError, "渲染的键名 `#{key}` 不存在,请检查实体定义以确认是否有拼写错误" if schema.nil?
107
+ raise Errors::RenderingError, "渲染的键名 `#{key}` 不存在,请检查实体定义以确认是否有拼写错误" unless entity_schema.properties.key?(key)
108
+ schema = entity_schema.properties[key].schema(:render)
117
109
 
118
110
  filtered = schema.filter(render_content[:value], **render_content[:options], execution: self, stage: :render)
119
111
  [key, filtered]
@@ -1,14 +1,15 @@
1
1
  # frozen_string_literal: true
2
+ require_relative 'parameters'
2
3
 
3
4
  module Meta
4
- class Meta
5
+ class Metadata
5
6
  attr_reader :title, :description, :tags, :parameters, :request_body, :responses
6
7
 
7
8
  def initialize(title: nil, description: nil, tags: [], parameters: {}, request_body: nil, responses: nil)
8
9
  @title = title
9
10
  @description = description
10
11
  @tags = tags
11
- @parameters = parameters
12
+ @parameters = parameters.is_a?(Parameters) ? parameters : Parameters.new(parameters)
12
13
  @request_body = request_body
13
14
  @responses = responses || { 204 => nil }
14
15
  end
@@ -24,18 +25,7 @@ module Meta
24
25
  operation_object[:tags] = tags unless tags.empty?
25
26
  operation_object[:description] = description if description
26
27
 
27
- operation_object[:parameters] = parameters.map do |name, options|
28
- property_options = options[:schema].param_options
29
- {
30
- name: name,
31
- in: options[:in],
32
- required: property_options[:required] || nil,
33
- description: property_options[:description] || '',
34
- schema: {
35
- type: property_options[:type]
36
- }
37
- }.compact
38
- end unless parameters.empty?
28
+ operation_object[:parameters] = parameters.to_swagger_doc
39
29
 
40
30
  if request_body
41
31
  schema = request_body.to_schema_doc(stage: :param, schemas: schemas)
@@ -65,7 +55,7 @@ module Meta
65
55
  end
66
56
 
67
57
  def self.new(meta = {})
68
- meta.is_a?(Meta) ? meta : super(**meta)
58
+ meta.is_a?(Metadata) ? meta : super(**meta)
69
59
  end
70
60
  end
71
61
  end
@@ -0,0 +1,47 @@
1
+ # 仅仅处理 URL、Query、Header 中的参数
2
+
3
+ module Meta
4
+ class Parameters
5
+ extend Forwardable
6
+
7
+ attr_reader :parameters
8
+
9
+ def initialize(parameters)
10
+ @parameters = parameters
11
+ end
12
+
13
+ def filter(request)
14
+ parameters.map do |name, options|
15
+ schema = options[:schema]
16
+ value = if options[:in] == 'header'
17
+ schema.filter(request.get_header('HTTP_' + name.to_s.upcase.gsub('-', '_')))
18
+ else
19
+ schema.filter(request.params[name.to_s])
20
+ end
21
+ [name, value]
22
+ end.to_h
23
+ end
24
+
25
+ def to_swagger_doc
26
+ parameters.map do |name, options|
27
+ property_options = options[:schema].options
28
+ {
29
+ name: name,
30
+ in: options[:in],
31
+ required: property_options[:required] || nil,
32
+ description: property_options[:description] || '',
33
+ schema: {
34
+ type: property_options[:type]
35
+ }
36
+ }.compact
37
+ end unless parameters.empty?
38
+ end
39
+
40
+ def merge(parameters)
41
+ parameters_hash = parameters.is_a?(Parameters) ? parameters.parameters : parameters
42
+ Parameters.new(self.parameters.merge(parameters_hash))
43
+ end
44
+
45
+ def_delegators :parameters, :key?, :empty?
46
+ end
47
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative 'execution'
4
4
  require_relative 'path_matching_mod'
5
- require_relative 'meta'
5
+ require_relative 'metadata'
6
6
 
7
7
  module Meta
8
8
  class Route
@@ -13,7 +13,7 @@ module Meta
13
13
  def initialize(path: '', method: :all, meta: {}, actions: [])
14
14
  @path = Utils::Path.normalize_path(path)
15
15
  @method = method
16
- @meta = Meta.new(meta)
16
+ @meta = Metadata.new(meta)
17
17
  @actions = actions
18
18
  end
19
19
 
@@ -4,21 +4,22 @@ module Meta
4
4
  module JsonSchema
5
5
  class ArraySchemaBuilder
6
6
  def initialize(options, &block)
7
- raise 'type 选项必须是 array' if !options[:type].nil? && options[:type] != 'array'
8
-
9
- options = options.merge(type: 'array')
10
- @options = options
11
-
12
- items_options = options.delete(:items) || {}
13
- if object_property?(items_options, block)
14
- @items = ObjectSchemaBuilder.new(items_options, &block).to_schema
7
+ options = options.dup
8
+ if options[:items]
9
+ items_options = options.delete(:items)
10
+ elsif options[:ref]
11
+ items_options = { ref: options.delete(:ref) }
12
+ elsif options[:dynamic_ref]
13
+ items_options = { dynamic_ref: options.delete(:dynamic_ref) }
15
14
  else
16
- @items = BaseSchema.new(items_options)
15
+ items_options = {}
17
16
  end
17
+ @items_schema = SchemaBuilderTool.build(items_options, &block)
18
+ @base_options = options
18
19
  end
19
20
 
20
21
  def to_schema
21
- ArraySchema.new(@items, @options)
22
+ ArraySchema.new(@items_schema, @base_options)
22
23
  end
23
24
 
24
25
  def object_property?(options, block)
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../schemas/dynamic_schema'
4
+
5
+ module Meta
6
+ module JsonSchema
7
+ class DynamicSchemaBuilder
8
+ def initialize(options)
9
+ options = options.dup
10
+ @dynamic_schema_options = options.delete(:dynamic_ref)
11
+ @base_options = options
12
+ end
13
+
14
+ def to_schema
15
+ DynamicSchema.new(
16
+ @dynamic_schema_options[:resolve],
17
+ one_of: @dynamic_schema_options[:one_of] && @dynamic_schema_options[:one_of].map { |schema| RefSchema.new(schema.to_schema) },
18
+ **@base_options
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../schemas/properties'
4
+
3
5
  module Meta
4
6
  module JsonSchema
5
7
  class ObjectSchemaBuilder
@@ -41,23 +43,7 @@ module Meta
41
43
  end
42
44
 
43
45
  def property(name, options = {}, &block)
44
- name = name.to_sym
45
- # REVIEW: 为何要 dup,删掉试试
46
- options = options.dup
47
-
48
- using = options[:using]
49
- if using.respond_to?(:to_schema)
50
- schema = using.to_schema
51
- if options[:type] == 'array'
52
- @properties[name] = ArraySchema.new(schema, options)
53
- else
54
- @properties[name] = schema.dup(options)
55
- end
56
- elsif using.is_a?(Proc) || using.is_a?(Hash) || using.nil?
57
- @properties[name] = SchemaBuilderTool.build(options, &block)
58
- else
59
- raise "非法的 `using` 选项,应传递具有 `to_schema` 方法(如 Entity、Schema 等)或 Hash、Proc(动态生成 Schema)。当前传递:#{block}"
60
- end
46
+ @properties[name.to_sym] = Properties.build_property(options, ->(options) { SchemaBuilderTool.build(options, &block) })
61
47
  end
62
48
 
63
49
  alias expose property
@@ -70,7 +56,7 @@ module Meta
70
56
  end
71
57
 
72
58
  def to_schema(locked_options = nil)
73
- ObjectSchema.new(properties: @properties, object_validations: @validations, options: @options, locked_options: locked_options, schema_name_resolver: @schema_name_resolver)
59
+ ObjectSchema.new(properties: @properties, options: @options, locked_options: locked_options, schema_name_resolver: @schema_name_resolver)
74
60
  end
75
61
 
76
62
  def lock(key, value)
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../schemas/ref_schema'
4
+
5
+ module Meta
6
+ module JsonSchema
7
+ class RefSchemaBuilder
8
+ def initialize(options)
9
+ options = options.dup
10
+ @ref_schema_builder = options.delete(:ref)
11
+ @base_options = options
12
+ end
13
+
14
+ def to_schema
15
+ RefSchema.new(@ref_schema_builder.to_schema, @base_options)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,12 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'ref_schema_builder'
4
+ require_relative 'dynamic_schema_builder'
5
+ require_relative 'array_schema_builder'
6
+ require_relative 'object_schema_builder'
7
+
3
8
  module Meta
4
9
  module JsonSchema
5
10
  class SchemaBuilderTool
6
11
  class << self
12
+ SCHEMA_BUILDER_OPTIONS = Utils::KeywordArgs::Builder.build do
13
+ permit_extras true
14
+
15
+ key :ref, alias_names: [:using]
16
+ key :dynamic_ref, alias_names: [:dynamic_using], normalizer: ->(value) { value.is_a?(Proc) ? { resolve: value } : value }
17
+ end
7
18
  def build(options = {}, &block)
19
+ options = SCHEMA_BUILDER_OPTIONS.check(options)
20
+
8
21
  if apply_array_schema?(options, block)
9
22
  ArraySchemaBuilder.new(options, &block).to_schema
23
+ elsif apply_ref_schema?(options, block)
24
+ RefSchemaBuilder.new(options).to_schema
25
+ elsif apply_dynamic_schema?(options, block)
26
+ DynamicSchemaBuilder.new(options).to_schema
10
27
  elsif apply_object_schema?(options, block)
11
28
  ObjectSchemaBuilder.new(options, &block).to_schema
12
29
  else
@@ -17,12 +34,20 @@ module Meta
17
34
  private
18
35
 
19
36
  def apply_array_schema?(options, block)
20
- options[:type] == 'array' && (options[:items] || block)
37
+ options[:type] == 'array' && (options[:items] || options[:ref] || options[:dynamic_ref] || block)
21
38
  end
22
39
 
23
40
  def apply_object_schema?(options, block)
24
41
  (options[:type] == 'object' || options[:type].nil?) && (options[:properties] || block)
25
42
  end
43
+
44
+ def apply_ref_schema?(options, block)
45
+ options[:ref] != nil
46
+ end
47
+
48
+ def apply_dynamic_schema?(options, block)
49
+ options[:dynamic_ref] != nil
50
+ end
26
51
  end
27
52
  end
28
53
  end
@@ -25,12 +25,12 @@ module Meta
25
25
  end
26
26
  end
27
27
 
28
- def to_schema_doc(user_options = {})
29
- stage_options = user_options[:stage] == :param ? @param_options : @render_options
28
+ def to_schema_doc(**user_options)
29
+ stage_options = options
30
30
 
31
31
  schema = {
32
32
  type: 'array',
33
- items: @items ? @items.to_schema_doc(user_options) : {}
33
+ items: @items ? @items.to_schema_doc(**user_options) : {}
34
34
  }
35
35
  schema[:description] = stage_options[:description] if stage_options[:description]
36
36
  schema
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../utils/kwargs/check'
3
4
  require_relative '../support/schema_options'
4
5
 
5
6
  module Meta
@@ -12,63 +13,30 @@ module Meta
12
13
  # `options` 属性都是描述该对象的本身,而不是深层的属性。
13
14
  #
14
15
  # 较常出现错误的是数组,`options` 是描述数组的,而不是描述数组内部元素的。
15
- attr_reader :param_options, :render_options
16
+ attr_reader :options
16
17
 
17
- # 传递 path 参数主要是为了渲染 Parameter 文档时需要
18
18
  def initialize(options = {})
19
- @param_options, @render_options = SchemaOptions.normalize_to_param_and_render(options)
19
+ @options = SchemaOptions.normalize(options)
20
20
  end
21
21
 
22
- def options(stage, key = nil)
23
- stage_options = case stage
24
- when :param
25
- param_options
26
- when :render
27
- render_options
28
- when nil
29
- merged_options
30
- else
31
- raise "非法的 stage 参数,它只允许取值 :param、:render 或 nil,却收到 #{stage.inspect}"
32
- end
33
- if key
34
- stage_options ? stage_options[key] : nil
35
- else
36
- stage_options
37
- end
38
- end
22
+ USER_OPTIONS_CHECKER = Utils::KeywordArgs::Builder.build do
23
+ key :stage, :execution, :object_value, :type_conversion, :validation
39
24
 
40
- # params 和 render 的选项合并
41
- def merged_options
42
- (param_options || {}).merge(render_options || {})
25
+ # 以下三个是 ObjectSchema 需要的选项
26
+ key :discard_missing, :exclude, :scope
43
27
  end
44
28
 
45
29
  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
-
62
- stage_options = options(user_options[:stage])
63
-
64
- value = resolve_value(user_options) if stage_options[:value]
65
- value = JsonSchema::Presenters.present(stage_options[:presenter], value) if stage_options[:presenter]
66
- value = stage_options[:default] if value.nil? && stage_options.key?(:default)
67
- value = stage_options[:convert].call(value) if stage_options[:convert]
30
+ user_options = USER_OPTIONS_CHECKER.check(user_options)
31
+
32
+ value = resolve_value(user_options) if options[:value]
33
+ value = JsonSchema::Presenters.present(options[:presenter], value) if options[:presenter]
34
+ value = options[:default] if value.nil? && options.key?(:default)
35
+ value = options[:convert].call(value) if options[:convert]
68
36
 
69
37
  # 第一步,转化值。
70
38
  # 需要注意的是,对象也可能被转换,因为并没有深层次的结构被声明。
71
- type = stage_options[:type]
39
+ type = options[:type]
72
40
  unless user_options[:type_conversion] == false || type.nil? || value.nil?
73
41
  begin
74
42
  value = JsonSchema::TypeConverter.convert_value(value, type)
@@ -78,23 +46,17 @@ module Meta
78
46
  end
79
47
 
80
48
  # 第二步,做校验。
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
49
+ validate!(value, options) unless user_options[:validation] == false
88
50
 
89
51
  value
90
52
  end
91
53
 
92
- def value?(stage)
93
- options(stage, :value) != nil
54
+ def value?
55
+ options[:value] != nil
94
56
  end
95
57
 
96
58
  def resolve_value(user_options)
97
- value_proc = options(user_options[:stage], :value)
59
+ value_proc = options[:value]
98
60
  value_proc_params = (value_proc.lambda? && value_proc.arity == 0) ? [] : [user_options[:object_value]]
99
61
 
100
62
  if user_options[:execution]
@@ -104,21 +66,19 @@ module Meta
104
66
  end
105
67
  end
106
68
 
107
- def to_schema_doc(user_options = {})
108
- stage_options = options(user_options[:stage])
109
-
110
- return Presenters.to_schema_doc(stage_options[:presenter], stage_options) if stage_options[:presenter]
69
+ # 生成 Swagger 文档的 schema 格式。
70
+ #
71
+ # 选项:
72
+ # - stage: 传递 :param :render
73
+ # - schemas: 用于保存已经生成的 Schema
74
+ # - presenter: 兼容 Grape 框架的实体类
75
+ def to_schema_doc(**user_options)
76
+ return Presenters.to_schema_doc(options[:presenter], options) if options[:presenter]
111
77
 
112
78
  schema = {}
113
- schema[:type] = stage_options[:type] if stage_options[:type]
114
- schema[:description] = stage_options[:description] if stage_options[:description]
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
79
+ schema[:type] = options[:type] if options[:type]
80
+ schema[:description] = options[:description] if options[:description]
81
+ schema[:enum] = options[:allowable] if options[:allowable]
122
82
 
123
83
  schema
124
84
  end
@@ -0,0 +1,29 @@
1
+ require_relative 'base_schema'
2
+
3
+ module Meta
4
+ module JsonSchema
5
+ class DynamicSchema < BaseSchema
6
+ def initialize(resolver, one_of: nil, **base_options)
7
+ super(base_options)
8
+
9
+ @resolver = resolver
10
+ @one_of = one_of
11
+ end
12
+
13
+ def filter(value, user_options = {})
14
+ value = super(value, user_options)
15
+ schema = @resolver.call(value).to_schema
16
+ schema.filter(value, user_options)
17
+ end
18
+
19
+ def to_schema_doc(user_options)
20
+ schema = { type: 'object' }
21
+ schema[:oneOf] = @one_of.map do |schema|
22
+ schema.to_schema_doc(user_options)
23
+ end if @one_of
24
+
25
+ schema
26
+ end
27
+ end
28
+ end
29
+ end