Class: Dry::Schema::JSONSchema::SchemaCompiler Private

Inherits:
Object
  • Object
show all
Defined in:
lib/dry/schema/extensions/json_schema/schema_compiler.rb

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Direct Known Subclasses

OpenAPI::SchemaCompiler

Constant Summary collapse

UnknownConversionError =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

An error raised when a predicate cannot be converted

Class.new(StandardError)
IDENTITY =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(v, _) { v }.freeze
TO_INTEGER =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(v, _) { v.to_i }.freeze
PREDICATE_TO_TYPE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

{
  array?: {type: "array"},
  bool?: {type: "boolean"},
  date?: {type: "string", format: "date"},
  date_time?: {type: "string", format: "date-time"},
  decimal?: {type: "number"},
  float?: {type: "number"},
  hash?: {type: "object"},
  int?: {type: "integer"},
  nil?: {type: "null"},
  str?: {type: "string"},
  time?: {type: "string", format: "time"},
  min_size?: {minLength: TO_INTEGER},
  max_size?: {maxLength: TO_INTEGER},
  included_in?: {enum: ->(v, _) { v.to_a }},
  filled?: EMPTY_HASH,
  uri?: {format: "uri"},
  uuid_v1?: {
    pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-1[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
  },
  uuid_v2?: {
    pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-2[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
  },
  uuid_v3?: {
    pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
  },
  uuid_v4?: {
    pattern: "^[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}$"
  },
  uuid_v5?: {
    pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
  },
  gt?: {exclusiveMinimum: IDENTITY},
  gteq?: {mininum: IDENTITY},
  lt?: {exclusiveMaximum: IDENTITY},
  lteq?: {maximum: IDENTITY},
  odd?: {type: "integer", not: {multipleOf: 2}},
  even?: {type: "integer", multipleOf: 2}
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root: false, loose: false) ⇒ SchemaCompiler

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of SchemaCompiler.



61
62
63
64
65
66
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 61

def initialize(root: false, loose: false)
  @keys = EMPTY_HASH.dup
  @required = Set.new
  @root = root
  @loose = loose
end

Instance Attribute Details

#keysObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



58
59
60
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 58

def keys
  @keys
end

#requiredObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



58
59
60
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 58

def required
  @required
end

Instance Method Details

#call(ast) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



79
80
81
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 79

def call(ast)
  visit(ast)
end

#fetch_filled_options(type, _target) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 182

def fetch_filled_options(type, _target)
  case type
  when "string"
    {minLength: 1}
  when "array"
    raise_unknown_conversion_error!(:type, :array) unless loose?

    {not: {type: "null"}}
  else
    {not: {type: "null"}}
  end
end

#fetch_type_opts_for_predicate(name, rest, target) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



170
171
172
173
174
175
176
177
178
179
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 170

def fetch_type_opts_for_predicate(name, rest, target)
  type_opts = PREDICATE_TO_TYPE.fetch(name) do
    raise_unknown_conversion_error!(:predicate, name) unless loose?

    EMPTY_HASH
  end.dup
  type_opts.transform_values! { |v| v.respond_to?(:call) ? v.call(rest[0][1], target) : v }
  type_opts.merge!(fetch_filled_options(target[:type], target)) if name == :filled?
  type_opts
end

#loose?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


213
214
215
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 213

def loose?
  @loose
end

#merge_opts!(orig_opts, new_opts) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



196
197
198
199
200
201
202
203
204
205
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 196

def merge_opts!(orig_opts, new_opts)
  new_type = new_opts[:type]
  orig_type = orig_opts[:type]

  if orig_type && new_type && orig_type != new_type
    new_opts[:type] = [orig_type, new_type]
  end

  orig_opts.merge!(new_opts)
end

#raise_unknown_conversion_error!(type, name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 217

def raise_unknown_conversion_error!(type, name)
  message = <<~MSG
    Could not find an equivalent conversion for #{type} #{name.inspect}.

    This means that your generated JSON schema may be missing this validation.

    You can ignore this by generating the schema in "loose" mode, i.e.:
        my_schema.json_schema(loose: true)
  MSG

  raise UnknownConversionError, message.chomp
end

#root?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


208
209
210
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 208

def root?
  @root
end

#to_hashObject Also known as: to_h

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



69
70
71
72
73
74
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 69

def to_hash
  result = {}
  result[:$schema] = "http://json-schema.org/draft-06/schema#" if root?
  result.merge!(type: "object", properties: keys, required: required.to_a)
  result
end

#visit(node, opts = EMPTY_HASH) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



84
85
86
87
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 84

def visit(node, opts = EMPTY_HASH)
  meth, rest = node
  public_send(:"visit_#{meth}", rest, opts)
end

#visit_and(node, opts = EMPTY_HASH) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 104

def visit_and(node, opts = EMPTY_HASH)
  left, right = node

  # We need to know the type first to apply filled macro
  if left[1][0] == :filled?
    visit(right, opts)
    visit(left, opts)
  else
    visit(left, opts)
    visit(right, opts)
  end
end

#visit_each(node, opts = EMPTY_HASH) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



125
126
127
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 125

def visit_each(node, opts = EMPTY_HASH)
  visit(node, opts.merge(member: true))
end

#visit_implication(node, opts = EMPTY_HASH) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



118
119
120
121
122
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 118

def visit_implication(node, opts = EMPTY_HASH)
  node.each do |el|
    visit(el, **opts, required: false)
  end
end

#visit_key(node, opts = EMPTY_HASH) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



130
131
132
133
134
135
136
137
138
139
140
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 130

def visit_key(node, opts = EMPTY_HASH)
  name, rest = node

  if opts.fetch(:required, :true)
    required << name.to_s
  else
    opts.delete(:required)
  end

  visit(rest, opts.merge(key: name))
end

#visit_not(node, opts = EMPTY_HASH) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



143
144
145
146
147
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 143

def visit_not(node, opts = EMPTY_HASH)
  _name, rest = node

  visit_predicate(rest, opts)
end

#visit_predicate(node, opts = EMPTY_HASH) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 150

def visit_predicate(node, opts = EMPTY_HASH)
  name, rest = node

  if name.equal?(:key?)
    prop_name = rest[0][1]
    keys[prop_name] = {}
  else
    target = keys[opts[:key]]
    type_opts = fetch_type_opts_for_predicate(name, rest, target)

    if target[:type]&.include?("array")
      target[:items] ||= {}
      merge_opts!(target[:items], type_opts)
    else
      merge_opts!(target, type_opts)
    end
  end
end

#visit_set(node, opts = EMPTY_HASH) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/dry/schema/extensions/json_schema/schema_compiler.rb', line 90

def visit_set(node, opts = EMPTY_HASH)
  target = (key = opts[:key]) ? self.class.new : self

  node.map { |child| target.visit(child, opts) }

  return unless key

  target_info = opts[:member] ? {items: target.to_h} : target.to_h
  type = opts[:member] ? "array" : "object"

  keys.update(key => {**keys[key], type: type, **target_info})
end