diff options
Diffstat (limited to 'lib/active_relation')
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 |