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:
parent
c54392872f
commit
6647a1e08e
21 changed files with 100 additions and 75 deletions
|
@ -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
|
|
@ -1,7 +0,0 @@
|
||||||
class Class
|
|
||||||
def memoize(method)
|
|
||||||
define_method "#{method}_with_memoization" do |*args|
|
|
||||||
end
|
|
||||||
alias_method_chain method, :memoization
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -84,7 +84,7 @@ module ActiveRelation
|
||||||
|
|
||||||
class RelationInclusion < Binary
|
class RelationInclusion < Binary
|
||||||
alias_method :relation, :operand2
|
alias_method :relation, :operand2
|
||||||
|
|
||||||
protected
|
protected
|
||||||
def predicate_sql
|
def predicate_sql
|
||||||
'IN'
|
'IN'
|
||||||
|
|
|
@ -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'
|
||||||
|
|
||||||
|
|
25
lib/active_relation/primitives/scalar.rb
Normal file
25
lib/active_relation/primitives/scalar.rb
Normal 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
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
@ -127,6 +127,14 @@ module ActiveRelation
|
||||||
def attribute_for_attribute(attribute)
|
def attribute_for_attribute(attribute)
|
||||||
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue