aboutsummaryrefslogtreecommitdiffstats
path: root/lib/active_relation
diff options
context:
space:
mode:
Diffstat (limited to 'lib/active_relation')
-rw-r--r--lib/active_relation/extensions/array.rb5
-rw-r--r--lib/active_relation/extensions/base.rb47
-rw-r--r--lib/active_relation/extensions/hash.rb13
-rw-r--r--lib/active_relation/extensions/object.rb12
-rw-r--r--lib/active_relation/extensions/range.rb0
-rw-r--r--lib/active_relation/predicates/binary_predicate.rb25
-rw-r--r--lib/active_relation/predicates/equality_predicate.rb12
-rw-r--r--lib/active_relation/predicates/greater_than_or_equal_to_predicate.rb2
-rw-r--r--lib/active_relation/predicates/greater_than_predicate.rb2
-rw-r--r--lib/active_relation/predicates/less_than_or_equal_to_predicate.rb2
-rw-r--r--lib/active_relation/predicates/less_than_predicate.rb2
-rw-r--r--lib/active_relation/predicates/match_predicate.rb7
-rw-r--r--lib/active_relation/predicates/predicate.rb5
-rw-r--r--lib/active_relation/predicates/range_inclusion_predicate.rb0
-rw-r--r--lib/active_relation/predicates/relation_inclusion_predicate.rb11
-rw-r--r--lib/active_relation/relations/attribute.rb56
-rw-r--r--lib/active_relation/relations/compound_relation.rb3
-rw-r--r--lib/active_relation/relations/deletion_relation.rb22
-rw-r--r--lib/active_relation/relations/inner_join_operation.rb6
-rw-r--r--lib/active_relation/relations/inner_join_relation.rb6
-rw-r--r--lib/active_relation/relations/insertion_relation.rb29
-rw-r--r--lib/active_relation/relations/join.rb15
-rw-r--r--lib/active_relation/relations/join_operation.rb16
-rw-r--r--lib/active_relation/relations/join_relation.rb36
-rw-r--r--lib/active_relation/relations/left_outer_join_operation.rb6
-rw-r--r--lib/active_relation/relations/left_outer_join_relation.rb6
-rw-r--r--lib/active_relation/relations/order_relation.rb25
-rw-r--r--lib/active_relation/relations/projection_relation.rb23
-rw-r--r--lib/active_relation/relations/range_relation.rb18
-rw-r--r--lib/active_relation/relations/relation.rb91
-rw-r--r--lib/active_relation/relations/rename_relation.rb34
-rw-r--r--lib/active_relation/relations/selection_relation.rb21
-rw-r--r--lib/active_relation/relations/table_relation.rb31
-rw-r--r--lib/active_relation/sql_builder/columns_builder.rb16
-rw-r--r--lib/active_relation/sql_builder/conditions_builder.rb20
-rw-r--r--lib/active_relation/sql_builder/delete_builder.rb32
-rw-r--r--lib/active_relation/sql_builder/equals_condition_builder.rb18
-rw-r--r--lib/active_relation/sql_builder/inner_join_builder.rb5
-rw-r--r--lib/active_relation/sql_builder/insert_builder.rb41
-rw-r--r--lib/active_relation/sql_builder/join_builder.rb13
-rw-r--r--lib/active_relation/sql_builder/joins_builder.rb18
-rw-r--r--lib/active_relation/sql_builder/left_outer_join_builder.rb5
-rw-r--r--lib/active_relation/sql_builder/order_builder.rb16
-rw-r--r--lib/active_relation/sql_builder/select_builder.rb61
-rw-r--r--lib/active_relation/sql_builder/selects_builder.rb9
-rw-r--r--lib/active_relation/sql_builder/sql_builder.rb35
-rw-r--r--lib/active_relation/sql_builder/values_builder.rb16
47 files changed, 894 insertions, 0 deletions
diff --git a/lib/active_relation/extensions/array.rb b/lib/active_relation/extensions/array.rb
new file mode 100644
index 0000000000..5b6d6d6abd
--- /dev/null
+++ b/lib/active_relation/extensions/array.rb
@@ -0,0 +1,5 @@
+class Array
+ def to_hash
+ Hash[*flatten]
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/extensions/base.rb b/lib/active_relation/extensions/base.rb
new file mode 100644
index 0000000000..0dbdef703f
--- /dev/null
+++ b/lib/active_relation/extensions/base.rb
@@ -0,0 +1,47 @@
+class ActiveRecord::Base
+ class << self
+ def cache
+ @identity_map ||= IdentityMap.new
+ end
+
+ def relation
+ @relation ||= TableRelation.new(table_name)
+ end
+ end
+
+ class IdentityMap
+ def initialize
+ @map = {}
+ end
+
+ def get(record, &block)
+ @map[record] ||= yield
+ end
+ end
+end
+
+class ActiveRecord::Associations::BelongsToAssociation
+ def instantiate(record, joins = [])
+ @target = proxy_reflection.klass.instantiate(record, joins)
+ loaded
+ end
+
+ # this basically disables belongs_to from loading themselves
+ def reload
+ @target = 'hack'
+ end
+end
+
+class ActiveRecord::Associations::AssociationCollection
+ def instantiate(record, joins = [])
+ @target << proxy_reflection.klass.instantiate(record, joins)
+ loaded # technically, this isn't true. doesn't matter though
+ end
+end
+
+class ActiveRecord::Associations::HasManyThroughAssociation
+ def instantiate(record, joins = [])
+ @target << proxy_reflection.klass.instantiate(record, joins)
+ loaded # again, not really true.
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/extensions/hash.rb b/lib/active_relation/extensions/hash.rb
new file mode 100644
index 0000000000..f643ac17ab
--- /dev/null
+++ b/lib/active_relation/extensions/hash.rb
@@ -0,0 +1,13 @@
+class Hash
+ def alias(&block)
+ inject({}) do |aliased, (key, value)|
+ aliased.merge(yield(key) => value)
+ end
+ end
+
+ def to_sql(builder = ValuesBuilder.new)
+ builder.call do
+ row *values
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/extensions/object.rb b/lib/active_relation/extensions/object.rb
new file mode 100644
index 0000000000..c241581f86
--- /dev/null
+++ b/lib/active_relation/extensions/object.rb
@@ -0,0 +1,12 @@
+class Object
+ def qualify
+ self
+ end
+
+ def to_sql(builder = EqualsConditionBuilder.new)
+ me = self
+ builder.call do
+ value me.to_s
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/extensions/range.rb b/lib/active_relation/extensions/range.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/active_relation/extensions/range.rb
diff --git a/lib/active_relation/predicates/binary_predicate.rb b/lib/active_relation/predicates/binary_predicate.rb
new file mode 100644
index 0000000000..f5c420c833
--- /dev/null
+++ b/lib/active_relation/predicates/binary_predicate.rb
@@ -0,0 +1,25 @@
+class BinaryPredicate < Predicate
+ attr_reader :attribute1, :attribute2
+
+ def initialize(attribute1, attribute2)
+ @attribute1, @attribute2 = attribute1, attribute2
+ end
+
+ def ==(other)
+ super and
+ (attribute1.eql?(other.attribute1) and attribute2.eql?(other.attribute2))
+ end
+
+ def qualify
+ self.class.new(attribute1.qualify, attribute2.qualify)
+ end
+
+ def to_sql(builder = ConditionsBuilder.new)
+ builder.call do
+ send(predicate_name) do
+ attribute1.to_sql(self)
+ attribute2.to_sql(self)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/predicates/equality_predicate.rb b/lib/active_relation/predicates/equality_predicate.rb
new file mode 100644
index 0000000000..7040c45f67
--- /dev/null
+++ b/lib/active_relation/predicates/equality_predicate.rb
@@ -0,0 +1,12 @@
+class EqualityPredicate < BinaryPredicate
+ def ==(other)
+ self.class == other.class and
+ ((attribute1.eql?(other.attribute1) and attribute2.eql?(other.attribute2)) or
+ (attribute1.eql?(other.attribute2) and attribute2.eql?(other.attribute1)))
+ end
+
+ protected
+ def predicate_name
+ :equals
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/predicates/greater_than_or_equal_to_predicate.rb b/lib/active_relation/predicates/greater_than_or_equal_to_predicate.rb
new file mode 100644
index 0000000000..49127c312c
--- /dev/null
+++ b/lib/active_relation/predicates/greater_than_or_equal_to_predicate.rb
@@ -0,0 +1,2 @@
+class GreaterThanOrEqualToPredicate < BinaryPredicate
+end \ No newline at end of file
diff --git a/lib/active_relation/predicates/greater_than_predicate.rb b/lib/active_relation/predicates/greater_than_predicate.rb
new file mode 100644
index 0000000000..03aecaed62
--- /dev/null
+++ b/lib/active_relation/predicates/greater_than_predicate.rb
@@ -0,0 +1,2 @@
+class GreaterThanPredicate < BinaryPredicate
+end \ No newline at end of file
diff --git a/lib/active_relation/predicates/less_than_or_equal_to_predicate.rb b/lib/active_relation/predicates/less_than_or_equal_to_predicate.rb
new file mode 100644
index 0000000000..fee6ea7f35
--- /dev/null
+++ b/lib/active_relation/predicates/less_than_or_equal_to_predicate.rb
@@ -0,0 +1,2 @@
+class LessThanOrEqualToPredicate < BinaryPredicate
+end \ No newline at end of file
diff --git a/lib/active_relation/predicates/less_than_predicate.rb b/lib/active_relation/predicates/less_than_predicate.rb
new file mode 100644
index 0000000000..03cbdcf000
--- /dev/null
+++ b/lib/active_relation/predicates/less_than_predicate.rb
@@ -0,0 +1,2 @@
+class LessThanPredicate < BinaryPredicate
+end \ No newline at end of file
diff --git a/lib/active_relation/predicates/match_predicate.rb b/lib/active_relation/predicates/match_predicate.rb
new file mode 100644
index 0000000000..90a13090d4
--- /dev/null
+++ b/lib/active_relation/predicates/match_predicate.rb
@@ -0,0 +1,7 @@
+class MatchPredicate < Predicate
+ attr_reader :attribute, :regexp
+
+ def initialize(attribute, regexp)
+ @attribute, @regexp = attribute, regexp
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/predicates/predicate.rb b/lib/active_relation/predicates/predicate.rb
new file mode 100644
index 0000000000..4c395a3fdc
--- /dev/null
+++ b/lib/active_relation/predicates/predicate.rb
@@ -0,0 +1,5 @@
+class Predicate
+ def ==(other)
+ self.class == other.class
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/predicates/range_inclusion_predicate.rb b/lib/active_relation/predicates/range_inclusion_predicate.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/active_relation/predicates/range_inclusion_predicate.rb
diff --git a/lib/active_relation/predicates/relation_inclusion_predicate.rb b/lib/active_relation/predicates/relation_inclusion_predicate.rb
new file mode 100644
index 0000000000..5881a85d99
--- /dev/null
+++ b/lib/active_relation/predicates/relation_inclusion_predicate.rb
@@ -0,0 +1,11 @@
+class RelationInclusionPredicate < Predicate
+ attr_reader :attribute, :relation
+
+ def initialize(attribute, relation)
+ @attribute, @relation = attribute, relation
+ end
+
+ def ==(other)
+ super and attribute == other.attribute and relation == other.relation
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/attribute.rb b/lib/active_relation/relations/attribute.rb
new file mode 100644
index 0000000000..7583553b80
--- /dev/null
+++ b/lib/active_relation/relations/attribute.rb
@@ -0,0 +1,56 @@
+class Attribute
+ attr_reader :relation, :name, :aliaz
+
+ def initialize(relation, name, aliaz = nil)
+ @relation, @name, @aliaz = relation, name, aliaz
+ end
+
+ def aliazz(aliaz)
+ Attribute.new(relation, name, aliaz)
+ end
+
+ def qualified_name
+ "#{relation.table}.#{name}"
+ end
+
+ def qualify
+ aliazz(qualified_name)
+ end
+
+ module Predications
+ def eql?(other)
+ relation == other.relation and name == other.name and aliaz == other.aliaz
+ end
+
+ def ==(other)
+ EqualityPredicate.new(self, other)
+ end
+
+ def <(other)
+ LessThanPredicate.new(self, other)
+ end
+
+ def <=(other)
+ LessThanOrEqualToPredicate.new(self, other)
+ end
+
+ def >(other)
+ GreaterThanPredicate.new(self, other)
+ end
+
+ def >=(other)
+ GreaterThanOrEqualToPredicate.new(self, other)
+ end
+
+ def =~(regexp)
+ MatchPredicate.new(self, regexp)
+ end
+ end
+ include Predications
+
+ def to_sql(builder = SelectsBuilder.new)
+ builder.call do
+ column relation.table, name, aliaz
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/compound_relation.rb b/lib/active_relation/relations/compound_relation.rb
new file mode 100644
index 0000000000..fe92905d92
--- /dev/null
+++ b/lib/active_relation/relations/compound_relation.rb
@@ -0,0 +1,3 @@
+class CompoundRelation < Relation
+ delegate :attributes, :attribute, :joins, :selects, :orders, :table, :inserts, :to => :relation
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/deletion_relation.rb b/lib/active_relation/relations/deletion_relation.rb
new file mode 100644
index 0000000000..e060efd5f9
--- /dev/null
+++ b/lib/active_relation/relations/deletion_relation.rb
@@ -0,0 +1,22 @@
+class DeletionRelation < CompoundRelation
+ attr_reader :relation
+
+ def ==(other)
+ relation == other.relation
+ end
+
+ def initialize(relation)
+ @relation = relation
+ end
+
+ def to_sql(builder = DeleteBuilder.new)
+ builder.call do
+ delete
+ from table
+ where do
+ selects.each { |s| s.to_sql(self) }
+ end
+ end
+ end
+
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/inner_join_operation.rb b/lib/active_relation/relations/inner_join_operation.rb
new file mode 100644
index 0000000000..6b5c5ce8d0
--- /dev/null
+++ b/lib/active_relation/relations/inner_join_operation.rb
@@ -0,0 +1,6 @@
+class InnerJoinOperation < JoinOperation
+ protected
+ def relation_class
+ InnerJoinRelation
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/inner_join_relation.rb b/lib/active_relation/relations/inner_join_relation.rb
new file mode 100644
index 0000000000..5e58f241f8
--- /dev/null
+++ b/lib/active_relation/relations/inner_join_relation.rb
@@ -0,0 +1,6 @@
+class InnerJoinRelation < JoinRelation
+ protected
+ def join_type
+ :inner_join
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/insertion_relation.rb b/lib/active_relation/relations/insertion_relation.rb
new file mode 100644
index 0000000000..84752d13f9
--- /dev/null
+++ b/lib/active_relation/relations/insertion_relation.rb
@@ -0,0 +1,29 @@
+class InsertionRelation < CompoundRelation
+ attr_reader :relation, :tuple
+
+ def initialize(relation, tuple)
+ @relation, @tuple = relation, tuple
+ end
+
+ def to_sql(builder = InsertBuilder.new)
+ builder.call do
+ insert
+ into table
+ columns do
+ tuple.keys.each { |attribute| attribute.to_sql(self) }
+ end
+ values do
+ inserts.each { |insert| insert.to_sql(self) }
+ end
+ end
+ end
+
+ def ==(other)
+ relation == other.relation and tuple == other.tuple
+ end
+
+ protected
+ def inserts
+ relation.inserts + [tuple]
+ 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..9a6196deac
--- /dev/null
+++ b/lib/active_relation/relations/join.rb
@@ -0,0 +1,15 @@
+class Join
+ attr_reader :relation1, :relation2, :predicates, :join_type
+
+ def initialize(relation1, relation2, predicates, join_type)
+ @relation1, @relation2, @predicates, @join_type = relation1, relation2, predicates, join_type
+ end
+
+ def to_sql(builder = JoinsBuilder.new)
+ builder.call do
+ send(join_type, relation2.table) do
+ predicates.each { |p| p.to_sql(self) }
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/join_operation.rb b/lib/active_relation/relations/join_operation.rb
new file mode 100644
index 0000000000..2b4548a041
--- /dev/null
+++ b/lib/active_relation/relations/join_operation.rb
@@ -0,0 +1,16 @@
+class JoinOperation
+ attr_reader :relation1, :relation2
+
+ def initialize(relation1, relation2)
+ @relation1, @relation2 = relation1, relation2
+ end
+
+ def on(*predicates)
+ relation_class.new(relation1, relation2, *predicates)
+ end
+
+ def ==(other)
+ (relation1 == other.relation1 and relation2 == other.relation2) or
+ (relation1 == other.relation2 and relation2 == other.relation1)
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/join_relation.rb b/lib/active_relation/relations/join_relation.rb
new file mode 100644
index 0000000000..79c8a915b8
--- /dev/null
+++ b/lib/active_relation/relations/join_relation.rb
@@ -0,0 +1,36 @@
+class JoinRelation < Relation
+ attr_reader :relation1, :relation2, :predicates
+
+ def initialize(relation1, relation2, *predicates)
+ @relation1, @relation2, @predicates = 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
+ self.class.new(relation1.qualify, relation2.qualify, *predicates.collect(&:qualify))
+ end
+
+ protected
+ def joins
+ relation1.joins + relation2.joins + [Join.new(relation1, relation2, predicates, join_type)]
+ 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
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/left_outer_join_operation.rb b/lib/active_relation/relations/left_outer_join_operation.rb
new file mode 100644
index 0000000000..fbb2a4e2ed
--- /dev/null
+++ b/lib/active_relation/relations/left_outer_join_operation.rb
@@ -0,0 +1,6 @@
+class LeftOuterJoinOperation < JoinOperation
+ protected
+ def relation_class
+ LeftOuterJoinRelation
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/left_outer_join_relation.rb b/lib/active_relation/relations/left_outer_join_relation.rb
new file mode 100644
index 0000000000..6d13d8da07
--- /dev/null
+++ b/lib/active_relation/relations/left_outer_join_relation.rb
@@ -0,0 +1,6 @@
+class LeftOuterJoinRelation < JoinRelation
+ protected
+ def join_type
+ :left_outer_join
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/order_relation.rb b/lib/active_relation/relations/order_relation.rb
new file mode 100644
index 0000000000..b39dc45c3f
--- /dev/null
+++ b/lib/active_relation/relations/order_relation.rb
@@ -0,0 +1,25 @@
+class OrderRelation < CompoundRelation
+ 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
+ OrderRelation.new(relation.qualify, *attributes.collect { |a| a.qualify })
+ end
+
+ def to_sql(builder = SelectBuilder.new)
+ relation.to_sql(builder).call do
+ attributes.each do |attribute|
+ order_by do
+ attribute.to_sql(self)
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/projection_relation.rb b/lib/active_relation/relations/projection_relation.rb
new file mode 100644
index 0000000000..1a0e8dbfe4
--- /dev/null
+++ b/lib/active_relation/relations/projection_relation.rb
@@ -0,0 +1,23 @@
+class ProjectionRelation < Relation
+ 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
+ ProjectionRelation.new(relation.qualify, *attributes.collect(&:qualify))
+ end
+
+ def to_sql(builder = SelectBuilder.new)
+ relation.to_sql(builder).call do
+ select do
+ attributes.collect { |a| a.to_sql(self) }
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/range_relation.rb b/lib/active_relation/relations/range_relation.rb
new file mode 100644
index 0000000000..9225d5615b
--- /dev/null
+++ b/lib/active_relation/relations/range_relation.rb
@@ -0,0 +1,18 @@
+class RangeRelation < Relation
+ attr_reader :relation, :range
+
+ def initialize(relation, range)
+ @relation, @range = relation, range
+ end
+
+ def ==(other)
+ relation == other.relation and range == other.range
+ end
+
+ def to_sql(builder = SelectBuilder.new)
+ relation.to_sql(builder).call do
+ limit range.last - range.first + 1
+ offset range.first
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/relation.rb b/lib/active_relation/relations/relation.rb
new file mode 100644
index 0000000000..be6ee760a5
--- /dev/null
+++ b/lib/active_relation/relations/relation.rb
@@ -0,0 +1,91 @@
+class Relation
+ 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)
+ InnerJoinOperation.new(self, other)
+ end
+
+ def <<(other)
+ LeftOuterJoinOperation.new(self, other)
+ end
+
+ def [](index)
+ case index
+ when Symbol
+ attribute(index)
+ when Range
+ RangeRelation.new(self, index)
+ end
+ end
+
+ def include?(attribute)
+ RelationInclusionPredicate.new(attribute, self)
+ end
+
+ def select(*predicates)
+ SelectionRelation.new(self, *predicates)
+ end
+
+ def project(*attributes)
+ ProjectionRelation.new(self, *attributes)
+ end
+
+ def order(*attributes)
+ OrderRelation.new(self, *attributes)
+ end
+
+ def rename(attribute, aliaz)
+ RenameRelation.new(self, attribute => aliaz)
+ end
+
+ def insert(tuple)
+ InsertionRelation.new(self, tuple)
+ end
+
+ def delete
+ DeletionRelation.new(self)
+ end
+ end
+ include Operations
+
+ def connection
+ ActiveRecord::Base.connection
+ end
+
+ def to_sql(builder = SelectBuilder.new)
+ builder.call do
+ select do
+ attributes.each { |a| a.to_sql(self) }
+ end
+ from table do
+ joins.each { |j| j.to_sql(self) }
+ end
+ where do
+ selects.each { |s| s.to_sql(self) }
+ end
+ order_by do
+ orders.each { |o| o.to_sql(self) }
+ end
+ end
+ end
+ delegate :to_s, :to => :to_sql
+
+ protected
+ def attributes; [] end
+ def joins; [] end
+ def selects; [] end
+ def orders; [] end
+ def inserts; [] end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/rename_relation.rb b/lib/active_relation/relations/rename_relation.rb
new file mode 100644
index 0000000000..8acf5091b2
--- /dev/null
+++ b/lib/active_relation/relations/rename_relation.rb
@@ -0,0 +1,34 @@
+class RenameRelation < CompoundRelation
+ attr_reader :relation, :schmattribute, :aliaz
+
+ def initialize(relation, renames)
+ @schmattribute, @aliaz = renames.shift
+ @relation = renames.empty?? relation : RenameRelation.new(relation, renames)
+ end
+
+ def ==(other)
+ relation == other.relation and schmattribute.eql?(other.schmattribute) and aliaz == other.aliaz
+ end
+
+ def attributes
+ relation.attributes.collect { |a| substitute(a) }
+ end
+
+ def qualify
+ RenameRelation.new(relation.qualify, schmattribute.qualify => aliaz)
+ end
+
+ protected
+ def attribute(name)
+ case
+ when name == aliaz then schmattribute.aliazz(aliaz)
+ when relation[name].eql?(schmattribute) then nil
+ else relation[name]
+ end
+ end
+
+ private
+ def substitute(a)
+ a.eql?(schmattribute) ? a.aliazz(aliaz) : a
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/selection_relation.rb b/lib/active_relation/relations/selection_relation.rb
new file mode 100644
index 0000000000..77864efb28
--- /dev/null
+++ b/lib/active_relation/relations/selection_relation.rb
@@ -0,0 +1,21 @@
+class SelectionRelation < CompoundRelation
+ attr_reader :relation, :predicate
+
+ def initialize(relation, *predicates)
+ @predicate = predicates.shift
+ @relation = predicates.empty?? relation : SelectionRelation.new(relation, *predicates)
+ end
+
+ def ==(other)
+ relation == other.relation and predicate == other.predicate
+ end
+
+ def qualify
+ SelectionRelation.new(relation.qualify, predicate.qualify)
+ end
+
+ protected
+ def selects
+ relation.send(:selects) + [predicate]
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/relations/table_relation.rb b/lib/active_relation/relations/table_relation.rb
new file mode 100644
index 0000000000..5a47ae7a34
--- /dev/null
+++ b/lib/active_relation/relations/table_relation.rb
@@ -0,0 +1,31 @@
+class TableRelation < Relation
+ attr_reader :table
+
+ def initialize(table)
+ @table = table
+ end
+
+ def attributes
+ attributes_by_name.values
+ end
+
+ def qualify
+ RenameRelation.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 => Attribute.new(self, column.name.to_sym))
+ end
+ end
+
+ def qualifications
+ attributes.zip(attributes.collect(&:qualified_name)).to_hash
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/columns_builder.rb b/lib/active_relation/sql_builder/columns_builder.rb
new file mode 100644
index 0000000000..a8a5d0e4ca
--- /dev/null
+++ b/lib/active_relation/sql_builder/columns_builder.rb
@@ -0,0 +1,16 @@
+class ColumnsBuilder < SqlBuilder
+ def initialize(&block)
+ @columns = []
+ super(&block)
+ end
+
+ def to_s
+ @columns.join(', ')
+ end
+
+ def column(table, column, aliaz = nil)
+ @columns << "#{quote_table_name(table)}.#{quote_column_name(column)}"
+ end
+
+ delegate :blank?, :to => :@columns
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/conditions_builder.rb b/lib/active_relation/sql_builder/conditions_builder.rb
new file mode 100644
index 0000000000..60430f65d8
--- /dev/null
+++ b/lib/active_relation/sql_builder/conditions_builder.rb
@@ -0,0 +1,20 @@
+class ConditionsBuilder < SqlBuilder
+ def initialize(&block)
+ @conditions = []
+ super(&block)
+ end
+
+ def equals(&block)
+ @conditions << EqualsConditionBuilder.new(&block)
+ end
+
+ def value(value)
+ @conditions << value
+ end
+
+ def to_s
+ @conditions.join(' AND ')
+ end
+
+ delegate :blank?, :to => :@conditions
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/delete_builder.rb b/lib/active_relation/sql_builder/delete_builder.rb
new file mode 100644
index 0000000000..2e8be94dfe
--- /dev/null
+++ b/lib/active_relation/sql_builder/delete_builder.rb
@@ -0,0 +1,32 @@
+class DeleteBuilder < SqlBuilder
+ def delete
+ end
+
+ def from(table)
+ @table = table
+ end
+
+ def where(&block)
+ @conditions = ConditionsBuilder.new(&block)
+ end
+
+ def to_s
+ [delete_clause,
+ from_clause,
+ where_clause
+ ].compact.join("\n")
+ end
+
+ private
+ def delete_clause
+ "DELETE"
+ end
+
+ def from_clause
+ "FROM #{quote_table_name(@table)}"
+ end
+
+ def where_clause
+ "WHERE #{@conditions}" unless @conditions.blank?
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/equals_condition_builder.rb b/lib/active_relation/sql_builder/equals_condition_builder.rb
new file mode 100644
index 0000000000..cfa919c34c
--- /dev/null
+++ b/lib/active_relation/sql_builder/equals_condition_builder.rb
@@ -0,0 +1,18 @@
+class EqualsConditionBuilder < SqlBuilder
+ def initialize(&block)
+ @operands = []
+ super(&block)
+ end
+
+ def column(table, column, aliaz = nil)
+ @operands << "#{quote_table_name(table)}.#{quote_column_name(column)}"
+ end
+
+ def value(value)
+ @operands << value
+ end
+
+ def to_s
+ "#{@operands[0]} = #{@operands[1]}"
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/inner_join_builder.rb b/lib/active_relation/sql_builder/inner_join_builder.rb
new file mode 100644
index 0000000000..6aec703325
--- /dev/null
+++ b/lib/active_relation/sql_builder/inner_join_builder.rb
@@ -0,0 +1,5 @@
+class InnerJoinBuilder < JoinBuilder
+ def join_type
+ "INNER JOIN"
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/insert_builder.rb b/lib/active_relation/sql_builder/insert_builder.rb
new file mode 100644
index 0000000000..09deefad10
--- /dev/null
+++ b/lib/active_relation/sql_builder/insert_builder.rb
@@ -0,0 +1,41 @@
+class InsertBuilder < SqlBuilder
+ def insert
+ end
+
+ def into(table)
+ @table = table
+ end
+
+ def columns(&block)
+ @columns = ColumnsBuilder.new(&block)
+ end
+
+ def values(&block)
+ @values = ValuesBuilder.new(&block)
+ end
+
+ def to_s
+ [insert_clause,
+ into_clause,
+ columns_clause,
+ values_clause
+ ].compact.join("\n")
+ end
+
+ private
+ def insert_clause
+ "INSERT"
+ end
+
+ def into_clause
+ "INTO #{quote_table_name(@table)}"
+ end
+
+ def values_clause
+ "VALUES #{@values}" unless @values.blank?
+ end
+
+ def columns_clause
+ "(#{@columns})" unless @columns.blank?
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/join_builder.rb b/lib/active_relation/sql_builder/join_builder.rb
new file mode 100644
index 0000000000..ef63d1fcb1
--- /dev/null
+++ b/lib/active_relation/sql_builder/join_builder.rb
@@ -0,0 +1,13 @@
+class JoinBuilder < SqlBuilder
+ def initialize(table, &block)
+ @table = table
+ @conditions = ConditionsBuilder.new
+ super(&block)
+ end
+
+ delegate :call, :to => :@conditions
+
+ def to_s
+ "#{join_type} #{quote_table_name(@table)} ON #{@conditions}"
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/joins_builder.rb b/lib/active_relation/sql_builder/joins_builder.rb
new file mode 100644
index 0000000000..36a92e9922
--- /dev/null
+++ b/lib/active_relation/sql_builder/joins_builder.rb
@@ -0,0 +1,18 @@
+class JoinsBuilder < SqlBuilder
+ def initialize(&block)
+ @joins = []
+ super(&block)
+ end
+
+ def inner_join(table, &block)
+ @joins << InnerJoinBuilder.new(table, &block)
+ end
+
+ def left_outer_join(table, &block)
+ @joins << LeftOuterJoinBuilder.new(table, &block)
+ end
+
+ def to_s
+ @joins.join(' ')
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/left_outer_join_builder.rb b/lib/active_relation/sql_builder/left_outer_join_builder.rb
new file mode 100644
index 0000000000..dad3f85810
--- /dev/null
+++ b/lib/active_relation/sql_builder/left_outer_join_builder.rb
@@ -0,0 +1,5 @@
+class LeftOuterJoinBuilder < JoinBuilder
+ def join_type
+ "LEFT OUTER JOIN"
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/order_builder.rb b/lib/active_relation/sql_builder/order_builder.rb
new file mode 100644
index 0000000000..66a8cfdba9
--- /dev/null
+++ b/lib/active_relation/sql_builder/order_builder.rb
@@ -0,0 +1,16 @@
+class OrderBuilder < SqlBuilder
+ def initialize(&block)
+ @orders = []
+ super(&block)
+ end
+
+ def column(table, column, aliaz = nil)
+ @orders << "#{quote_table_name(table)}.#{quote_column_name(column)}"
+ end
+
+ def to_s
+ @orders.join(', ')
+ end
+
+ delegate :blank?, :to => :@orders
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/select_builder.rb b/lib/active_relation/sql_builder/select_builder.rb
new file mode 100644
index 0000000000..57116cb64b
--- /dev/null
+++ b/lib/active_relation/sql_builder/select_builder.rb
@@ -0,0 +1,61 @@
+class SelectBuilder < SqlBuilder
+ def select(&block)
+ @selects = SelectsBuilder.new(&block)
+ end
+
+ def from(table, &block)
+ @table = table
+ @joins = JoinsBuilder.new(&block)
+ end
+
+ def where(&block)
+ @conditions = ConditionsBuilder.new(&block)
+ end
+
+ def order_by(&block)
+ @orders = OrderBuilder.new(&block)
+ end
+
+ def limit(i, offset = nil)
+ @limit = i
+ offset(offset) if offset
+ end
+
+ def offset(i)
+ @offset = i
+ end
+
+ def to_s
+ [select_clause,
+ from_clause,
+ where_clause,
+ order_by_clause,
+ limit_clause,
+ offset_clause].compact.join("\n")
+ end
+
+ private
+ def select_clause
+ "SELECT #{@selects}" unless @selects.blank?
+ end
+
+ def from_clause
+ "FROM #{quote_table_name(@table)} #{@joins}" unless @table.blank?
+ end
+
+ def where_clause
+ "WHERE #{@conditions}" unless @conditions.blank?
+ end
+
+ def order_by_clause
+ "ORDER BY #{@orders}" unless @orders.blank?
+ end
+
+ def limit_clause
+ "LIMIT #{@limit}" unless @limit.blank?
+ end
+
+ def offset_clause
+ "OFFSET #{@offset}" unless @offset.blank?
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/selects_builder.rb b/lib/active_relation/sql_builder/selects_builder.rb
new file mode 100644
index 0000000000..6ad06d0ae4
--- /dev/null
+++ b/lib/active_relation/sql_builder/selects_builder.rb
@@ -0,0 +1,9 @@
+class SelectsBuilder < ColumnsBuilder
+ def all
+ @columns << :*
+ end
+
+ def column(table, column, aliaz = nil)
+ @columns << "#{quote_table_name(table)}.#{quote_column_name(column)}" + (aliaz ? " AS #{quote(aliaz)}" : '')
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/sql_builder.rb b/lib/active_relation/sql_builder/sql_builder.rb
new file mode 100644
index 0000000000..c984444e41
--- /dev/null
+++ b/lib/active_relation/sql_builder/sql_builder.rb
@@ -0,0 +1,35 @@
+class SqlBuilder
+ def initialize(&block)
+ @callers = []
+ call(&block) if block
+ end
+
+ def method_missing(method, *args)
+ @callers.last.send(method, *args)
+ end
+
+ def ==(other)
+ to_s == other.to_s
+ end
+
+ def to_s
+ end
+
+ def call(&block)
+ returning self do |builder|
+ @callers << eval("self", block.binding)
+ begin
+ instance_eval &block
+ ensure
+ @callers.pop
+ end
+ end
+ end
+
+ private
+ delegate :quote_table_name, :quote_column_name, :quote, :to => :connection
+
+ def connection
+ ActiveRecord::Base.connection
+ end
+end \ No newline at end of file
diff --git a/lib/active_relation/sql_builder/values_builder.rb b/lib/active_relation/sql_builder/values_builder.rb
new file mode 100644
index 0000000000..f22b1e507e
--- /dev/null
+++ b/lib/active_relation/sql_builder/values_builder.rb
@@ -0,0 +1,16 @@
+class ValuesBuilder < SqlBuilder
+ def initialize(&block)
+ @values = []
+ super(&block)
+ end
+
+ def row(*values)
+ @values << "(#{values.collect { |v| quote(v) }.join(', ')})"
+ end
+
+ def to_s
+ @values.join(', ')
+ end
+
+ delegate :blank?, :to => :@values
+end \ No newline at end of file