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:



100
101
102
103
104
# File 'lib/dry/schema/dsl.rb', line 100

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:



132
133
134
# File 'lib/dry/schema/dsl.rb', line 132

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:



275
276
277
278
# File 'lib/dry/schema/dsl.rb', line 275

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)


246
247
248
# File 'lib/dry/schema/dsl.rb', line 246

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:



260
261
262
263
# File 'lib/dry/schema/dsl.rb', line 260

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:



203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/dry/schema/dsl.rb', line 203

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



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

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

#configConfig

Returns Configuration object exposed via `#configure` method.

Returns:

  • (Config)

    Configuration object exposed via `#configure` method



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

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:



120
121
122
123
# File 'lib/dry/schema/dsl.rb', line 120

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)


329
330
331
# File 'lib/dry/schema/dsl.rb', line 329

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)


366
367
368
369
370
371
372
# File 'lib/dry/schema/dsl.rb', line 366

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.



350
351
352
# File 'lib/dry/schema/dsl.rb', line 350

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


359
360
361
# File 'lib/dry/schema/dsl.rb', line 359

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)


181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/dry/schema/dsl.rb', line 181

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



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

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:



222
223
224
225
226
227
228
229
# File 'lib/dry/schema/dsl.rb', line 222

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

#merge_types(op_class, lhs, rhs) ⇒ 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.



382
383
384
# File 'lib/dry/schema/dsl.rb', line 382

def merge_types(op_class, lhs, rhs)
  types_merger.(op_class, lhs, rhs)
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)


305
306
307
# File 'lib/dry/schema/dsl.rb', line 305

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:



168
169
170
# File 'lib/dry/schema/dsl.rb', line 168

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



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

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



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

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)



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

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:



152
153
154
# File 'lib/dry/schema/dsl.rb', line 152

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)


340
341
342
343
344
345
346
347
# File 'lib/dry/schema/dsl.rb', line 340

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)


317
318
319
320
321
322
# File 'lib/dry/schema/dsl.rb', line 317

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:



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

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

#to_ruleRuleApplier

Cast this DSL into a rule object

Returns:



234
235
236
# File 'lib/dry/schema/dsl.rb', line 234

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)


294
295
296
297
298
# File 'lib/dry/schema/dsl.rb', line 294

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

#typesObject

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.

This DSL's type map merged with any parent type maps



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

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