Class: Rbdantic::FieldInfo

Inherits:
Object
  • Object
show all
Defined in:
lib/rbdantic/field.rb

Overview

FieldInfo: Stores field metadata and handles validation pipeline

Constant Summary collapse

UNSET =
Object.new.freeze
CONSTRAINT_KEYS =
i[
  min_length max_length gt ge lt le pattern format
  min_items max_items unique_items element_type multiple_of
  min_properties max_properties
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name: nil, type: nil, default: UNSET, default_factory: nil, optional: nil, required: nil, validators: nil, alias_name: nil, **constraints) ⇒ FieldInfo

Returns a new instance of FieldInfo.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/rbdantic/field.rb', line 40

def initialize(name: nil, type: nil, default: UNSET, default_factory: nil,
               optional: nil, required: nil, validators: nil, alias_name: nil, **constraints)
  @name = name
  @type = type
  @alias_name = alias_name
  @default = default.equal?(UNSET) ? nil : default
  @default_provided = !default.equal?(UNSET)
  @default_factory = default_factory
  @optional = required == false ? true : (optional || false)
  @validators = validators || []
  validate_constraint_keys!(constraints)
  @constraints = constraints.slice(*CONSTRAINT_KEYS).freeze

  validate_mutable_default! if @default_provided && !@default_factory
  validate_constraint_compatibility! if @type
  @type_validator = Validators::Types.create_validator(@type, **@constraints)
end

Instance Attribute Details

#alias_nameObject (readonly)

Returns the value of attribute alias_name.



10
11
12
# File 'lib/rbdantic/field.rb', line 10

def alias_name
  @alias_name
end

#constraintsObject (readonly)

Returns the value of attribute constraints.



10
11
12
# File 'lib/rbdantic/field.rb', line 10

def constraints
  @constraints
end

#defaultObject (readonly)

Returns the value of attribute default.



10
11
12
# File 'lib/rbdantic/field.rb', line 10

def default
  @default
end

#default_factoryObject (readonly)

Returns the value of attribute default_factory.



10
11
12
# File 'lib/rbdantic/field.rb', line 10

def default_factory
  @default_factory
end

#default_providedObject (readonly)

Returns the value of attribute default_provided.



10
11
12
# File 'lib/rbdantic/field.rb', line 10

def default_provided
  @default_provided
end

#nameObject (readonly)

Returns the value of attribute name.



10
11
12
# File 'lib/rbdantic/field.rb', line 10

def name
  @name
end

#optionalObject (readonly)

Returns the value of attribute optional.



10
11
12
# File 'lib/rbdantic/field.rb', line 10

def optional
  @optional
end

#typeObject (readonly)

Returns the value of attribute type.



10
11
12
# File 'lib/rbdantic/field.rb', line 10

def type
  @type
end

#type_validatorObject (readonly)

Returns the value of attribute type_validator.



10
11
12
# File 'lib/rbdantic/field.rb', line 10

def type_validator
  @type_validator
end

#validatorsObject (readonly)

Returns the value of attribute validators.



10
11
12
# File 'lib/rbdantic/field.rb', line 10

def validators
  @validators
end

Class Method Details

.from_dsl(name, type, metadata = nil, **options) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/rbdantic/field.rb', line 20

def self.from_dsl(name, type,  = nil, **options)
  meta = case 
         when nil then {}
         when FieldInfo then { default: .default, default_factory: .default_factory,
                               optional: .optional, validators: .validators,
                               alias_name: .alias_name, **.constraints }
         when Hash then 
         else raise ArgumentError, "field metadata must be a Rbdantic::FieldInfo or Hash"
         end

  opts = meta.merge(options)
  type, opts = normalize_type(type, opts)
  new(name: name, type: type, **opts)
end

.normalize_type(type, options) ⇒ Object



35
36
37
38
# File 'lib/rbdantic/field.rb', line 35

def self.normalize_type(type, options)
  return [Array, options.merge(element_type: type.first)] if type.is_a?(Array) && type.length == 1
  [type, options]
end

Instance Method Details

#get_defaultObject



59
# File 'lib/rbdantic/field.rb', line 59

def get_default; @default_factory ? @default_factory.call : @default; end

#has_default?Boolean

Returns:



58
# File 'lib/rbdantic/field.rb', line 58

def has_default?; @default_provided || !@default_factory.nil?; end

#required?Boolean

Returns:



60
# File 'lib/rbdantic/field.rb', line 60

def required?; !@optional && !has_default?; end

#validate(value, model_instance, context) ⇒ Array<ErrorDetail>, value

Main validation entry point

Parameters:

  • value

    the value to validate

  • model_instance

    the model instance being validated

  • context (ValidatorContext)

    validation context

Returns:

  • (Array<ErrorDetail>, value)

    tuple of errors and validated value



67
68
69
70
71
72
73
74
75
76
77
# File 'lib/rbdantic/field.rb', line 67

def validate(value, model_instance, context)
  return handle_nil_value if value.nil?

  strict = model_instance.class.model_config.strict
  custom_validators = model_instance.class.field_validators[@name] || []

  errors, value = run_before_validators(value, custom_validators, context)
  return [errors, value] if errors.any?

  run_main_validation(value, custom_validators, context, strict)
end