# encoding: utf-8 module Assertion # The base class for assertions about some attributes # # Every assertion should define a list of attributes to be checked and # the [#check] method to apply the assertion to those attributes # # The assertion `call` method provides the object, describing the state # of the assertion applied to its attributes. The provided state carries # the result of the checkup and a corresponding <error> message. # Later it can be composed with other states to provide complex validation. # # The class DSL also defines shortcuts: # # * [.[]] can be used to initialize the assertion for given attributes and # then apply it immediately with creation of the corresponding state. # * [.not] can be used to provide the assertion opposite to the initial one. # # @example # class IsAdult < Assertion::Base # attribute :name, :age # # def check # age >= 18 # end # end # # child = IsAdult.not # # jane = { name: "Jane", age: 12 } # IsAdult[jane].valid? # => false # child[jane].valid? # => true # class Base extend DSL::Attributes extend DSL::Inversion extend DSL::Caller # The class-specific translator of assertion states # # @private # def self.translator @translator ||= Translator.new(self) end # @private def self.translate(state, attributes) translator.call(state, attributes) end # @!attribute [r] attributes # # @example # IsAdult = Assertion.about :name, :age do # age >= 18 # end # # adult = IsAdult[name: "Joe", age: 15, gender: :male] # adult.attributes # => { name: "Joe", age: 15 } # # @return [Hash] # The hash of the allowed attributes having been initialized # attr_reader :attributes # @!scope class # @!method new(args = {}) # Initializes an assertion for the current object # # @param [Hash] args The arguments to check # # @return [Assertion::Base] # @private def initialize(args = {}) keys = self.class.attributes @attributes = Hash[keys.zip(args.values_at(*keys))] IceNine.deep_freeze(self) end # Returns the message describing the current state # # @param [Boolean] state The state to describe # # @return [String] # def message(state = nil) self.class.translate(state, attributes) end # Calls the assertion checkup and returns the resulting state # # The state is a unified composable object, unaware of the # structure and attributes of the specific assertion. # # @return [Assertion::State] # The state of the assertion being applied to its attributes # def call State.new check, message end end # class Base end # module Assertion