diff options
Diffstat (limited to 'lib/arel/algebra/relations')
-rw-r--r-- | lib/arel/algebra/relations/operations/alias.rb | 7 | ||||
-rw-r--r-- | lib/arel/algebra/relations/operations/group.rb | 15 | ||||
-rw-r--r-- | lib/arel/algebra/relations/operations/join.rb | 41 | ||||
-rw-r--r-- | lib/arel/algebra/relations/operations/order.rb | 16 | ||||
-rw-r--r-- | lib/arel/algebra/relations/operations/project.rb | 19 | ||||
-rw-r--r-- | lib/arel/algebra/relations/operations/skip.rb | 10 | ||||
-rw-r--r-- | lib/arel/algebra/relations/operations/take.rb | 10 | ||||
-rw-r--r-- | lib/arel/algebra/relations/operations/where.rb | 16 | ||||
-rw-r--r-- | lib/arel/algebra/relations/relation.rb | 134 | ||||
-rw-r--r-- | lib/arel/algebra/relations/utilities/compound.rb | 18 | ||||
-rw-r--r-- | lib/arel/algebra/relations/utilities/externalization.rb | 24 | ||||
-rw-r--r-- | lib/arel/algebra/relations/utilities/nil.rb | 7 | ||||
-rw-r--r-- | lib/arel/algebra/relations/writes/delete.rb | 10 | ||||
-rw-r--r-- | lib/arel/algebra/relations/writes/insert.rb | 14 | ||||
-rw-r--r-- | lib/arel/algebra/relations/writes/update.rb | 14 |
15 files changed, 355 insertions, 0 deletions
diff --git a/lib/arel/algebra/relations/operations/alias.rb b/lib/arel/algebra/relations/operations/alias.rb new file mode 100644 index 0000000000..67837f6a75 --- /dev/null +++ b/lib/arel/algebra/relations/operations/alias.rb @@ -0,0 +1,7 @@ +module Arel + class Alias < Compound + attributes :relation + deriving :initialize + alias_method :==, :equal? + end +end
\ No newline at end of file diff --git a/lib/arel/algebra/relations/operations/group.rb b/lib/arel/algebra/relations/operations/group.rb new file mode 100644 index 0000000000..04fd9fea62 --- /dev/null +++ b/lib/arel/algebra/relations/operations/group.rb @@ -0,0 +1,15 @@ +module Arel + class Group < Compound + attributes :relation, :groupings + deriving :== + + def initialize(relation, *groupings, &block) + @relation = relation + @groupings = (groupings + (block_given?? [yield(relatoin)] : [])).collect { |g| g.bind(relation) } + end + + def externalizable? + true + end + end +end
\ No newline at end of file diff --git a/lib/arel/algebra/relations/operations/join.rb b/lib/arel/algebra/relations/operations/join.rb new file mode 100644 index 0000000000..8e19254378 --- /dev/null +++ b/lib/arel/algebra/relations/operations/join.rb @@ -0,0 +1,41 @@ +module Arel + class Join < Relation + attributes :join_sql, :relation1, :relation2, :predicates + deriving :== + delegate :engine, :name, :to => :relation1 + hash_on :relation1 + + def initialize(join_sql, relation1, relation2 = Nil.instance, *predicates) + @join_sql, @relation1, @relation2, @predicates = join_sql, relation1, relation2, predicates + end + + def attributes + @attributes ||= (relation1.externalize.attributes + + relation2.externalize.attributes).collect { |a| a.bind(self) } + end + + def wheres + # TESTME bind to self? + relation1.externalize.wheres + end + + def ons + @ons ||= @predicates.collect { |p| p.bind(self) } + end + + # TESTME + def externalizable? + relation1.externalizable? or relation2.externalizable? + end + + def join? + true + end + end + + class Relation + def join? + false + end + end +end diff --git a/lib/arel/algebra/relations/operations/order.rb b/lib/arel/algebra/relations/operations/order.rb new file mode 100644 index 0000000000..05af3fde4d --- /dev/null +++ b/lib/arel/algebra/relations/operations/order.rb @@ -0,0 +1,16 @@ +module Arel + class Order < Compound + attributes :relation, :orderings + deriving :== + + def initialize(relation, *orderings, &block) + @relation = relation + @orderings = (orderings + (block_given?? [yield(relation)] : [])).collect { |o| o.bind(relation) } + end + + # TESTME + def orders + (orderings + relation.orders).collect { |o| o.bind(self) } + end + end +end
\ No newline at end of file diff --git a/lib/arel/algebra/relations/operations/project.rb b/lib/arel/algebra/relations/operations/project.rb new file mode 100644 index 0000000000..5507ea3163 --- /dev/null +++ b/lib/arel/algebra/relations/operations/project.rb @@ -0,0 +1,19 @@ +module Arel + class Project < Compound + attributes :relation, :projections + deriving :== + + def initialize(relation, *projections, &block) + @relation = relation + @projections = (projections + (block_given?? [yield(relation)] : [])).collect { |p| p.bind(relation) } + end + + def attributes + @attributes ||= projections.collect { |p| p.bind(self) } + end + + def externalizable? + attributes.any?(&:aggregation?) or relation.externalizable? + end + end +end diff --git a/lib/arel/algebra/relations/operations/skip.rb b/lib/arel/algebra/relations/operations/skip.rb new file mode 100644 index 0000000000..930e4c94ea --- /dev/null +++ b/lib/arel/algebra/relations/operations/skip.rb @@ -0,0 +1,10 @@ +module Arel + class Skip < Compound + attributes :relation, :skipped + deriving :initialize, :== + + def externalizable? + true + end + end +end
\ No newline at end of file diff --git a/lib/arel/algebra/relations/operations/take.rb b/lib/arel/algebra/relations/operations/take.rb new file mode 100644 index 0000000000..2fd3fdf635 --- /dev/null +++ b/lib/arel/algebra/relations/operations/take.rb @@ -0,0 +1,10 @@ +module Arel + class Take < Compound + attributes :relation, :taken + deriving :initialize, :== + + def externalizable? + true + end + end +end
\ No newline at end of file diff --git a/lib/arel/algebra/relations/operations/where.rb b/lib/arel/algebra/relations/operations/where.rb new file mode 100644 index 0000000000..608aaeb4b7 --- /dev/null +++ b/lib/arel/algebra/relations/operations/where.rb @@ -0,0 +1,16 @@ +module Arel + class Where < Compound + attributes :relation, :predicate + deriving :== + + def initialize(relation, *predicates, &block) + predicate = block_given?? yield(relation) : predicates.shift + @relation = predicates.empty?? relation : Where.new(relation, *predicates) + @predicate = predicate.bind(@relation) + end + + def wheres + @wheres ||= (relation.wheres + [predicate]).collect { |p| p.bind(self) } + end + end +end diff --git a/lib/arel/algebra/relations/relation.rb b/lib/arel/algebra/relations/relation.rb new file mode 100644 index 0000000000..20badaf165 --- /dev/null +++ b/lib/arel/algebra/relations/relation.rb @@ -0,0 +1,134 @@ +module Arel + class Relation + attr_reader :count + + def session + Session.new + end + + def call + engine.read(self) + end + + def bind(relation) + self + end + + def root + self + end + + module Enumerable + include ::Enumerable + + def each(&block) + session.read(self).each(&block) + end + + def first + session.read(self).first + end + end + include Enumerable + + module Operable + def join(other_relation = nil, join_type = "INNER JOIN") + case other_relation + when String + Join.new(other_relation, self) + when Relation + JoinOperation.new(join_type, self, other_relation) + else + self + end + end + + def outer_join(other_relation = nil) + join(other_relation, "LEFT OUTER JOIN") + end + + [:where, :project, :order, :take, :skip, :group].each do |operation_name| + operation = <<-OPERATION + def #{operation_name}(*arguments, &block) + arguments.all?(&:blank?) && !block_given?? self : #{operation_name.to_s.classify}.new(self, *arguments, &block) + end + OPERATION + class_eval operation, __FILE__, __LINE__ + end + + def alias + Alias.new(self) + end + + module Writable + def insert(record) + session.create Insert.new(self, record); self + end + + def update(assignments) + session.update Update.new(self, assignments) + end + + def delete + session.delete Deletion.new(self) + end + end + include Writable + + JoinOperation = Struct.new(:join_sql, :relation1, :relation2) do + def on(*predicates) + Join.new(join_sql, relation1, relation2, *predicates) + end + end + end + include Operable + + module AttributeAccessable + def [](index) + case index + when Symbol, String + find_attribute_matching_name(index) + when Attribute, Expression + find_attribute_matching_attribute(index) + when Array + index.collect { |i| self[i] } + end + end + + def find_attribute_matching_name(name) + attributes.detect { |a| a.named?(name) } + end + + def find_attribute_matching_attribute(attribute) + matching_attributes(attribute).max do |a1, a2| + (a1.original_attribute / attribute) <=> (a2.original_attribute / attribute) + end + end + + private + def matching_attributes(attribute) + (@matching_attributes ||= attributes.inject({}) do |hash, a| + (hash[a.root] ||= []) << a + hash + end)[attribute.root] || [] + end + + def has_attribute?(attribute) + !matching_attributes(attribute).empty? + end + end + include AttributeAccessable + + module DefaultOperations + def attributes; [] end + def wheres; [] end + def orders; [] end + def inserts; [] end + def groupings; [] end + def joins(formatter = nil); nil end # FIXME + def taken; nil end + def skipped; nil end + end + include DefaultOperations + end +end diff --git a/lib/arel/algebra/relations/utilities/compound.rb b/lib/arel/algebra/relations/utilities/compound.rb new file mode 100644 index 0000000000..e33b8dbf14 --- /dev/null +++ b/lib/arel/algebra/relations/utilities/compound.rb @@ -0,0 +1,18 @@ +module Arel + class Compound < Relation + attr_reader :relation + hash_on :relation + delegate :joins, :join?, :inserts, :taken, :skipped, :name, :externalizable?, + :column_for, :engine, :table, :table_sql, :array, + :to => :relation + + [:attributes, :wheres, :groupings, :orders].each do |operation_name| + operation = <<-OPERATION + def #{operation_name} + @#{operation_name} ||= relation.#{operation_name}.collect { |o| o.bind(self) } + end + OPERATION + class_eval operation, __FILE__, __LINE__ + end + end +end diff --git a/lib/arel/algebra/relations/utilities/externalization.rb b/lib/arel/algebra/relations/utilities/externalization.rb new file mode 100644 index 0000000000..bd067f2304 --- /dev/null +++ b/lib/arel/algebra/relations/utilities/externalization.rb @@ -0,0 +1,24 @@ +module Arel + class Externalization < Compound + attributes :relation + deriving :initialize, :== + + def wheres + [] + end + + def attributes + @attributes ||= relation.attributes.collect(&:to_attribute).collect { |a| a.bind(self) } + end + end + + class Relation + def externalize + @externalized ||= externalizable?? Externalization.new(self) : self + end + + def externalizable? + false + end + end +end diff --git a/lib/arel/algebra/relations/utilities/nil.rb b/lib/arel/algebra/relations/utilities/nil.rb new file mode 100644 index 0000000000..6a9d678c45 --- /dev/null +++ b/lib/arel/algebra/relations/utilities/nil.rb @@ -0,0 +1,7 @@ +require 'singleton' + +module Arel + class Nil < Relation + include Singleton + end +end diff --git a/lib/arel/algebra/relations/writes/delete.rb b/lib/arel/algebra/relations/writes/delete.rb new file mode 100644 index 0000000000..a94067c7fa --- /dev/null +++ b/lib/arel/algebra/relations/writes/delete.rb @@ -0,0 +1,10 @@ +module Arel + class Deletion < Compound + attributes :relation + deriving :initialize, :== + + def call + engine.delete(self) + end + end +end diff --git a/lib/arel/algebra/relations/writes/insert.rb b/lib/arel/algebra/relations/writes/insert.rb new file mode 100644 index 0000000000..6d85e9769a --- /dev/null +++ b/lib/arel/algebra/relations/writes/insert.rb @@ -0,0 +1,14 @@ +module Arel + class Insert < Compound + attributes :relation, :record + deriving :== + + def initialize(relation, record) + @relation, @record = relation, record.bind(relation) + end + + def call + engine.create(self) + end + end +end diff --git a/lib/arel/algebra/relations/writes/update.rb b/lib/arel/algebra/relations/writes/update.rb new file mode 100644 index 0000000000..e647218a80 --- /dev/null +++ b/lib/arel/algebra/relations/writes/update.rb @@ -0,0 +1,14 @@ +module Arel + class Update < Compound + attributes :relation, :assignments + deriving :== + + def initialize(relation, assignments) + @relation, @assignments = relation, assignments + end + + def call + engine.update(self) + end + end +end |