Class: DataMapper::Validations::ContextualValidators

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Enumerable
Defined in:
lib/dm-validations/contextual_validators.rb

Overview

Author:

  • Guy van den Berg

Since:

  • 0.9

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model = nil) ⇒ ContextualValidators

Returns a new instance of ContextualValidators.

Since:

  • 0.9



21
22
23
24
25
# File 'lib/dm-validations/contextual_validators.rb', line 21

def initialize(model = nil)
  @model      = model
  @contexts   = {}
  @attributes = {}
end

Instance Attribute Details

#attributesObject (readonly)

Since:

  • 0.9



19
20
21
# File 'lib/dm-validations/contextual_validators.rb', line 19

def attributes
  @attributes
end

#contextsObject (readonly)

Since:

  • 0.9



19
20
21
# File 'lib/dm-validations/contextual_validators.rb', line 19

def contexts
  @contexts
end

Instance Method Details

#add(validator_class, *attributes) ⇒ Object

Create a new validator of the given klazz and push it onto the requested context for each of the attributes in attributes

Parameters:

  • validator_class (DataMapper::Validations::GenericValidator)

    Validator class, example: DataMapper::Validations::LengthValidator

  • attributes (Array<Symbol>)

    Attribute names given to validation macro, example:

    :first_name, :last_name

    in validates_presence_of :first_name, :last_name

  • options (Hash)

    Options supplied to validation macro, example: :maximum=>50, :allow_nil=>true, :message=>nil

  • [Symbol] (Hash)

    a customizable set of options

  • [Boolean] (Hash)

    a customizable set of options

Since:

  • 0.9



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/dm-validations/contextual_validators.rb', line 78

def add(validator_class, *attributes)
  options = attributes.last.kind_of?(Hash) ? attributes.pop.dup : {}
  normalize_options(options)
  validator_options = DataMapper::Ext::Hash.except(options, :context)

  attributes.each do |attribute|
    # TODO: is :context part of the Validator state (ie, intrinsic),
    # or is it just membership in a collection?
    validator = validator_class.new(attribute, validator_options)
    attribute_validators = self.attribute(attribute)
    attribute_validators << validator unless attribute_validators.include?(validator)

    options[:context].each do |context|
      context_validators = self.context(context)
      next if context_validators.include?(validator)
      context_validators << validator
      # TODO: eliminate this, then eliminate the @model ivar entirely
      Validations::ClassMethods.create_context_instance_methods(@model, context) if @model
    end
  end
end

#assert_valid(context) ⇒ 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.

Assert that the given context is valid for this model

TODO: is this method actually needed?

Parameters:

  • context (Symbol)

    the context to test

Raises:

Since:

  • 0.9



172
173
174
175
176
# File 'lib/dm-validations/contextual_validators.rb', line 172

def assert_valid(context)
  unless valid_context?(context)
    raise InvalidContextError, "#{context} is an invalid context, known contexts are #{contexts.keys.inspect}"
  end
end

#attribute(name) ⇒ Array<DataMapper::Validations::GenericValidator>

Return an array of validators for a named property

Parameters:

  • Property (Symbol)

    name for which to return validators

Returns:

Since:

  • 0.9



47
48
49
# File 'lib/dm-validations/contextual_validators.rb', line 47

def attribute(name)
  attributes[name] ||= OrderedSet.new
end

#clear!Object

Clear all named context validators off of the resource

Since:

  • 0.9



53
54
55
56
# File 'lib/dm-validations/contextual_validators.rb', line 53

def clear!
  contexts.clear
  attributes.clear
end

#context(name) ⇒ Array<DataMapper::Validations::GenericValidator>

Return an array of validators for a named context

Parameters:

  • Context (String)

    name for which to return validators

Returns:

Since:

  • 0.9



37
38
39
# File 'lib/dm-validations/contextual_validators.rb', line 37

def context(name)
  contexts[name] ||= OrderedSet.new
end

#current_contextSymbol

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 the current validation context on the stack if valid for this model, nil if no contexts are defined for the model (and no contexts are on the validation stack), or :default if the current context is invalid for this model or no contexts have been defined for this model and no context is on the stack.

TODO: simplify the semantics of #current_context, #valid?

Returns:

  • (Symbol)

    the current validation context from the stack (if valid for this model), nil if no context is on the stack and no contexts are defined for this model, or :default if the context on the stack is invalid for this model or no context is on the stack and this model has at least one validation context

Since:

  • 0.9



141
142
143
144
# File 'lib/dm-validations/contextual_validators.rb', line 141

def current_context
  context = Validations::Context.current
  valid_context?(context) ? context : :default
end

#execute(named_context, target) ⇒ Boolean

Execute all validators in the named context against the target. Load together any properties that are designated lazy but are not yet loaded.

Parameters:

  • named_context (Symbol)

    the context we are validating against

  • target (Object)

    the resource that we are validating

Returns:

  • (Boolean)

    true if all are valid, otherwise false

Since:

  • 0.9



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/dm-validations/contextual_validators.rb', line 188

def execute(named_context, target)
  target.errors.clear!

  available_validators  = context(named_context)
  executable_validators = available_validators.select { |v| v.execute?(target) }

  # In the case of a new Resource or regular ruby class instance,
  # everything needs to be validated completely, and no eager-loading
  # logic should apply.
  #
  if target.kind_of?(DataMapper::Resource) && !target.new?
    load_validated_properties(target, executable_validators)
  end
  executable_validators.map { |validator| validator.call(target) }.all?
end

#load_validated_properties(resource, validators) ⇒ Object

Load all lazy, not-yet-loaded properties that need validation, all at once.

Since:

  • 0.9



206
207
208
209
210
211
212
213
214
215
216
# File 'lib/dm-validations/contextual_validators.rb', line 206

def load_validated_properties(resource, validators)
  properties = resource.model.properties

  properties_to_load = validators.map { |validator|
    properties[validator.field_name]
  }.compact.select { |property|
    property.lazy? && !property.loaded?(resource)
  }

  resource.__send__(:eager_load, properties_to_load)
end

#normalize_options(options, defaults = nil) ⇒ Hash

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.

Clean up the argument list and return a opts hash, including the merging of any default opts. Set the context to default if none is provided. Also allow :context to be aliased to :on, :when & :group

Parameters:

  • options (Hash)

    the options to be normalized

  • defaults (NilClass, Hash) (defaults to: nil)

    default keys/values to set on normalized options

Returns:

  • (Hash)

    the normalized options

Since:

  • 0.9



113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/dm-validations/contextual_validators.rb', line 113

def normalize_options(options, defaults = nil)
  context = [
    options.delete(:group),
    options.delete(:on),
    options.delete(:when),
    options.delete(:context)
  ].compact.first

  options[:context] = Array(context || :default)
  options.update(defaults) unless defaults.nil?
  options
end

#valid_context?(context) ⇒ 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.

Test if the context is valid for the model

TODO: investigate removing the ‘contexts.empty?` test here.

Parameters:

  • context (Symbol)

    the context to test

Returns:

  • (Boolean)

    true if the context is valid for the model

Since:

  • 0.9



157
158
159
# File 'lib/dm-validations/contextual_validators.rb', line 157

def valid_context?(context)
  contexts.empty? || contexts.include?(context)
end