lib//meta/json_schema/support/schema_options.rb in meta-api-0.1.2 vs lib//meta/json_schema/support/schema_options.rb in meta-api-0.2.0

- old
+ new

@@ -3,46 +3,115 @@ require_relative '../../utils/kwargs/builder' module Meta module JsonSchema module SchemaOptions - @default_options = { - scope: [], - required: false - } + BaseBuildOptions = Utils::Kwargs::Builder.build do + key :type, :items, :description, :presenter, :value, :default, :properties, :convert + key :validate, :required, :format + key :enum, alias_names: [:allowable] + key :ref, alias_names: [:using], normalizer: ->(entity) { entity } + key :dynamic_ref, alias_names: [:dynamic_using], normalizer: ->(value) { value.is_a?(Proc) ? { resolve: value } : value } + key :before, :after + key :if + end + module UserOptions + Common = Utils::Kwargs::Builder.build do + key :stage + key :scope, normalizer: ->(value) { + raise ArgumentError, 'scope 选项不可传递 nil' if value.nil? + value = [value] unless value.is_a?(Array) + value.map do |v| + # 只要加入了 Meta::Scope::Base 模块,就有与 Meta::Scope 一样的行为 + next v if v.is_a?(Meta::Scope::Base) + + # 将 v 类名化 + scope_name = v.to_s.split('_').map(&:capitalize).join + # 如果符号对应的类名不存在,就报错 + if !defined?(::Scopes) || !::Scopes.const_defined?(scope_name) + raise NameError, "未找到常量 Scopes::#{scope_name}。如果你用的是命名 Scope(字符串或符号),则检查一下是不是拼写错误" + end + # 返回对应的常量 + ::Scopes.const_get(scope_name) + end.compact + } + + handle_extras :merged + end + ToDoc = Utils::Kwargs::Builder.build(Common) do + key :schema_docs_mapping, :defined_scopes_mapping + end + Filter = Utils::Kwargs::Builder.build(Common) do + key :discard_missing, :exclude, :extra_properties, :type_conversion, :validation + key :execution, :user_data, :object_value + end + end + class << self + def fix_type_option!(options) + if options[:type].is_a?(Class) + # 修复 type 为自定义类的情形 + the_class = options[:type] + # 修复 param 选项 + options[:param] = {} if options[:param].nil? + make_after_cast_to_class(options[:param], the_class) if options[:param] + # 修复 render 选项 + options[:render] = {} if options[:render].nil? + make_before_match_to_class(options[:render], the_class) if options[:render] + # 最终确保 type 为 object + options.merge!(type: 'object') + end + end + def divide_to_param_and_render(options) common_opts = (options || {}).dup param_opts = common_opts.delete(:param) render_opts = common_opts.delete(:render) param_opts = merge_common_to_stage(common_opts, param_opts) render_opts = merge_common_to_stage(common_opts, render_opts) [param_opts, render_opts, common_opts] end - def normalize(options) - # 只要 options 中设置为 nil 的选项没有明确的意义,则下行代码是永远有效的 - options = (@default_options.compact).merge(options.compact) - if options[:using] - if options[:type].nil? - options[:type] = 'object' - elsif options[:type] != 'object' && options[:type] != 'array' - raise "当使用 using 时,type 必须声明为 object 或 array" - end - end - - options - end - private def merge_common_to_stage(common_opts, stage_opts) stage_opts = {} if stage_opts.nil? || stage_opts == true stage_opts = common_opts.merge(stage_opts) if stage_opts stage_opts end + + def make_after_cast_to_class(options, the_class) + if options[:after].nil? + options[:after] = ->(value) { the_class.new(value) } + else + # 如果用户自定义了 after,那么我们需要在 after 之后再包一层 + original_after_block = options[:after] + options[:after] = ->(value) do + value = instance_exec(value, &original_after_block) + the_class.new(value) + end + end + end + + def make_before_match_to_class(options, the_class) + match_class = ->(value) do + raise ValidationError, "value 必须是 #{the_class} 类型" unless value.is_a?(the_class) + value + end + if options[:before].nil? + options[:before] = match_class + else + # 如果用户自定义了 before,那么我们需要在 before 之前再包一层 + original_before_block = options[:before] + options[:before] = ->(value) do + value = match_class.call(value) + instance_exec(value, &original_before_block) + end + end + end + end end end end