lib/axiom/relation.rb in axiom-0.1.0 vs lib/axiom/relation.rb in axiom-0.1.1
- old
+ new
@@ -2,11 +2,11 @@
module Axiom
# Abstract base class for Relation operations
class Relation
- include Enumerable, Visitable
+ include Enumerable, Visitable, Adamantium::Flat
include Equalizer.new(:header, :to_set)
# The relation header
#
# @return [Header]
@@ -32,35 +32,49 @@
end
# Instantiate a new Relation
#
# @example of a materialized Array based relation
- # array = [ [ 1 ], [ 2 ], [ 3 ] ]
- # relation = Relation.new([ [ :id, Integer ] ], array)
+ # array = [[1], [2], [3]]
+ # relation = Relation.new([[:id, Integer]], array)
#
# @example of a materialized Set based relation
- # set = Set[ [ 1 ], [ 2 ], [ 3 ] ]
- # relation = Relation.new([ [ :id, Integer ] ], set)
+ # set = Set[[1], [2], [3]]
+ # relation = Relation.new([[:id, Integer]], set)
#
# @example of a non-materialized Enumerator based relation
- # enumerator = [ [ 1 ], [ 2 ], [ 3 ] ].each
- # relation = Relation.new([ [ :id, Integer ] ], enumerator)
+ # enumerator = [[1], [2], [3]].each
+ # relation = Relation.new([[:id, Integer]], enumerator)
#
# @param [Array(Header, Enumerable)] args
#
# @return [Relation]
#
# @api public
def self.new(*args)
- last = args.last
- if superclass.equal?(Object) && last.respond_to?(:size) && last.size.kind_of?(Integer)
+ if equal?(Relation) && materialized?(args[1])
Materialized.new(*args)
else
super
end
end
+ # Test if the tuples are materialized
+ #
+ # When tuples are nil, it means there are no tuples so it is the equivalent
+ # of specifying [] for the tuples.
+ #
+ # @param [Enumerable, nil] tuples
+ #
+ # @return [Boolean]
+ #
+ # @api private
+ def self.materialized?(tuples)
+ tuples.nil? || tuples.respond_to?(:size) && tuples.size.kind_of?(Integer)
+ end
+ private_class_method :materialized?
+
# Initialize a Relation
#
# @param [Header, #to_ary] header
# the relation header
# @param [Enumerable] tuples
@@ -69,11 +83,11 @@
# @return [undefined]
#
# @api private
def initialize(header, tuples)
@header = Header.coerce(header)
- @tuples = freeze_object(tuples)
+ @tuples = tuples
end
# Lookup an Attribute in the header given an attribute name
#
# @example
@@ -103,37 +117,18 @@
# @return [self]
#
# @api public
def each
return to_enum unless block_given?
- seen = Hash.new
+ seen = {}
tuples.each do |tuple|
tuple = Tuple.coerce(header, tuple)
yield seen[tuple] = tuple unless seen.key?(tuple)
end
self
end
- # Return a tuple if the relation contains exactly one tuple
- #
- # @example
- # tuple = relation.one
- #
- # @return [Tuple]
- #
- # @raise [NoTuplesError]
- # raised if no tuples are returned
- # @raise [ManyTuplesError]
- # raised if more than one tuple is returned
- #
- # @api public
- def one
- tuples = to_a
- assert_exactly_one_tuple(tuples.size)
- tuples.first
- end
-
# Return a relation that represents a replacement of a relation
#
# Delete the tuples from the relation that are not in the other relation,
# then insert only new tuples.
#
@@ -199,11 +194,12 @@
# @return [Boolean]
#
# @api public
def ==(other)
other = coerce(other)
- header == other.header &&
+ other.kind_of?(Relation) &&
+ header == other.header &&
to_set == other.to_set
end
# Test if there are no tuples
#
@@ -240,28 +236,13 @@
#
# @return [Relation]
#
# @api private
def self.coerce(header, object)
- object.kind_of?(Relation) ? object : Relation.new(header, object)
- end
-
- # Assert exactly one tuple is returned
- #
- # @return [undefined]
- #
- # @raise [NoTuplesError]
- # raised if no tuples are returned
- # @raise [ManyTuplesError]
- # raised if more than one tuple is returned
- #
- # @api private
- def assert_exactly_one_tuple(size)
- if size.zero?
- raise NoTuplesError, 'one tuple expected, but was an empty set'
- elsif size > 1
- raise ManyTuplesError,
- "one tuple expected, but set contained #{size} tuples"
+ if object.kind_of?(Relation) || ! object.kind_of?(Enumerable)
+ object
+ else
+ Relation.new(header, object)
end
end
end # class Relation
end # module Axiom