diff options
Diffstat (limited to 'lib/active_relation/relations')
-rw-r--r-- | lib/active_relation/relations/base.rb | 111 | ||||
-rw-r--r-- | lib/active_relation/relations/compound.rb | 9 | ||||
-rw-r--r-- | lib/active_relation/relations/deletion.rb | 17 | ||||
-rw-r--r-- | lib/active_relation/relations/insertion.rb | 25 | ||||
-rw-r--r-- | lib/active_relation/relations/join.rb | 45 | ||||
-rw-r--r-- | lib/active_relation/relations/order.rb | 19 | ||||
-rw-r--r-- | lib/active_relation/relations/projection.rb | 19 | ||||
-rw-r--r-- | lib/active_relation/relations/range.rb | 23 | ||||
-rw-r--r-- | lib/active_relation/relations/rename.rb | 38 | ||||
-rw-r--r-- | lib/active_relation/relations/selection.rb | 25 | ||||
-rw-r--r-- | lib/active_relation/relations/table.rb | 35 |
11 files changed, 366 insertions, 0 deletions
diff --git a/lib/active_relation/relations/base.rb b/lib/active_relation/relations/base.rb new file mode 100644 index 0000000000..c4a887eecd --- /dev/null +++ b/lib/active_relation/relations/base.rb @@ -0,0 +1,111 @@ +module ActiveRelation + module Relations + class Base + include SqlBuilder + + module Iteration + include Enumerable + + def each(&block) + connection.select_all(to_s).each(&block) + end + + def first + connection.select_one(to_s) + end + end + include Iteration + + module Operations + def <=>(other) + JoinOperation.new("INNER JOIN", self, other) + end + + def <<(other) + JoinOperation.new("LEFT OUTER JOIN", self, other) + end + + def [](index) + case index + when Symbol + attribute(index) + when ::Range + Range.new(self, index) + end + end + + def include?(attribute) + Predicates::RelationInclusion.new(attribute, self) + end + + def select(*s) + Selection.new(self, *s) + end + + def project(*attributes) + Projection.new(self, *attributes) + end + + def order(*attributes) + Order.new(self, *attributes) + end + + def rename(attribute, aliaz) + Rename.new(self, attribute => aliaz) + end + + def insert(record) + Insertion.new(self, record) + end + + def delete + Deletion.new(self) + end + + class JoinOperation + attr_reader :join_sql, :relation1, :relation2 + + def initialize(join_sql, relation1, relation2) + @join_sql, @relation1, @relation2 = join_sql, relation1, relation2 + end + + def on(*predicates) + Join.new(join_sql, relation1, relation2, *predicates) + end + + def ==(other) + (relation1 == other.relation1 and relation2 == other.relation2) or + (relation1 == other.relation2 and relation2 == other.relation1) + end + end + end + include Operations + + def connection + ActiveRecord::Base.connection + end + + def to_sql(options = {}) + [ + "SELECT #{attributes.collect{ |a| a.to_sql(:use_alias => true) }.join(', ')}", + "FROM #{quote_table_name(table)}", + (joins.to_sql(:quote => false) unless joins.blank?), + ("WHERE #{selects.collect{|s| s.to_sql(:quote => false)}.join("\n\tAND ")}" unless selects.blank?), + ("ORDER BY #{orders.collect(&:to_sql)}" unless orders.blank?), + ("LIMIT #{limit.to_sql}" unless limit.blank?), + ("OFFSET #{offset.to_sql}" unless offset.blank?) + ].compact.join("\n") + end + alias_method :to_s, :to_sql + + protected + def attributes; [] end + def selects; [] end + def orders; [] end + def inserts; [] end + def joins; nil end + def limit; nil end + def offset; nil end + end + end +end
\ No newline at end of file diff --git a/lib/active_relation/relations/compound.rb b/lib/active_relation/relations/compound.rb new file mode 100644 index 0000000000..442224a011 --- /dev/null +++ b/lib/active_relation/relations/compound.rb @@ -0,0 +1,9 @@ +module ActiveRelation + module Relations + class Compound < Base + attr_reader :relation + + delegate :attributes, :attribute, :joins, :selects, :orders, :table, :inserts, :limit, :offset, :to => :relation + end + end +end
\ No newline at end of file diff --git a/lib/active_relation/relations/deletion.rb b/lib/active_relation/relations/deletion.rb new file mode 100644 index 0000000000..f218d9da6d --- /dev/null +++ b/lib/active_relation/relations/deletion.rb @@ -0,0 +1,17 @@ +module ActiveRelation + module Relations + class Deletion < Compound + def initialize(relation) + @relation = relation + end + + def to_sql(options = {}) + [ + "DELETE", + "FROM #{quote_table_name(table)}", + ("WHERE #{selects.collect(&:to_sql).join('\n\tAND ')}" unless selects.blank?) + ].compact.join("\n") + end + end + end +end
\ No newline at end of file diff --git a/lib/active_relation/relations/insertion.rb b/lib/active_relation/relations/insertion.rb new file mode 100644 index 0000000000..a0042a18a5 --- /dev/null +++ b/lib/active_relation/relations/insertion.rb @@ -0,0 +1,25 @@ +module ActiveRelation + module Relations + class Insertion < Compound + attr_reader :record + + def initialize(relation, record) + @relation, @record = relation, record + end + + def to_sql(options = {}) + [ + "INSERT", + "INTO #{quote_table_name(table)}", + "(#{record.keys.collect(&:to_sql).join(', ')})", + "VALUES #{inserts.collect(&:to_sql).join(', ')}" + ].join("\n") + end + + protected + def inserts + relation.inserts + [record] + end + end + end +end
\ No newline at end of file diff --git a/lib/active_relation/relations/join.rb b/lib/active_relation/relations/join.rb new file mode 100644 index 0000000000..1bd1439dd6 --- /dev/null +++ b/lib/active_relation/relations/join.rb @@ -0,0 +1,45 @@ +module ActiveRelation + module Relations + class Join < Base + attr_reader :join_sql, :relation1, :relation2, :predicates + + def initialize(join_sql, relation1, relation2, *predicates) + @join_sql, @relation1, @relation2, @predicates = join_sql, relation1, relation2, predicates + end + + def ==(other) + predicates == other.predicates and + ((relation1 == other.relation1 and relation2 == other.relation2) or + (relation2 == other.relation1 and relation1 == other.relation2)) + end + + def qualify + Join.new(join_sql, relation1.qualify, relation2.qualify, *predicates.collect(&:qualify)) + end + + protected + def joins + [relation1.joins, relation2.joins, join].compact.join(" ") + end + + def selects + relation1.send(:selects) + relation2.send(:selects) + end + + def attributes + relation1.attributes + relation2.attributes + end + + def attribute(name) + relation1[name] || relation2[name] + end + + delegate :table, :to => :relation1 + + private + def join + "#{join_sql} #{quote_table_name(relation2.table)} ON #{predicates.collect { |p| p.to_sql(:quote => false) }.join(' AND ')}" + end + end + end +end
\ No newline at end of file diff --git a/lib/active_relation/relations/order.rb b/lib/active_relation/relations/order.rb new file mode 100644 index 0000000000..99ff939528 --- /dev/null +++ b/lib/active_relation/relations/order.rb @@ -0,0 +1,19 @@ +module ActiveRelation + module Relations + class Order < Compound + attr_reader :relation, :orders + + def initialize(relation, *orders) + @relation, @orders = relation, orders + end + + def ==(other) + relation == other.relation and orders.eql?(other.orders) + end + + def qualify + Order.new(relation.qualify, *orders.collect { |o| o.qualify }) + end + end + end +end
\ No newline at end of file diff --git a/lib/active_relation/relations/projection.rb b/lib/active_relation/relations/projection.rb new file mode 100644 index 0000000000..b30c76898d --- /dev/null +++ b/lib/active_relation/relations/projection.rb @@ -0,0 +1,19 @@ +module ActiveRelation + module Relations + class Projection < Compound + attr_reader :relation, :attributes + + def initialize(relation, *attributes) + @relation, @attributes = relation, attributes + end + + def ==(other) + relation == other.relation and attributes.eql?(other.attributes) + end + + def qualify + Projection.new(relation.qualify, *attributes.collect(&:qualify)) + end + end + end +end
\ No newline at end of file diff --git a/lib/active_relation/relations/range.rb b/lib/active_relation/relations/range.rb new file mode 100644 index 0000000000..d7e08efa06 --- /dev/null +++ b/lib/active_relation/relations/range.rb @@ -0,0 +1,23 @@ +module ActiveRelation + module Relations + class Range < Compound + attr_reader :range + + def initialize(relation, range) + @relation, @range = relation, range + end + + def ==(other) + relation == other.relation and range == other.range + end + + def limit + range.end - range.begin + 1 + end + + def offset + range.begin + end + end + end +end
\ No newline at end of file diff --git a/lib/active_relation/relations/rename.rb b/lib/active_relation/relations/rename.rb new file mode 100644 index 0000000000..7a1693df57 --- /dev/null +++ b/lib/active_relation/relations/rename.rb @@ -0,0 +1,38 @@ +module ActiveRelation + module Relations + class Rename < Compound + attr_reader :relation, :schmattribute, :alias + + def initialize(relation, renames) + @schmattribute, @alias = renames.shift + @relation = renames.empty?? relation : Rename.new(relation, renames) + end + + def ==(other) + relation == other.relation and schmattribute.eql?(other.schmattribute) and self.alias == other.alias + end + + def attributes + relation.attributes.collect { |a| substitute(a) } + end + + def qualify + Rename.new(relation.qualify, schmattribute.qualify => self.alias) + end + + protected + def attribute(name) + case + when name == self.alias then schmattribute.alias(self.alias) + when relation[name].eql?(schmattribute) then nil + else relation[name] + end + end + + private + def substitute(a) + a.eql?(schmattribute) ? a.alias(self.alias) : a + end + end + end +end
\ No newline at end of file diff --git a/lib/active_relation/relations/selection.rb b/lib/active_relation/relations/selection.rb new file mode 100644 index 0000000000..e102d105a0 --- /dev/null +++ b/lib/active_relation/relations/selection.rb @@ -0,0 +1,25 @@ +module ActiveRelation + module Relations + class Selection < Compound + attr_reader :relation, :predicate + + def initialize(relation, *predicates) + @predicate = predicates.shift + @relation = predicates.empty?? relation : Selection.new(relation, *predicates) + end + + def ==(other) + relation == other.relation and predicate == other.predicate + end + + def qualify + Selection.new(relation.qualify, predicate.qualify) + end + + protected + def selects + relation.send(:selects) + [predicate] + end + end + end +end
\ No newline at end of file diff --git a/lib/active_relation/relations/table.rb b/lib/active_relation/relations/table.rb new file mode 100644 index 0000000000..38f540cc52 --- /dev/null +++ b/lib/active_relation/relations/table.rb @@ -0,0 +1,35 @@ +module ActiveRelation + module Relations + class Table < Base + attr_reader :table + + def initialize(table) + @table = table + end + + def attributes + attributes_by_name.values + end + + def qualify + Rename.new self, qualifications + end + + protected + def attribute(name) + attributes_by_name[name.to_s] + end + + private + def attributes_by_name + @attributes_by_name ||= connection.columns(table, "#{table} Columns").inject({}) do |attributes_by_name, column| + attributes_by_name.merge(column.name => ActiveRelation::Primitives::Attribute.new(self, column.name.to_sym)) + end + end + + def qualifications + attributes.zip(attributes.collect(&:qualified_name)).to_hash + end + end + end +end
\ No newline at end of file |