lib/axiom/algebra/restriction.rb
# encoding: utf-8
module Axiom
module Algebra
# Restrict the tuples to those that match a predicate
class Restriction < Relation
include Relation::Operation::Unary
include Equalizer.new(:operand, :predicate)
TAUTOLOGY = Function::Proposition::Tautology.instance
# The predicate for the relation
#
# @return [Function, #call]
#
# @api private
attr_reader :predicate
# Initialize a Restriction
#
# @param [Relation] operand
# the relation to restrict
# @param [Function, #call] predicate
# the function to restrict the tuples with
#
# @return [undefined]
#
# @api private
def initialize(operand, predicate)
super(operand)
@predicate = predicate
end
# Iterate over each tuple in the set
#
# @example
# restriction = Restriction.new(operand, predicate)
# restriction.each { |tuple| ... }
#
# @yield [tuple]
#
# @yieldparam [Tuple] tuple
# each tuple in the set
#
# @return [self]
#
# @api public
def each
return to_enum unless block_given?
operand.each do |tuple|
yield tuple if Function.extract_value(predicate, tuple).equal?(true)
end
self
end
# Insert a relation into the Restriction
#
# The tuples must match the predicate to be inserted.
#
# @example
# new_relation = restriction.insert(other)
#
# @param [Relation] other
#
# @return [Restriction]
#
# @api public
def insert(other)
other = coerce(other)
operand.insert(other.restrict(predicate)).restrict(predicate)
end
# Delete a relation from the Restriction
#
# The tuples must match the predicate to be deleted.
#
# @example
# new_relation = restriction.delete(other)
#
# @param [Relation] other
#
# @return [Restriction]
#
# @api public
def delete(other)
other = coerce(other)
operand.delete(other.restrict(predicate)).restrict(predicate)
end
module Methods
# Return a relation with restricted tuples
#
# @example restriction with a predicate
# restriction = relation.restrict(predicate)
#
# @example restriction using a block
# restriction = relation.restrict do |context|
# context.a.eq('other').and(context.b.gte(42))
# end
#
# @example restriction using a Hash
# restriction = relation.restrict(id: 1)
#
# @example restriction using an Array
# restriction = relation.restrict([[:id, 1]])
#
# @param [Array] args
# optional arguments
#
# @yield [context]
# optional block to restrict the tuples with
#
# @yieldparam [Evaluator::Context] context
# the context to evaluate the restriction with
#
# @yieldreturn [Function, #call]
# predicate to restrict the tuples with
#
# @return [Restriction]
#
# @api public
def restrict(*args, &block)
Restriction.new(self, coerce_to_predicate(*args, &block))
end
private
# Coerce the arguments and block into a predicate
#
# @param [Function, #call] predicate
# optional predicate
#
# @yield [relation]
# optional block to restrict the tuples with
#
# @yieldparam [Evaluator::Context] context
# the context to evaluate the restriction with
#
# @yieldreturn [Function, #call]
# predicate to restrict the tuples with
#
# @return [Function, #call]
#
# @api private
def coerce_to_predicate(predicate = Undefined)
case predicate
when Undefined
Evaluator::Context.new(header) { |context| yield context }.yield
when Enumerable
predicate.reduce(TAUTOLOGY) do |entry, (name, value)|
entry.and(header[name].eq(value))
end
else
predicate
end
end
end # module Methods
Relation.class_eval { include Methods }
end # class Restriction
end # module Algebra
end # module Axiom