diff options
-rw-r--r-- | lib/active_relation/relations/aggregation.rb | 1 | ||||
-rw-r--r-- | lib/active_relation/relations/alias.rb | 2 | ||||
-rw-r--r-- | lib/active_relation/relations/join.rb | 21 | ||||
-rw-r--r-- | lib/active_relation/relations/rename.rb | 4 | ||||
-rw-r--r-- | lib/active_relation/relations/table.rb | 2 | ||||
-rw-r--r-- | spec/active_relation/predicates/binary_spec.rb | 10 | ||||
-rw-r--r-- | spec/active_relation/primitives/attribute_spec.rb | 8 | ||||
-rw-r--r-- | spec/active_relation/relations/alias_spec.rb | 1 | ||||
-rw-r--r-- | spec/active_relation/relations/join_spec.rb | 81 | ||||
-rw-r--r-- | spec/active_relation/relations/rename_spec.rb | 35 | ||||
-rw-r--r-- | spec/active_relation/relations/table_spec.rb | 9 |
11 files changed, 94 insertions, 80 deletions
diff --git a/lib/active_relation/relations/aggregation.rb b/lib/active_relation/relations/aggregation.rb index 9e803b3587..7910223673 100644 --- a/lib/active_relation/relations/aggregation.rb +++ b/lib/active_relation/relations/aggregation.rb @@ -21,7 +21,6 @@ module ActiveRelation expressions.collect { |e| e.bind(self) } end - protected def aggregation? true end diff --git a/lib/active_relation/relations/alias.rb b/lib/active_relation/relations/alias.rb index 5b45d9dce4..941a95c58e 100644 --- a/lib/active_relation/relations/alias.rb +++ b/lib/active_relation/relations/alias.rb @@ -3,7 +3,7 @@ module ActiveRelation attr_reader :alias def aliased_prefix_for(attribute) - @alias + self[attribute] and @alias end def initialize(relation, aliaz) diff --git a/lib/active_relation/relations/join.rb b/lib/active_relation/relations/join.rb index 4c4f2e66a2..c61680fd55 100644 --- a/lib/active_relation/relations/join.rb +++ b/lib/active_relation/relations/join.rb @@ -20,20 +20,22 @@ module ActiveRelation def attributes [ - relation1.aggregation?? relation1.attributes.collect(&:to_attribute) : relation1.attributes, - relation2.aggregation?? relation2.attributes.collect(&:to_attribute) : relation2.attributes, + relation1.attributes.collect(&:to_attribute), + relation2.attributes.collect(&:to_attribute), ].flatten.collect { |a| a.bind(self) } end def prefix_for(attribute) - (relation1[attribute] && relation1.aliased_prefix_for(attribute)) || - (relation2[attribute] && relation2.aliased_prefix_for(attribute)) + relation1.aliased_prefix_for(attribute) or + relation2.aliased_prefix_for(attribute) end alias_method :aliased_prefix_for, :prefix_for protected def joins - [relation1.joins, relation2.joins, join].compact.join(" ") + right_table_sql = relation2.aggregation?? relation2.to_sql(Sql::Aggregation.new) : relation2.send(:table_sql) + this_join = [join_sql, right_table_sql, "ON", predicates.collect { |p| p.bind(self).to_sql(Sql::Predicate.new) }.join(' AND ')].join(" ") + [relation1.joins, relation2.joins, this_join].compact.join(" ") end def selects @@ -46,14 +48,5 @@ module ActiveRelation def table_sql relation1.aggregation?? relation1.to_sql(Sql::Aggregation.new) : relation1.send(:table_sql) end - - private - def join - [join_sql, right_table_sql, "ON", predicates.collect { |p| p.bind(self).to_sql(Sql::Predicate.new) }.join(' AND ')].join(" ") - end - - def right_table_sql - relation2.aggregation?? relation2.to_sql(Sql::Aggregation.new) : relation2.send(:table_sql) - end end end
\ No newline at end of file diff --git a/lib/active_relation/relations/rename.rb b/lib/active_relation/relations/rename.rb index 6b1b29bf75..20e79ebefb 100644 --- a/lib/active_relation/relations/rename.rb +++ b/lib/active_relation/relations/rename.rb @@ -8,8 +8,8 @@ module ActiveRelation end def ==(other) - self.class == other.class and - relation == other.relation and + self.class == other.class and + relation == other.relation and attribute == other.attribute and pseudonym == other.pseudonym end diff --git a/lib/active_relation/relations/table.rb b/lib/active_relation/relations/table.rb index c8ccb22fdd..d85c268019 100644 --- a/lib/active_relation/relations/table.rb +++ b/lib/active_relation/relations/table.rb @@ -17,7 +17,7 @@ module ActiveRelation end def prefix_for(attribute) - name + self[attribute] and name end alias_method :aliased_prefix_for, :prefix_for diff --git a/spec/active_relation/predicates/binary_spec.rb b/spec/active_relation/predicates/binary_spec.rb index 599cfa2942..127375f24c 100644 --- a/spec/active_relation/predicates/binary_spec.rb +++ b/spec/active_relation/predicates/binary_spec.rb @@ -3,10 +3,10 @@ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') module ActiveRelation describe Binary do before do - @relation1 = Table.new(:foo) - @relation2 = Table.new(:bar) - @attribute1 = Attribute.new(@relation1, :name1) - @attribute2 = Attribute.new(@relation2, :name2) + @relation1 = Table.new(:users) + @relation2 = Table.new(:photos) + @attribute1 = @relation1[:id] + @attribute2 = @relation2[:id] class ConcreteBinary < Binary def predicate_sql "<=>" @@ -43,7 +43,7 @@ module ActiveRelation describe '#to_sql' do it 'manufactures correct sql' do ConcreteBinary.new(@attribute1, @attribute2).to_sql.should be_like(""" - `foo`.`name1` <=> `bar`.`name2` + `users`.`id` <=> `photos`.`id` """) end end diff --git a/spec/active_relation/primitives/attribute_spec.rb b/spec/active_relation/primitives/attribute_spec.rb index 39feacbac7..a931ea2150 100644 --- a/spec/active_relation/primitives/attribute_spec.rb +++ b/spec/active_relation/primitives/attribute_spec.rb @@ -62,12 +62,16 @@ module ActiveRelation describe '#to_sql' do describe Sql::Strategy do + before do + stub(@relation).prefix_for(anything) { 'bruisers' } + end + it "manufactures sql without an alias if the strategy is Predicate" do - Attribute.new(@relation, :name, :alias).to_sql(Sql::Predicate.new).should be_like("`users`.`name`") + Attribute.new(@relation, :name, :alias).to_sql(Sql::Predicate.new).should be_like("`bruisers`.`name`") end it "manufactures sql with an alias if the strategy is Projection" do - Attribute.new(@relation, :name, :alias).to_sql(Sql::Projection.new).should be_like("`users`.`name` AS 'alias'") + Attribute.new(@relation, :name, :alias).to_sql(Sql::Projection.new).should be_like("`bruisers`.`name` AS 'alias'") end end end diff --git a/spec/active_relation/relations/alias_spec.rb b/spec/active_relation/relations/alias_spec.rb index ddb0c59d08..a2deae2840 100644 --- a/spec/active_relation/relations/alias_spec.rb +++ b/spec/active_relation/relations/alias_spec.rb @@ -16,6 +16,7 @@ module ActiveRelation describe '#aliased_prefix_for' do it "returns the alias" do @alias_relation.aliased_prefix_for(@relation[:id]).should == :foo + @alias_relation.aliased_prefix_for(:does_not_exist).should be_nil end end end diff --git a/spec/active_relation/relations/join_spec.rb b/spec/active_relation/relations/join_spec.rb index b131ac6918..b60238fa7e 100644 --- a/spec/active_relation/relations/join_spec.rb +++ b/spec/active_relation/relations/join_spec.rb @@ -5,13 +5,19 @@ module ActiveRelation before do @relation1 = Table.new(:users) @relation2 = Table.new(:photos) - @predicate = Equality.new(@relation1[:id], @relation2[:id]) + @predicate = @relation1[:id].equals(@relation2[:user_id]) end describe '==' do + before do + @another_predicate = @relation1[:id].equals(1) + @another_relation = Table.new(:cameras) + end + it 'obtains if the two relations and the predicate are identical' do Join.new("INNER JOIN", @relation1, @relation2, @predicate).should == Join.new("INNER JOIN", @relation1, @relation2, @predicate) - Join.new("INNER JOIN", @relation1, @relation2, @predicate).should_not == Join.new("INNER JOIN", @relation1, @relation1, @predicate) + Join.new("INNER JOIN", @relation1, @relation2, @predicate).should_not == Join.new("INNER JOIN", @relation1, @another_relation, @predicate) + Join.new("INNER JOIN", @relation1, @relation2, @predicate).should_not == Join.new("INNER JOIN", @relation1, @relation2, @another_predicate) end it 'is commutative on the relations' do @@ -26,22 +32,6 @@ module ActiveRelation end end - describe '#attributes' do - describe 'with simple relations' do - before do - @join = Join.new("INNER JOIN", @relation1, @relation2, @predicate) - end - - it 'combines the attributes of the two relations' do - @join.attributes.should == - (@relation1.attributes + @relation2.attributes).collect { |a| a.bind(@join) } - end - - it 'does something peculiar with expressions when aggregate' do - pending - end - end - end describe '#prefix_for' do it 'needs a test' do @@ -49,13 +39,21 @@ module ActiveRelation end end - describe '#to_sql' do - describe 'with simple relations' do + describe 'with simple relations' do + describe '#attributes' do + it 'combines the attributes of the two relations' do + simple_join = Join.new("INNER JOIN", @relation1, @relation2, @predicate) + simple_join.attributes.should == + (@relation1.attributes + @relation2.attributes).collect { |a| a.bind(simple_join) } + end + end + + describe '#to_sql' do it 'manufactures sql joining the two tables on the predicate' do Join.new("INNER JOIN", @relation1, @relation2, @predicate).to_sql.should be_like(""" SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id` FROM `users` - INNER JOIN `photos` ON `users`.`id` = `photos`.`id` + INNER JOIN `photos` ON `users`.`id` = `photos`.`user_id` """) end @@ -64,49 +62,60 @@ module ActiveRelation @relation2.select(@relation2[:id].equals(2)), @predicate).to_sql.should be_like(""" SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id` FROM `users` - INNER JOIN `photos` ON `users`.`id` = `photos`.`id` + INNER JOIN `photos` ON `users`.`id` = `photos`.`user_id` WHERE `users`.`id` = 1 AND `photos`.`id` = 2 """) end end - - describe 'aggregated relations' do - before do - @relation = Table.new(:users) - photos = Table.new(:photos) - @aggregate_relation = photos.aggregate(photos[:user_id], photos[:id].count).group(photos[:user_id]).rename(photos[:id].count, :cnt).as(:photo_count) - @predicate = Equality.new(@aggregate_relation[:user_id], @relation[:id]) + end + + describe 'with aggregated relations' do + before do + @aggregation = @relation2 \ + .aggregate(@relation2[:user_id], @relation2[:id].count) \ + .group(@relation2[:user_id]) \ + .rename(@relation2[:id].count, :cnt) \ + .as(:photo_count) + end + + describe '#attributes' do + it 'it transforms aggregate expressions into attributes' do + join_with_aggregation = Join.new("INNER JOIN", @relation1, @aggregation, @predicate) + join_with_aggregation.attributes.should == + (@relation1.attributes + @aggregation.attributes).collect(&:to_attribute).collect { |a| a.bind(join_with_aggregation) } end - + end + + describe '#to_sql' do describe 'with the aggregation on the right' do it 'manufactures sql joining the left table to a derived table' do - Join.new("INNER JOIN", @relation, @aggregate_relation, @predicate).to_sql.should be_like(""" + Join.new("INNER JOIN", @relation1, @aggregation, @predicate).to_sql.should be_like(""" SELECT `users`.`id`, `users`.`name`, `photo_count`.`user_id`, `photo_count`.`cnt` FROM `users` INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photo_count` - ON `photo_count`.`user_id` = `users`.`id` + ON `users`.`id` = `photo_count`.`user_id` """) end end describe 'with the aggregation on the left' do it 'manufactures sql joining the right table to a derived table' do - Join.new("INNER JOIN", @aggregate_relation, @relation, @predicate).to_sql.should be_like(""" + Join.new("INNER JOIN", @aggregation, @relation1, @predicate).to_sql.should be_like(""" SELECT `photo_count`.`user_id`, `photo_count`.`cnt`, `users`.`id`, `users`.`name` FROM (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photo_count` INNER JOIN `users` - ON `photo_count`.`user_id` = `users`.`id` + ON `users`.`id` = `photo_count`.`user_id` """) end end it "keeps selects on the aggregation within the derived table" do - Join.new("INNER JOIN", @relation, @aggregate_relation.select(@aggregate_relation[:user_id].equals(1)), @predicate).to_sql.should be_like(""" + Join.new("INNER JOIN", @relation1, @aggregation.select(@aggregation[:user_id].equals(1)), @predicate).to_sql.should be_like(""" SELECT `users`.`id`, `users`.`name`, `photo_count`.`user_id`, `photo_count`.`cnt` FROM `users` INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` WHERE `photos`.`user_id` = 1 GROUP BY `photos`.`user_id`) AS `photo_count` - ON `photo_count`.`user_id` = `users`.`id` + ON `users`.`id` = `photo_count`.`user_id` """) end end diff --git a/spec/active_relation/relations/rename_spec.rb b/spec/active_relation/relations/rename_spec.rb index 339bb2f39f..deb538615b 100644 --- a/spec/active_relation/relations/rename_spec.rb +++ b/spec/active_relation/relations/rename_spec.rb @@ -3,44 +3,51 @@ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') module ActiveRelation describe Rename do before do - @relation1 = Table.new(:users) - @relation2 = Table.new(:photos) - @renamed_relation = Rename.new(@relation1, @relation1[:id] => :schmid) + @relation = Table.new(:users) end describe '#initialize' do it "manufactures nested rename relations if multiple renames are provided" do - Rename.new(@relation1, @relation1[:id] => :humpty, @relation1[:name] => :dumpty). \ - should == Rename.new(Rename.new(@relation1, @relation1[:name] => :dumpty), @relation1[:id] => :humpty) + Rename.new(@relation, @relation[:id] => :humpty, @relation[:name] => :dumpty). \ + should == Rename.new(Rename.new(@relation, @relation[:name] => :dumpty), @relation[:id] => :humpty) end end describe '==' do + before do + @another_relation = Table.new(:photos) + end + it "obtains if the relation, attribute, and rename are identical" do - Rename.new(@relation1, @relation1[:id] => :humpty).should == Rename.new(@relation1, @relation1[:id] => :humpty) - Rename.new(@relation1, @relation1[:id] => :humpty).should_not == Rename.new(@relation1, @relation1[:id] => :dumpty) - Rename.new(@relation1, @relation1[:id] => :humpty).should_not == Rename.new(@relation2, @relation2[:id] => :humpty) + Rename.new(@relation, @relation[:id] => :humpty).should == Rename.new(@relation, @relation[:id] => :humpty) + Rename.new(@relation, @relation[:id] => :humpty).should_not == Rename.new(@relation, @relation[:id] => :dumpty) + Rename.new(@relation, @relation[:id] => :humpty).should_not == Rename.new(@another_relation, @relation[:id] => :humpty) end end describe '#attributes' do + before do + @renamed_relation = Rename.new(@relation, @relation[:id] => :schmid) + end + it "manufactures a list of attributes with the renamed attribute renameed" do - @renamed_relation.attributes.should include(@renamed_relation[:schmid]) - @renamed_relation.should have(@relation1.attributes.size).attributes - pending "this should be more rigorous" + @renamed_relation.attributes.should include(@relation[:id].as(:schmid).bind(@renamed_relation)) + @renamed_relation.attributes.should_not include(@relation[:id].bind(@renamed_relation)) + @renamed_relation.attributes.should include(@relation[:name].bind(@renamed_relation)) + @renamed_relation.should have(@relation.attributes.size).attributes end end describe '#qualify' do it "distributes over the relation and renames" do - Rename.new(@relation1, @relation1[:id] => :schmid).qualify. \ - should == Rename.new(@relation1.qualify, @relation1[:id].qualify => :schmid) + Rename.new(@relation, @relation[:id] => :schmid).qualify. \ + should == Rename.new(@relation.qualify, @relation[:id].qualify => :schmid) end end describe '#to_sql' do it 'manufactures sql renaming the attribute' do - @renamed_relation.to_sql.should be_like(""" + Rename.new(@relation, @relation[:id] => :schmid).to_sql.should be_like(""" SELECT `users`.`id` AS 'schmid', `users`.`name` FROM `users` """) diff --git a/spec/active_relation/relations/table_spec.rb b/spec/active_relation/relations/table_spec.rb index 498d841040..13a9198447 100644 --- a/spec/active_relation/relations/table_spec.rb +++ b/spec/active_relation/relations/table_spec.rb @@ -27,7 +27,7 @@ module ActiveRelation describe 'when given an', Expression do before do - @expression = Expression.new(Attribute.new(@relation, :id), "COUNT") + @expression = @relation[:id].count end it "returns the Expression if the Expression is within the relation" do @@ -46,14 +46,15 @@ module ActiveRelation end describe '#prefix_for' do - it "returns the table name" do - @relation.prefix_for(Attribute.new(@relation, :id)).should == :users + it "returns the table name if the relation contains the attribute" do + @relation.prefix_for(@relation[:id]).should == :users + @relation.prefix_for(:does_not_exist).should be_nil end end describe '#aliased_prefix_for' do it "returns the table name" do - @relation.aliased_prefix_for(Attribute.new(@relation, :id)).should == :users + @relation.aliased_prefix_for(@relation[:id]).should == :users end end |