Skip to content

Latest commit

 

History

History
136 lines (97 loc) · 3.99 KB

validations.md

File metadata and controls

136 lines (97 loc) · 3.99 KB

Validations

StoreModel supports all validations shipped with ActiveModel. Start with defining validation for the store model:

class Configuration
  include StoreModel::Model

  attribute :model, :string
  attribute :color, :string

  validates :color, presence: true
end

Then, configure your ActiveRecord model to validates this field as a store model:

class Product < ApplicationRecord
  attribute :configuration, Configuration.to_type

  validates :configuration, store_model: true
end

When attribute is invalid, errors are not copied to the parent model by default:

product = Product.new
puts product.valid? # => false
puts product.errors.messages # => { configuration: ["is invalid"] }
puts product.configuration.errors.messages # => { color: ["can't be blank"] }

You can change this behavior to have these errors on the root level (instead of ["is invalid"]):

class Product < ApplicationRecord
  attribute :configuration, Configuration.to_type

  validates :configuration, store_model: { merge_errors: true }
end

In this case errors look this way:

product = Product.new
puts product.valid? # => false
puts product.errors.messages # => { color: ["can't be blank"] }

When using array type, merging errors happens in a following way:

class Product < ApplicationRecord
  attribute :configurations, Configuration.to_array_type

  validates :configurations, store_model: { merge_array_errors: true }
end

product = Product.new
product.configurations << Configuration.new
puts product.valid? # => false
puts product.errors.messages # => { color: ["[0] Color can't be blank"] }

You can change the global behavior using StoreModel.config:

StoreModel.config.merge_errors = true

Heads up! Due to the changes of error internals in Rails >= 6.1 it's impossible to add an error with a key that does not have a corresponding attribute with the same name. Because of that, the behavior of merge_error strategy will be different–all errors are going to be placed under the attribute name ({ configuration: ["Color can't be blank"] } instead of { color: ["can't be blank"] }).

You can also add your own custom strategies to handle errors. All you need to do is to provide a callable object to StoreModel.config.merge_errors or as value of :merge_errors. It should accept three arguments–attribute, base_errors and store_model_errors:

lambda_strategy = lambda do |attribute, base_errors, _store_model_errors|
  base_errors.add(attribute, "cthulhu fhtagn")
end

StoreModel.config.merge_errors = lambda_strategy

StoreModel.config.merge_array_errors = lambda_strategy

If the logic is complex enough–it worth defining a separate class with a #call method:

class FhtagnErrorStrategy
  def call(attribute, base_errors, store_model_errors)
    base_errors.add(attribute, "cthulhu fhtagn")
  end
end

class FhtagnArrayErrorStrategy
  def call(attribute, base_errors, store_models)
    base_errors.add(attribute, "cthulhu fhtagn")
  end
end

You can provide its instance or snake-cased name when configuring global merge_errors:

StoreModel.config.merge_errors = :fhtagn_error_strategy

class Product < ApplicationRecord
  attribute :configuration, Configuration.to_type

  validates :configuration, store_model: { merge_errors: :fhtagn_error_strategy }
end

or when calling validates method on a class level:

StoreModel.config.merge_errors = FhtagnErrorStrategy.new

class Product < ApplicationRecord
  attribute :configuration, Configuration.to_type

  validates :configuration, store_model: { merge_errors: FhtagnErrorStrategy.new }
end

Heads up! :store_model validator does not allow nils by default, if you want to change this behavior–configure the validation with allow_nil: true:

class Product < ApplicationRecord
  attribute :configuration, Configuration.to_type

  validates :configuration, store_model: true, allow_nil: true
end