The gem allows to add a standalone validator to Ruby classes.
This is a part of collection of patterns extracted from Rails projects with a special focus on separation and composability of data validators.
Add this line to your application's Gemfile:
gem 'tram-examiner'
And then execute:
$ bundle
Or install it yourself as:
$ gem install tram-examiner
Include the module to the class and define validation rules inside the schema
block.
require "tram-examiner"
class User < Struct.new(:name, :age)
include Tram::Examiner
examiner do
validates :name, presence: true
end
end
Inside the block you should use standard ActiveModel validations. The trick is that instead of including ActiveModel::Validations
directly, the examiner adds validations to a separate decorator, wrapped around validated object.
It adds a variable @__examiner__
, which refers to that standalone decorator. Access to validation result is provided via delegators valid?
, validate!
and errors
.
user = User.new '', 46
user.valid? # => false
user.validate! # Boom with #<<ActiveModel::ValidationError: Validation failed: Name can't be blank>
user.errors.messages # => { name: ["cannot be blank"] }
Under the hood:
user.is_a? ActiveModel::Validations
# => false
user.instance_variable_get(:@__examiner__).is_a? ActiveModel::Validations
# => true
For syntax sugar you can use let
memoizer helper inside the examiner
block:
class User < Struct.new(:first_name, :last_name, :age)
include Tram::Examiner
examiner do
let(:name) { [first_name, last_name].compact.join(" ") }
validates :name, presence: true
end
end
user = User.new '', '', 51
user.errors.messages # => { name: ["cannot be blank"] }
Notice the block is evaluated in the scope of standalone validator, not the current class!
That is why neither let
, nor validates
are available outside of the block.
The gem is available as open source under the terms of the MIT License.