Class: Dry::Validation::Rust::Contract

Inherits:
Object
  • Object
show all
Defined in:
lib/dry/validation/rust/contract.rb,
lib/dry/validation/rust/contract/result.rb,
lib/dry/validation/rust/contract/values.rb

Overview

A validation contract that combines a compiled schema with ordered rules.

Examples:

Defining a contract

class UserContract < Dry::Validation::Rust::Contract
  params do
    required(:email).filled(:string)
  end

  rule(:email) do
    key.failure("is invalid") unless value.include?("@")
  end
end

Defined Under Namespace

Classes: OptionDefinition, Result, Values

Constant Summary collapse

Undefined =
Object.new.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(default_context: {}, **options) ⇒ Contract

Creates a contract with optional default context and declared options.

Parameters:

  • default_context (Hash) (defaults to: {})

    context merged with call-specific context

  • options (Hash{Symbol => Object})

    values for declared options

Raises:

  • (ArgumentError)

    if an option is missing or unknown



253
254
255
256
# File 'lib/dry/validation/rust/contract.rb', line 253

def initialize(default_context: {}, **options)
  @default_context = default_context
  initialize_options(options)
end

Instance Attribute Details

#default_contextHash (readonly)

Returns context merged into every call to this contract.

Returns:

  • (Hash)

    context merged into every call to this contract



245
246
247
# File 'lib/dry/validation/rust/contract.rb', line 245

def default_context
  @default_context
end

Class Method Details

.build(options = {}) { ... } ⇒ Contract

Builds an anonymous contract instance, optionally configured by a block.

Examples:

Building a one-off contract

contract = Contract.build { params { required(:name).filled(:string) } }
contract.call(name: "Ada").success? # => true

Parameters:

  • options (Hash{Symbol => Object}) (defaults to: {})

    options passed to the new instance

Yields:

  • anonymous contract class definition

Returns:

  • (Contract)

    a configured anonymous contract instance



186
187
188
# File 'lib/dry/validation/rust/contract.rb', line 186

def build(options = {}, &)
  Class.new(self, &).new(**options)
end

.configConfig

Returns this contract class's configuration.

Returns:

  • (Config)

    mutable schema configuration for this class



41
42
43
# File 'lib/dry/validation/rust/contract.rb', line 41

def config
  @config ||= Config.new
end

.import_predicates_as_macrosClass

Enables supported predicates to be resolved as rule macros.

Returns:

  • (Class)

    this contract class



172
173
174
175
# File 'lib/dry/validation/rust/contract.rb', line 172

def import_predicates_as_macros
  @predicates_as_macros = true
  self
end

.inherited(child) ⇒ void

This method returns an undefined value.

Copies schema configuration and macros when a contract is inherited.

Parameters:

  • child (Class)

    subclass inheriting this contract



32
33
34
35
36
# File 'lib/dry/validation/rust/contract.rb', line 32

def inherited(child)
  super
  child.instance_variable_set(:@config, config.dup)
  child.instance_variable_set(:@macro_registry, MacroRegistry.new(macro_registry))
end

.json(*external_schemas) { ... } ⇒ Schema

Defines or returns a JSON-mode schema for this contract.

Examples:

Defining a JSON schema

json { required(:name).filled(:string) }

Parameters:

  • external_schemas (Array<Schema>)

    schemas to import

Yields:

  • schema definition DSL

Returns:

  • (Schema)

    the compiled schema

Raises:



67
68
69
# File 'lib/dry/validation/rust/contract.rb', line 67

def json(*external_schemas, &)
  define_schema(:json, external_schemas, &)
end

.macro_registryMacroRegistry

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 this contract class's macro registry.

Returns:



165
166
167
# File 'lib/dry/validation/rust/contract.rb', line 165

def macro_registry
  @macro_registry ||= MacroRegistry.new(Rust.global_macros)
end

.option(name, default: Undefined, optional: false, **_options) ⇒ Class

Declares an injected contract option and its default behavior.

Examples:

Requiring an injected dependency

option :repository
rule(:email) { key.failure("is taken") if repository.taken?(value) }

Parameters:

  • name (Symbol, String)

    option reader name

  • default (Object, Proc) (defaults to: Undefined)

    value, or callable default, used when omitted

  • optional (Boolean) (defaults to: false)

    whether an omitted option is set to nil

Returns:

  • (Class)

    this contract class



127
128
129
130
131
132
133
134
135
136
# File 'lib/dry/validation/rust/contract.rb', line 127

def option(name, default: Undefined, optional: false, **_options)
  (@option_definitions ||= {})[name.to_sym] = OptionDefinition.new(
    name: name.to_sym,
    default: default,
    optional: optional
  )
  attr_reader name

  self
end

.option_definitionsHash{Symbol => OptionDefinition}

Returns option definitions inherited by this contract class.

Returns:



141
142
143
144
# File 'lib/dry/validation/rust/contract.rb', line 141

def option_definitions
  inherited = superclass.respond_to?(:option_definitions) ? superclass.option_definitions : {}
  inherited.merge(@option_definitions ||= {})
end

.own_rulesArray<Rule>

Returns rules declared directly on this contract class.

Returns:

  • (Array<Rule>)

    rules declared without inheritance



113
114
115
# File 'lib/dry/validation/rust/contract.rb', line 113

def own_rules
  @own_rules ||= []
end

.params(*external_schemas) { ... } ⇒ Schema

Defines or returns a Params-mode schema for this contract.

Examples:

Defining a params schema

params { required(:age).value(:integer) }

Parameters:

  • external_schemas (Array<Schema>)

    schemas to import

Yields:

  • schema definition DSL

Returns:

  • (Schema)

    the compiled schema

Raises:



54
55
56
# File 'lib/dry/validation/rust/contract.rb', line 54

def params(*external_schemas, &)
  define_schema(:params, external_schemas, &)
end

.register_macro(name) { ... } ⇒ Class

Registers a macro available to rules on this contract class.

Positional arguments are forwarded to the macro registry.

Examples:

Registering a macro

register_macro(:check_name) { key.failure("is invalid") unless value.match?(/\A[A-Z]/) }

Parameters:

  • name (Symbol, String)

    macro name

Yields:

  • macro implementation

Returns:

  • (Class)

    this contract class



155
156
157
158
# File 'lib/dry/validation/rust/contract.rb', line 155

def register_macro(name, *, &)
  macro_registry.register(name, *, &)
  self
end

.rule(*specs) { ... } ⇒ Rule

Registers a validation rule for one or more schema paths.

Examples:

Validating a nested field

rule("profile.email") { key.failure("is invalid") unless value.include?("@") }

Parameters:

  • specs (Array<Symbol, String, Array, Hash>)

    schema path specifications

Yields:

  • rule evaluated after successful schema validation

Returns:

  • (Rule)

    the registered rule

Raises:



94
95
96
97
98
99
100
# File 'lib/dry/validation/rust/contract.rb', line 94

def rule(*specs, &block)
  paths = specs.flat_map { |spec| Path.expand(spec) }
  ensure_valid_paths(paths) unless paths.empty?
  Rule.new(paths: paths, default_path: default_rule_path(specs, paths), block: block).tap do |new_rule|
    own_rules << new_rule
  end
end

.rulesArray<Rule>

Returns inherited and locally declared rules in execution order.

Returns:

  • (Array<Rule>)

    rules evaluated by instances of this contract



105
106
107
108
# File 'lib/dry/validation/rust/contract.rb', line 105

def rules
  inherited_rules = superclass.respond_to?(:rules) ? superclass.rules : []
  [*inherited_rules, *own_rules]
end

.schema(*external_schemas) { ... } ⇒ Schema

Defines or returns a schema-mode schema for this contract.

Examples:

Defining a schema-mode schema

schema { required(:name).filled(:string) }

Parameters:

  • external_schemas (Array<Schema>)

    schemas to import

Yields:

  • schema definition DSL

Returns:

  • (Schema)

    the compiled schema

Raises:



80
81
82
# File 'lib/dry/validation/rust/contract.rb', line 80

def schema(*external_schemas, &)
  define_schema(:schema, external_schemas, &)
end

.schema_definitionSchema?

Returns the compiled schema declared by this class or an ancestor.

Returns:

  • (Schema, nil)

    the compiled schema, if one has been declared



193
194
195
196
197
# File 'lib/dry/validation/rust/contract.rb', line 193

def schema_definition
  return @schema_definition if instance_variable_defined?(:@schema_definition)

  superclass.schema_definition if superclass.respond_to?(:schema_definition)
end

Instance Method Details

#[](input, context = {}) ⇒ Result

Validates input with bracket syntax; equivalent to #call.

Examples:

Calling with bracket syntax

contract[email: "ada@example.test"]

Parameters:

  • input (Hash)

    input accepted by the declared schema

  • context (Hash) (defaults to: {})

    context available to rule evaluators for this call

Returns:

  • (Result)

    finalized schema and rule validation result

Raises:



299
300
301
# File 'lib/dry/validation/rust/contract.rb', line 299

def [](input, context = {})
  call(input, context)
end

#call(input, context = {}) ⇒ Result

Validates input and returns a finalized result, including rule failures.

Examples:

Validating input

result = contract.call(email: "ada@example.test")
result.success? # => true

Parameters:

  • input (Hash)

    input accepted by the declared schema

  • context (Hash) (defaults to: {})

    context available to rule evaluators for this call

Returns:

  • (Result)

    finalized schema and rule validation result

Raises:



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/dry/validation/rust/contract.rb', line 268

def call(input, context = {})
  schema = self.class.schema_definition
  raise SchemaMissingError, "#{self.class} must define a schema" unless schema

  schema_result = schema.call(input)
  shared_context = default_context.merge(context)
  result = Result.new(schema_result, shared_context)
  schema_error_paths = schema_result.error_prefixes

  self.class.rules.each do |rule|
    if rule.each?
      execute_each(rule, result, shared_context)
    else
      next if rule.paths.any? { |path| dependency_error?(schema_error_paths, path) }

      execute_rule(rule, result, shared_context)
    end
  end

  result.finalize!
end

#inspectString

Returns a diagnostic representation of the compiled contract.

Returns:

  • (String)

    contract class, schema, and rules



327
328
329
# File 'lib/dry/validation/rust/contract.rb', line 327

def inspect
  "#<#{self.class} schema=#{self.class.schema_definition.inspect} rules=#{self.class.rules.inspect}>"
end

#macro_registered?(name) ⇒ 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 whether a macro can be resolved by this contract.

Parameters:

  • name (Symbol, String)

    macro name

Returns:

  • (Boolean)

    whether the macro is registered



309
310
311
# File 'lib/dry/validation/rust/contract.rb', line 309

def macro_registered?(name)
  self.class.macro_registry.key?(name)
end

#resolve_macro(name) ⇒ Macro

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.

Resolves a registered macro by name.

Parameters:

  • name (Symbol, String)

    macro name

Returns:

  • (Macro)

    registered macro implementation

Raises:

  • (KeyError)

    if no macro is registered with name



320
321
322
# File 'lib/dry/validation/rust/contract.rb', line 320

def resolve_macro(name)
  self.class.macro_registry.fetch(name)
end