Class: Dry::Schema::DSL

Inherits:
Object
  • Object
show all
Extended by:
Initializer
Defined in:
lib/dry/schema/dsl.rb

Overview

The schema definition DSL class

The DSL is exposed by:

- `Schema.define`
- `Schema.Params`
- `Schema.JSON`
- `Schema::Params.define` - use with sub-classes
- `Schema::JSON.define` - use with sub-classes

Examples:

class-based definition

class UserSchema < Dry::Schema::Params
  define do
    required(:name).filled
    required(:age).filled(:integer, gt: 18)
  end
end

user_schema = UserSchema.new
user_schema.(name: 'Jame', age: 21)

instance-based definition shortcut

UserSchema = Dry::Schema.Params do
  required(:name).filled
  required(:age).filled(:integer, gt: 18)
end

UserSchema.(name: 'Jame', age: 21)

Constant Summary collapse

Types =
Schema::Types

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.new(**options, &block) ⇒ DSL

Build a new DSL object and evaluate provided block

Parameters:

  • options (Hash)

Options Hash (**options):

  • :processor (Class)

    The processor type (`Params`, `JSON` or a custom sub-class)

  • :compiler (Compiler)

    An instance of a rule compiler (must be compatible with `Schema::Compiler`) (optional)

  • :parent (Array[DSL])

    One or more instances of the parent DSL (optional)

  • :config (Config)

    A configuration object (optional)

Returns:

See Also:



96
97
98
99
100
# File 'lib/dry/schema/dsl.rb', line 96

def self.new(**options, &block)
  dsl = super
  dsl.instance_eval(&block) if block
  dsl
end

Instance Method Details

#[](name) ⇒ Macros::Core

Return a macro with the provided name

Parameters:

  • name (Symbol)

Returns:



128
129
130
# File 'lib/dry/schema/dsl.rb', line 128

def [](name)
  macros.detect { |macro| macro.name.equal?(name) }
end

#after(key, &block) ⇒ DSL

Method allows steps injection to the processor

Examples:

after(:rule_applier) do |input|
  input.compact
end

Returns:



270
271
272
273
# File 'lib/dry/schema/dsl.rb', line 270

def after(key, &block)
  steps.after(key, &block)
  self
end

#arrayDry::Types::Array::Member

A shortcut for defining an array type with a member

Examples:

required(:tags).filled(array[:string])

Returns:

  • (Dry::Types::Array::Member)


241
242
243
# File 'lib/dry/schema/dsl.rb', line 241

def array
  -> member_type { type_registry["array"].of(resolve_type(member_type)) }
end

#before(key, &block) ⇒ DSL

Method allows steps injection to the processor

Examples:

before(:rule_applier) do |input|
  input.compact
end

Returns:



255
256
257
258
# File 'lib/dry/schema/dsl.rb', line 255

def before(key, &block)
  steps.before(key, &block)
  self
end

#callProcessor, ...

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.

Build a processor based on DSL's definitions

Returns:



198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/dry/schema/dsl.rb', line 198

def call
  all_steps = parents.map(&:steps) + [steps]

  result_steps = all_steps.inject { |result, steps| result.merge(steps) }

  result_steps[:key_validator] = key_validator if config.validate_keys
  result_steps[:key_coercer] = key_coercer
  result_steps[:value_coercer] = value_coercer
  result_steps[:rule_applier] = rule_applier
  result_steps[:filter_schema] = filter_schema.rule_applier if filter_rules?

  processor_type.new(schema_dsl: self, steps: result_steps)
end

#compilerCompiler

Returns The rule compiler object.

Returns:

  • (Compiler)

    The rule compiler object



57
# File 'lib/dry/schema/dsl.rb', line 57

option :compiler, default: -> { Compiler.new }

#configConfig

Returns Configuration object exposed via `#configure` method.

Returns:

  • (Config)

    Configuration object exposed via `#configure` method



72
# File 'lib/dry/schema/dsl.rb', line 72

option :config, optional: true, default: proc { default_config }

#configure(&block) ⇒ DSL

Provide customized configuration for your schema

Examples:

Dry::Schema.define do
  configure do |config|
    config.messages.backend = :i18n
  end
end

Returns:

See Also:



116
117
118
119
# File 'lib/dry/schema/dsl.rb', line 116

def configure(&block)
  config.configure(&block)
  self
end

#custom_type?(name) ⇒ Bool

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.

Check if a custom type was set under provided key name

Returns:

  • (Bool)


324
325
326
# File 'lib/dry/schema/dsl.rb', line 324

def custom_type?(name)
  !types[name].meta[:default].equal?(true)
end

#filter_rules?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.

Check if any filter rules were defined

Returns:

  • (Boolean)


361
362
363
364
365
366
367
# File 'lib/dry/schema/dsl.rb', line 361

def filter_rules?
  if instance_variable_defined?("@filter_schema_dsl") && !filter_schema_dsl.macros.empty?
    return true
  end

  parents.any?(&:filter_rules?)
end

#filter_schemaObject

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.



345
346
347
# File 'lib/dry/schema/dsl.rb', line 345

def filter_schema
  filter_schema_dsl.call
end

#filter_schema_dslObject

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.

Build an input schema DSL used by `filter` API

See Also:

  • Macros::Value#filter


354
355
356
# File 'lib/dry/schema/dsl.rb', line 354

def filter_schema_dsl
  @filter_schema_dsl ||= new(parent: parent_filter_schemas)
end

#key(name, macro:, &block) ⇒ Macros::Key

A generic method for defining keys

Parameters:

  • name (Symbol)

    The key name

  • macro (Class)

    The macro sub-class (ie `Macros::Required` or any other `Macros::Key` subclass)

Returns:

Raises:

  • (ArgumentError)


176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/dry/schema/dsl.rb', line 176

def key(name, macro:, &block)
  raise ArgumentError, "Key +#{name}+ is not a symbol" unless name.is_a?(::Symbol)

  set_type(name, Types::Any.meta(default: true))

  macro = macro.new(
    name: name,
    compiler: compiler,
    schema_dsl: self,
    filter_schema_dsl: filter_schema_dsl
  )

  macro.value(&block) if block
  macros << macro
  macro
end

#macrosArray

Returns An array with macros defined within the DSL.

Returns:

  • (Array)

    An array with macros defined within the DSL



63
# File 'lib/dry/schema/dsl.rb', line 63

option :macros, default: -> { EMPTY_ARRAY.dup }

#merge(other) ⇒ DSL

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.

Merge with another dsl

Returns:



217
218
219
220
221
222
223
224
# File 'lib/dry/schema/dsl.rb', line 217

def merge(other)
  new(
    parent: parents + other.parents,
    macros: macros + other.macros,
    types: types.merge(other.types),
    steps: steps.merge(other.steps)
  )
end

#new(**options, &block) ⇒ Dry::Types::Safe

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.

Return a new DSL instance using the same processor type

Returns:

  • (Dry::Types::Safe)


300
301
302
# File 'lib/dry/schema/dsl.rb', line 300

def new(**options, &block)
  self.class.new(**options, processor_type: processor_type, config: config, &block)
end

#optional(name, &block) ⇒ Macros::Optional

Define an optional key

This works exactly the same as `required` except that if a key is not present rules will not be applied

Parameters:

  • name (Symbol)

    The key name

Returns:

See Also:



164
165
166
# File 'lib/dry/schema/dsl.rb', line 164

def optional(name, &block)
  key(name, macro: Macros::Optional, &block)
end

#parentObject

The parent (last from parents) which is used for copying non mergeable configuration

Returns:

  • DSL



69
# File 'lib/dry/schema/dsl.rb', line 69

option :parent, Types::Coercible::Array, default: -> { EMPTY_ARRAY.dup }, as: :parents

#pathPath, Array

Returns Path under which the schema is defined.

Returns:

  • (Path, Array)

    Path under which the schema is defined



78
# File 'lib/dry/schema/dsl.rb', line 78

option :path, -> *args { Path[*args] if args.any? }, default: proc { EMPTY_ARRAY }

#processor_typeCompiler

Returns The type of the processor (Params, JSON, or a custom sub-class).

Returns:

  • (Compiler)

    The type of the processor (Params, JSON, or a custom sub-class)



60
# File 'lib/dry/schema/dsl.rb', line 60

option :processor_type, default: -> { Processor }

#required(name, &block) ⇒ Macros::Required

Define a required key

Examples:

required(:name).filled

required(:age).value(:integer)

required(:user_limit).value(:integer, gt?: 0)

required(:tags).filled { array? | str? }

Parameters:

  • name (Symbol)

    The key name

Returns:



148
149
150
# File 'lib/dry/schema/dsl.rb', line 148

def required(name, &block)
  key(name, macro: Macros::Required, &block)
end

#resolve_type(spec) ⇒ Dry::Types::Type

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.

Resolve type object from the provided spec

Parameters:

  • spec (Symbol, Array<Symbol>, Dry::Types::Type)

Returns:

  • (Dry::Types::Type)


335
336
337
338
339
340
341
342
# File 'lib/dry/schema/dsl.rb', line 335

def resolve_type(spec)
  case spec
  when ::Dry::Types::Type then spec
  when ::Array then spec.map { |s| resolve_type(s) }.reduce(:|)
  else
    type_registry[spec]
  end
end

#set_type(name, spec) ⇒ Dry::Types::Safe

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.

Set a type for the given key name

Parameters:

  • name (Symbol)

    The key name

  • spec (Symbol, Array<Symbol>, Dry::Types::Type)

    The type spec or a type object

Returns:

  • (Dry::Types::Safe)


312
313
314
315
316
317
# File 'lib/dry/schema/dsl.rb', line 312

def set_type(name, spec)
  type = resolve_type(spec)
  meta = {required: false, maybe: type.optional?}

  types[name] = type.meta(meta)
end

#stepsProcessorSteps

Returns Steps for the processor.

Returns:



75
# File 'lib/dry/schema/dsl.rb', line 75

option :steps, default: proc { ProcessorSteps.new }

#to_ruleRuleApplier

Cast this DSL into a rule object

Returns:



229
230
231
# File 'lib/dry/schema/dsl.rb', line 229

def to_rule
  call.to_rule
end

#type_schemaDry::Types::Safe

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.

Return type schema used by the value coercer

Returns:

  • (Dry::Types::Safe)


289
290
291
292
293
# File 'lib/dry/schema/dsl.rb', line 289

def type_schema
  our_schema = type_registry["hash"].schema(types).lax
  schemas = [*parents.map(&:type_schema), our_schema]
  schemas.inject { |result, schema| result.schema(schema.to_a) }
end

#typesCompiler

Returns A key=>type map defined within the DSL.

Returns:

  • (Compiler)

    A key=>type map defined within the DSL



66
# File 'lib/dry/schema/dsl.rb', line 66

option :types, default: -> { EMPTY_HASH.dup }