1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

scalars are now lifted; the heavy lifting is done by the operations on relation (select, join, etc.)

This commit is contained in:
Nick Kallen 2008-03-02 21:10:35 -08:00
parent c54392872f
commit 6647a1e08e
21 changed files with 100 additions and 75 deletions

View file

@ -2,4 +2,8 @@ class Array
def to_hash def to_hash
Hash[*flatten] Hash[*flatten]
end end
def to_sql(strategy = nil)
"(#{collect(&:to_sql).join(', ')})"
end
end end

View file

@ -1,7 +0,0 @@
class Class
def memoize(method)
define_method "#{method}_with_memoization" do |*args|
end
alias_method_chain method, :memoization
end
end

View file

@ -1,11 +1,11 @@
class Hash class Hash
def alias(&block) def bind(relation)
inject({}) do |aliased, (key, value)| descend { |x| x.bind(relation) }
aliased.merge(yield(key) => value)
end
end end
def to_sql(strategy = nil) def descend(&block)
"(#{values.collect(&:to_sql).join(', ')})" inject({}) do |descendent, (key, value)|
descendent.merge(yield(key) => yield(value))
end
end end
end end

View file

@ -1,18 +1,6 @@
class Object class Object
def qualify
self
end
def bind(relation) def bind(relation)
self ActiveRelation::Scalar.new(self, relation)
end
def to_sql(strategy = self.strategy)
strategy.scalar self
end
def strategy
ActiveRelation::Sql::Scalar.new(ActiveRelation::Table.engine)
end end
def metaclass def metaclass

View file

@ -1,3 +1,4 @@
require 'active_relation/primitives/attribute' require 'active_relation/primitives/attribute'
require 'active_relation/primitives/scalar'
require 'active_relation/primitives/expression' require 'active_relation/primitives/expression'

View file

@ -0,0 +1,25 @@
module ActiveRelation
class Scalar
attr_reader :value, :relation
def initialize(value, relation)
@value, @relation = value, relation
end
def to_sql(strategy = self.strategy)
strategy.scalar value
end
def strategy
ActiveRelation::Sql::Scalar.new(relation.engine)
end
def ==(other)
value == other.value
end
def qualify
self
end
end
end

View file

@ -11,7 +11,7 @@ module ActiveRelation
"INSERT", "INSERT",
"INTO #{table_sql}", "INTO #{table_sql}",
"(#{record.keys.collect(&:to_sql).join(', ')})", "(#{record.keys.collect(&:to_sql).join(', ')})",
"VALUES #{record.to_sql}" "VALUES #{record.values.to_sql}"
].join("\n") ].join("\n")
end end

View file

@ -7,8 +7,9 @@ module ActiveRelation
end end
def ==(other) def ==(other)
relation == other.relation and self.class == other.class and
orders == other.orders relation == other.relation and
orders == other.orders
end end
def descend(&block) def descend(&block)

View file

@ -42,11 +42,11 @@ module ActiveRelation
end end
def select(*predicates) def select(*predicates)
Selection.new(self, *predicates) Selection.new(self, *predicates.collect {|p| p.bind(self)})
end end
def project(*attributes) def project(*attributes)
Projection.new(self, *attributes) Projection.new(self, *attributes.collect {|a| a.bind(self)})
end end
def as(aliaz) def as(aliaz)
@ -54,7 +54,7 @@ module ActiveRelation
end end
def order(*attributes) def order(*attributes)
Order.new(self, *attributes) Order.new(self, *attributes.collect {|a| a.bind(self)})
end end
def rename(attribute, aliaz) def rename(attribute, aliaz)
@ -67,11 +67,11 @@ module ActiveRelation
module Writes module Writes
def insert(record) def insert(record)
session.create Insertion.new(self, record); self session.create Insertion.new(self, record.bind(self)); self
end end
def update(assignments) def update(assignments)
session.update Update.new(self, assignments); self session.update Update.new(self, assignments.bind(self)); self
end end
def delete def delete
@ -108,14 +108,14 @@ module ActiveRelation
def to_sql(strategy = Sql::Relation.new(engine)) def to_sql(strategy = Sql::Relation.new(engine))
strategy.select [ strategy.select [
"SELECT #{attributes.collect{ |a| a.to_sql(Sql::Projection.new(engine)) }.join(', ')}", "SELECT #{attributes.collect{ |a| a.to_sql(Sql::Projection.new(engine)) }.join(', ')}",
"FROM #{table_sql}", "FROM #{table_sql}",
(joins unless joins.blank?), (joins unless joins.blank? ),
("WHERE #{selects.collect{|s| s.to_sql(Sql::Selection.new(engine))}.join("\n\tAND ")}" unless selects.blank?), ("WHERE #{selects.collect{|s| s.to_sql(Sql::Selection.new(engine))}.join("\n\tAND ")}" unless selects.blank? ),
("ORDER BY #{orders.collect(&:to_sql)}" unless orders.blank?), ("ORDER BY #{orders.collect(&:to_sql)}" unless orders.blank? ),
("GROUP BY #{groupings.collect(&:to_sql)}" unless groupings.blank?), ("GROUP BY #{groupings.collect(&:to_sql)}" unless groupings.blank? ),
("LIMIT #{limit.to_sql}" unless limit.blank?), ("LIMIT #{limit}" unless limit.blank? ),
("OFFSET #{offset.to_sql}" unless offset.blank?) ("OFFSET #{offset}" unless offset.blank? )
].compact.join("\n"), self.alias ].compact.join("\n"), self.alias
end end
alias_method :to_s, :to_sql alias_method :to_s, :to_sql
@ -128,6 +128,14 @@ module ActiveRelation
attributes.detect { |a| a =~ attribute } attributes.detect { |a| a =~ attribute }
end end
def bind(relation)
self
end
def strategy
Sql::Predicate.new(engine)
end
def attributes; [] end def attributes; [] end
def selects; [] end def selects; [] end
def orders; [] end def orders; [] end

View file

@ -15,7 +15,7 @@ module ActiveRelation
end end
def attributes def attributes
relation.attributes.collect(&method(:baptize)) relation.attributes.collect(&method(:christen))
end end
def descend(&block) def descend(&block)
@ -23,7 +23,7 @@ module ActiveRelation
end end
private private
def baptize(attribute) def christen(attribute)
(attribute =~ self.attribute ? attribute.as(pseudonym) : attribute).bind(self) rescue nil (attribute =~ self.attribute ? attribute.as(pseudonym) : attribute).bind(self) rescue nil
end end
end end

View file

@ -9,7 +9,9 @@ module ActiveRelation
def to_sql(strategy = nil) def to_sql(strategy = nil)
[ [
"UPDATE #{table_sql} SET", "UPDATE #{table_sql} SET",
assignments.inject([]) { |assignments, (attribute, value)| assignments << "#{attribute.to_sql} = #{value.to_sql}" }.join(" "), assignments.inject("") do |assignments, (attribute, value)|
assignments << " #{attribute.to_sql} = #{value.to_sql}"
end,
("WHERE #{selects.collect(&:to_sql).join('\n\tAND ')}" unless selects.blank?) ("WHERE #{selects.collect(&:to_sql).join('\n\tAND ')}" unless selects.blank?)
].join("\n") ].join("\n")
end end

View file

@ -20,7 +20,7 @@ module ActiveRelation
end end
def select(select_sql, aliaz) def select(select_sql, aliaz)
"(#{select_sql})" + (aliaz ? " AS #{quote_column_name(aliaz)}" : "") "(#{select_sql})" + (aliaz ? " AS #{quote(aliaz)}" : "")
end end
end end

View file

@ -55,7 +55,7 @@ module ActiveRelation
end end
it 'appropriately quotes scalars' do it 'appropriately quotes scalars' do
ConcreteBinary.new(@attribute1, "1-asdf").to_sql.should be_like(" ConcreteBinary.new(@attribute1, "1-asdf".bind(@relation1)).to_sql.should be_like("
`users`.`id` <=> 1 `users`.`id` <=> 1
") ")
end end

View file

@ -3,23 +3,16 @@ require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
module ActiveRelation module ActiveRelation
describe RelationInclusion do describe RelationInclusion do
before do before do
foo = Table.new(:foo) users = Table.new(:users)
@relation1 = foo.project(foo[:id]) @relation = users.project(users[:id])
@relation2 = Table.new(:bar) @attribute = @relation[:id]
@attribute = @relation1[:id]
end
describe RelationInclusion, '==' do
it "obtains if attribute1 and attribute2 are identical" do
RelationInclusion.new(@attribute, @relation1).should == RelationInclusion.new(@attribute, @relation1)
RelationInclusion.new(@attribute, @relation1).should_not == RelationInclusion.new(@attribute, @relation2)
end
end end
describe RelationInclusion, '#to_sql' do describe RelationInclusion, '#to_sql' do
it "manufactures subselect sql" do it "manufactures subselect sql" do
RelationInclusion.new(@attribute, @relation1).to_sql.should be_like(" # remove when sufficient coverage of sql strategies exists
`foo`.`id` IN (SELECT `foo`.`id` FROM `foo`) RelationInclusion.new(@attribute, @relation).to_sql.should be_like("
`users`.`id` IN (SELECT `users`.`id` FROM `users`)
") ")
end end
end end

View file

@ -8,7 +8,7 @@ module ActiveRelation
describe '#to_sql' do describe '#to_sql' do
it 'manufactures sql inserting the data for one item' do it 'manufactures sql inserting the data for one item' do
Insertion.new(@relation, @relation[:name] => "nick").to_sql.should be_like(" Insertion.new(@relation, @relation[:name] => "nick".bind(@relation)).to_sql.should be_like("
INSERT INSERT
INTO `users` INTO `users`
(`users`.`name`) VALUES ('nick') (`users`.`name`) VALUES ('nick')

View file

@ -81,7 +81,7 @@ module ActiveRelation
.aggregate(@relation2[:user_id], @relation2[:id].count) \ .aggregate(@relation2[:user_id], @relation2[:id].count) \
.group(@relation2[:user_id]) \ .group(@relation2[:user_id]) \
.rename(@relation2[:id].count, :cnt) \ .rename(@relation2[:id].count, :cnt) \
.as(:photo_count) .as('photo_count')
end end
describe '#attributes' do describe '#attributes' do

View file

@ -89,7 +89,7 @@ module ActiveRelation
end end
it "accepts arbitrary strings" do it "accepts arbitrary strings" do
@relation.select("arbitrary").should == Selection.new(@relation, "arbitrary") @relation.select("arbitrary").should == Selection.new(@relation, Scalar.new("arbitrary", @relation))
end end
end end
@ -100,9 +100,17 @@ module ActiveRelation
end end
describe '#aggregate' do describe '#aggregate' do
before do
@expression1 = @attribute1.sum
@expression2 = @attribute2.sum
end
it 'manufactures a group relation' do it 'manufactures a group relation' do
@relation.aggregate(@expression1, @expression2).group(@attribute1, @attribute2). \ @relation.aggregate(@expression1, @expression2).group(@attribute1, @attribute2). \
should == Aggregation.new(@relation, :expressions => [@expresion, @expression2], :groupings => [@attribute1, @attribute2]) should == Aggregation.new(@relation,
:expressions => [@expression1, @expression2],
:groupings => [@attribute1, @attribute2]
)
end end
end end
@ -119,7 +127,8 @@ module ActiveRelation
describe '#insert' do describe '#insert' do
it 'manufactures an insertion relation' do it 'manufactures an insertion relation' do
Session.start do Session.start do
mock(Session.new).create(Insertion.new(@relation, record = {@relation[:name] => 'carl'})) record = {@relation[:name] => 'carl'}
mock(Session.new).create(Insertion.new(@relation, record.bind(@relation)))
@relation.insert(record).should == @relation @relation.insert(record).should == @relation
end end
end end
@ -128,7 +137,8 @@ module ActiveRelation
describe '#update' do describe '#update' do
it 'manufactures an update relation' do it 'manufactures an update relation' do
Session.start do Session.start do
mock(Session.new).update(Update.new(@relation, assignments = {@relation[:name] => 'bob'})) assignments = {@relation[:name] => Scalar.new('bob', @relation)}
mock(Session.new).update(Update.new(@relation, assignments.bind(@relation)))
@relation.update(assignments).should == @relation @relation.update(assignments).should == @relation
end end
end end

View file

@ -4,12 +4,12 @@ module ActiveRelation
describe Selection do describe Selection do
before do before do
@relation = Table.new(:users) @relation = Table.new(:users)
@predicate = Equality.new(@relation[:id], 1) @predicate = Equality.new(@relation[:id], 1.bind(@relation))
end end
describe '#initialize' do describe '#initialize' do
it "manufactures nested selection relations if multiple predicates are provided" do it "manufactures nested selection relations if multiple predicates are provided" do
@predicate2 = LessThan.new(@relation[:age], 2) @predicate2 = LessThan.new(@relation[:age], 2.bind(@relation))
Selection.new(@relation, @predicate, @predicate2). \ Selection.new(@relation, @predicate, @predicate2). \
should == Selection.new(Selection.new(@relation, @predicate2), @predicate) should == Selection.new(Selection.new(@relation, @predicate2), @predicate)
end end
@ -39,7 +39,7 @@ module ActiveRelation
end end
it "allows arbitrary sql" do it "allows arbitrary sql" do
Selection.new(@relation, "asdf").to_sql.should be_like(" Selection.new(@relation, "asdf".bind(@relation)).to_sql.should be_like("
SELECT `users`.`id`, `users`.`name` SELECT `users`.`id`, `users`.`name`
FROM `users` FROM `users`
WHERE asdf WHERE asdf

View file

@ -8,14 +8,14 @@ module ActiveRelation
describe '#to_sql' do describe '#to_sql' do
it 'manufactures sql updating attributes' do it 'manufactures sql updating attributes' do
Update.new(@relation, @relation[:name] => "nick").to_sql.should be_like(" Update.new(@relation, @relation[:name] => "nick".bind(@relation)).to_sql.should be_like("
UPDATE `users` UPDATE `users`
SET `users`.`name` = 'nick' SET `users`.`name` = 'nick'
") ")
end end
it 'manufactures sql updating a selection relation' do it 'manufactures sql updating a selection relation' do
Update.new(@relation.select(@relation[:id].equals(1)), @relation[:name] => "nick").to_sql.should be_like(" Update.new(@relation.select(@relation[:id].equals(1)), @relation[:name] => "nick".bind(@relation)).to_sql.should be_like("
UPDATE `users` UPDATE `users`
SET `users`.`name` = 'nick' SET `users`.`name` = 'nick'
WHERE `users`.`id` = 1 WHERE `users`.`id` = 1

View file

@ -32,8 +32,8 @@ module ActiveRelation
describe Session::CRUD do describe Session::CRUD do
before do before do
@insert = Insertion.new(@relation, @relation[:name] => 'nick') @insert = Insertion.new(@relation, @relation[:name] => 'nick'.bind(@relation))
@update = Update.new(@relation, @relation[:name] => 'nick') @update = Update.new(@relation, @relation[:name] => 'nick'.bind(@relation))
@delete = Deletion.new(@relation) @delete = Deletion.new(@relation)
@select = @relation @select = @relation
end end