aboutsummaryrefslogtreecommitdiffstats
path: root/spec/active_relation/unit
diff options
context:
space:
mode:
Diffstat (limited to 'spec/active_relation/unit')
-rw-r--r--spec/active_relation/unit/predicates/binary_spec.rb58
-rw-r--r--spec/active_relation/unit/predicates/equality_spec.rb27
-rw-r--r--spec/active_relation/unit/predicates/relation_inclusion_spec.rb27
-rw-r--r--spec/active_relation/unit/primitives/attribute_spec.rb157
-rw-r--r--spec/active_relation/unit/primitives/expression_spec.rb45
-rw-r--r--spec/active_relation/unit/relations/alias_spec.rb16
-rw-r--r--spec/active_relation/unit/relations/compound_spec.rb21
-rw-r--r--spec/active_relation/unit/relations/deletion_spec.rb26
-rw-r--r--spec/active_relation/unit/relations/insertion_spec.rb28
-rw-r--r--spec/active_relation/unit/relations/join_spec.rb123
-rw-r--r--spec/active_relation/unit/relations/order_spec.rb35
-rw-r--r--spec/active_relation/unit/relations/projection_spec.rb62
-rw-r--r--spec/active_relation/unit/relations/range_spec.rb35
-rw-r--r--spec/active_relation/unit/relations/relation_spec.rb124
-rw-r--r--spec/active_relation/unit/relations/rename_spec.rb64
-rw-r--r--spec/active_relation/unit/relations/selection_spec.rb50
-rw-r--r--spec/active_relation/unit/relations/table_spec.rb70
17 files changed, 968 insertions, 0 deletions
diff --git a/spec/active_relation/unit/predicates/binary_spec.rb b/spec/active_relation/unit/predicates/binary_spec.rb
new file mode 100644
index 0000000000..44c1a1a7a0
--- /dev/null
+++ b/spec/active_relation/unit/predicates/binary_spec.rb
@@ -0,0 +1,58 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Binary do
+ before do
+ @relation1 = Table.new(:users)
+ @relation2 = Table.new(:photos)
+ @attribute1 = @relation1[:id]
+ @attribute2 = @relation2[:id]
+ class ConcreteBinary < Binary
+ def predicate_sql
+ "<=>"
+ end
+ end
+ end
+
+ describe '==' do
+ it "obtains if attribute1 and attribute2 are identical" do
+ Binary.new(@attribute1, @attribute2).should == Binary.new(@attribute1, @attribute2)
+ Binary.new(@attribute1, @attribute2).should_not == Binary.new(@attribute1, @attribute1)
+ end
+
+ it "obtains if the concrete type of the Predicates::Binarys are identical" do
+ Binary.new(@attribute1, @attribute2).should == Binary.new(@attribute1, @attribute2)
+ Binary.new(@attribute1, @attribute2).should_not == ConcreteBinary.new(@attribute1, @attribute2)
+ end
+ end
+
+ describe '#qualify' do
+ it "descends" do
+ ConcreteBinary.new(@attribute1, @attribute2).qualify \
+ .should == ConcreteBinary.new(@attribute1, @attribute2).descend(&:qualify)
+ end
+ end
+
+ describe '#descend' do
+ it "distributes over the predicates and attributes" do
+ ConcreteBinary.new(@attribute1, @attribute2).descend(&:qualify). \
+ should == ConcreteBinary.new(@attribute1.qualify, @attribute2.qualify)
+ end
+ end
+
+ describe '#bind' do
+ it "distributes over the predicates and attributes" do
+ ConcreteBinary.new(@attribute1, @attribute2).bind(@relation2). \
+ should == ConcreteBinary.new(@attribute1.bind(@relation2), @attribute2.bind(@relation2))
+ end
+ end
+
+ describe '#to_sql' do
+ it 'manufactures sql with a binary operation' do
+ ConcreteBinary.new(@attribute1, @attribute2).to_sql.should be_like("""
+ `users`.`id` <=> `photos`.`id`
+ """)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/predicates/equality_spec.rb b/spec/active_relation/unit/predicates/equality_spec.rb
new file mode 100644
index 0000000000..fd30846c70
--- /dev/null
+++ b/spec/active_relation/unit/predicates/equality_spec.rb
@@ -0,0 +1,27 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Equality do
+ before do
+ @relation1 = Table.new(:foo)
+ @relation2 = Table.new(:bar)
+ @attribute1 = Attribute.new(@relation1, :name)
+ @attribute2 = Attribute.new(@relation2, :name)
+ end
+
+ describe '==' do
+ it "obtains if attribute1 and attribute2 are identical" do
+ Equality.new(@attribute1, @attribute2).should == Equality.new(@attribute1, @attribute2)
+ Equality.new(@attribute1, @attribute2).should_not == Equality.new(@attribute1, @attribute1)
+ end
+
+ it "obtains if the concrete type of the predicates are identical" do
+ Equality.new(@attribute1, @attribute2).should_not == Binary.new(@attribute1, @attribute2)
+ end
+
+ it "is commutative on the attributes" do
+ Equality.new(@attribute1, @attribute2).should == Equality.new(@attribute2, @attribute1)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/predicates/relation_inclusion_spec.rb b/spec/active_relation/unit/predicates/relation_inclusion_spec.rb
new file mode 100644
index 0000000000..799be23085
--- /dev/null
+++ b/spec/active_relation/unit/predicates/relation_inclusion_spec.rb
@@ -0,0 +1,27 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe RelationInclusion do
+ before do
+ foo = Table.new(:foo)
+ @relation1 = foo.project(foo[:id])
+ @relation2 = Table.new(:bar)
+ @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
+
+ describe RelationInclusion, '#to_sql' do
+ it "manufactures subselect sql" do
+ RelationInclusion.new(@attribute, @relation1).to_sql.should be_like("""
+ `foo`.`id` IN (SELECT `foo`.`id` FROM `foo`)
+ """)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/primitives/attribute_spec.rb b/spec/active_relation/unit/primitives/attribute_spec.rb
new file mode 100644
index 0000000000..e5a3792d85
--- /dev/null
+++ b/spec/active_relation/unit/primitives/attribute_spec.rb
@@ -0,0 +1,157 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Attribute do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe Attribute::Transformations do
+ before do
+ @attribute = Attribute.new(@relation, :id)
+ end
+
+ describe '#as' do
+ it "manufactures an aliased attributed" do
+ @attribute.as(:alias).should == Attribute.new(@relation, @attribute.name, :alias, @attribute)
+ end
+ end
+
+ describe '#bind' do
+ it "manufactures an attribute with the relation bound and self as an ancestor" do
+ derived_relation = @relation.select(@relation[:id].equals(1))
+ @attribute.bind(derived_relation).should == Attribute.new(derived_relation, @attribute.name, nil, @attribute)
+ end
+
+ it "returns self if the substituting to the same relation" do
+ @attribute.bind(@relation).should == @attribute
+ end
+ end
+
+ describe '#qualify' do
+ it "manufactures an attribute aliased with that attribute's qualified name" do
+ @attribute.qualify.should == Attribute.new(@attribute.relation, @attribute.name, @attribute.qualified_name, @attribute)
+ end
+ end
+
+ describe '#to_attribute' do
+ it "returns self" do
+ @attribute.to_attribute.should == @attribute
+ end
+ end
+ end
+
+ describe '#qualified_name' do
+ it "manufactures an attribute name prefixed with the relation's name" do
+ Attribute.new(@relation, :id).qualified_name.should == 'users.id'
+ end
+ end
+
+ describe Attribute::Congruence do
+ describe '=~' do
+ it "obtains if the attributes are identical" do
+ Attribute.new(@relation, :name).should =~ Attribute.new(@relation, :name)
+ end
+
+ it "obtains if the attributes have an overlapping history" do
+ Attribute.new(@relation, :name, nil, Attribute.new(@relation, :name)).should =~ Attribute.new(@relation, :name)
+ Attribute.new(@relation, :name).should =~ Attribute.new(@relation, :name, nil, Attribute.new(@relation, :name))
+ end
+ end
+ end
+
+ 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("`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("`bruisers`.`name` AS 'alias'")
+ end
+ end
+ end
+
+ describe Attribute::Predications do
+ before do
+ @attribute = Attribute.new(@relation, :name)
+ end
+
+ describe '#equals' do
+ it "manufactures an equality predicate" do
+ @attribute.equals('name').should == Equality.new(@attribute, 'name')
+ end
+ end
+
+ describe '#less_than' do
+ it "manufactures a less-than predicate" do
+ @attribute.less_than(10).should == LessThan.new(@attribute, 10)
+ end
+ end
+
+ describe '#less_than_or_equal_to' do
+ it "manufactures a less-than or equal-to predicate" do
+ @attribute.less_than_or_equal_to(10).should == LessThanOrEqualTo.new(@attribute, 10)
+ end
+ end
+
+ describe '#greater_than' do
+ it "manufactures a greater-than predicate" do
+ @attribute.greater_than(10).should == GreaterThan.new(@attribute, 10)
+ end
+ end
+
+ describe '#greater_than_or_equal_to' do
+ it "manufactures a greater-than or equal-to predicate" do
+ @attribute.greater_than_or_equal_to(10).should == GreaterThanOrEqualTo.new(@attribute, 10)
+ end
+ end
+
+ describe '#matches' do
+ it "manufactures a match predicate" do
+ @attribute.matches(/.*/).should == Match.new(@attribute, /.*/)
+ end
+ end
+ end
+
+ describe Attribute::Expressions do
+ before do
+ @attribute = Attribute.new(@relation, :name)
+ end
+
+ describe '#count' do
+ it "manufactures a count Expression" do
+ @attribute.count.should == Expression.new(@attribute, "COUNT")
+ end
+ end
+
+ describe '#sum' do
+ it "manufactures a sum Expression" do
+ @attribute.sum.should == Expression.new(@attribute, "SUM")
+ end
+ end
+
+ describe '#maximum' do
+ it "manufactures a maximum Expression" do
+ @attribute.maximum.should == Expression.new(@attribute, "MAX")
+ end
+ end
+
+ describe '#minimum' do
+ it "manufactures a minimum Expression" do
+ @attribute.minimum.should == Expression.new(@attribute, "MIN")
+ end
+ end
+
+ describe '#average' do
+ it "manufactures an average Expression" do
+ @attribute.average.should == Expression.new(@attribute, "AVG")
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/primitives/expression_spec.rb b/spec/active_relation/unit/primitives/expression_spec.rb
new file mode 100644
index 0000000000..5506f52b86
--- /dev/null
+++ b/spec/active_relation/unit/primitives/expression_spec.rb
@@ -0,0 +1,45 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Expression do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+
+ describe Expression::Transformations do
+ before do
+ @expression = Expression.new(@attribute, "COUNT")
+ end
+
+ describe '#bind' do
+ it "manufactures an attribute with a rebound relation and self as the ancestor" do
+ derived_relation = @relation.select(@relation[:id] == 1)
+ @expression.bind(derived_relation).should == Expression.new(@attribute.bind(derived_relation), "COUNT", nil, @expression)
+ end
+
+ it "returns self if the substituting to the same relation" do
+ @expression.bind(@relation).should == @expression
+ end
+ end
+
+ describe '#as' do
+ it "manufactures an aliased expression" do
+ @expression.as(:alias).should == Expression.new(@attribute, "COUNT", :alias, @expression)
+ end
+ end
+
+ describe '#to_attribute' do
+ it "manufactures an attribute with the expression as an ancestor" do
+ @expression.to_attribute.should == Attribute.new(@expression.relation, @expression.alias, nil, @expression)
+ end
+ end
+ end
+
+ describe '#to_sql' do
+ it "manufactures sql with the expression and alias" do
+ Expression.new(@attribute, "COUNT", :alias).to_sql.should == "COUNT(`users`.`id`) AS `alias`"
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/alias_spec.rb b/spec/active_relation/unit/relations/alias_spec.rb
new file mode 100644
index 0000000000..4a2144db82
--- /dev/null
+++ b/spec/active_relation/unit/relations/alias_spec.rb
@@ -0,0 +1,16 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Alias do
+ before do
+ @relation = Table.new(:users)
+ @alias_relation = Alias.new(@relation, :foo)
+ end
+
+ describe '#alias' do
+ it "returns the alias" do
+ @alias_relation.alias.should == :foo
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/compound_spec.rb b/spec/active_relation/unit/relations/compound_spec.rb
new file mode 100644
index 0000000000..03de27fb05
--- /dev/null
+++ b/spec/active_relation/unit/relations/compound_spec.rb
@@ -0,0 +1,21 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Compound do
+ before do
+ class ConcreteCompound < Compound
+ def initialize(relation)
+ @relation = relation
+ end
+ end
+ @relation = Table.new(:users)
+ @compound_relation = ConcreteCompound.new(@relation)
+ end
+
+ describe '#attributes' do
+ it 'manufactures attributes associated with the compound relation' do
+ @compound_relation.attributes.should == @relation.attributes.collect { |a| a.bind(@compound_relation) }
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/deletion_spec.rb b/spec/active_relation/unit/relations/deletion_spec.rb
new file mode 100644
index 0000000000..1a536e7aed
--- /dev/null
+++ b/spec/active_relation/unit/relations/deletion_spec.rb
@@ -0,0 +1,26 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Deletion do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '#to_sql' do
+ it 'manufactures sql deleting a table relation' do
+ Deletion.new(@relation).to_sql.should be_like("""
+ DELETE
+ FROM `users`
+ """)
+ end
+
+ it 'manufactures sql deleting a selection relation' do
+ Deletion.new(@relation.select(@relation[:id].equals(1))).to_sql.should be_like("""
+ DELETE
+ FROM `users`
+ WHERE `users`.`id` = 1
+ """)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/insertion_spec.rb b/spec/active_relation/unit/relations/insertion_spec.rb
new file mode 100644
index 0000000000..a0427bc74f
--- /dev/null
+++ b/spec/active_relation/unit/relations/insertion_spec.rb
@@ -0,0 +1,28 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Insertion do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '#to_sql' do
+ it 'manufactures sql inserting the data for one item' do
+ Insertion.new(@relation, @relation[:name] => "nick").to_sql.should be_like("""
+ INSERT
+ INTO `users`
+ (`users`.`name`) VALUES ('nick')
+ """)
+ end
+
+ it 'manufactures sql inserting the data for multiple items' do
+ nested_insertion = Insertion.new(@relation, @relation[:name] => "cobra")
+ Insertion.new(nested_insertion, nested_insertion[:name] => "commander").to_sql.should be_like("""
+ INSERT
+ INTO `users`
+ (`users`.`name`) VALUES ('cobra'), ('commander')
+ """)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/join_spec.rb b/spec/active_relation/unit/relations/join_spec.rb
new file mode 100644
index 0000000000..bc55a72e55
--- /dev/null
+++ b/spec/active_relation/unit/relations/join_spec.rb
@@ -0,0 +1,123 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Join do
+ before do
+ @relation1 = Table.new(:users)
+ @relation2 = Table.new(:photos)
+ @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, @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
+ Join.new("INNER JOIN", @relation1, @relation2, @predicate).should == Join.new("INNER JOIN", @relation2, @relation1, @predicate)
+ end
+ end
+
+ describe '#descend' do
+ it 'distributes over the relations and predicates' do
+ Join.new("INNER JOIN", @relation1, @relation2, @predicate).qualify. \
+ should == Join.new("INNER JOIN", @relation1.qualify, @relation2.qualify, @predicate.qualify)
+ end
+ end
+
+ describe '#prefix_for' do
+ it 'needs a test' do
+ pending
+ end
+ end
+
+ 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`.`user_id`
+ """)
+ end
+
+ it 'manufactures sql joining the two tables, merging any selects' do
+ Join.new("INNER JOIN", @relation1.select(@relation1[:id].equals(1)),
+ @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`.`user_id`
+ WHERE `users`.`id` = 1
+ AND `photos`.`id` = 2
+ """)
+ end
+ end
+ 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", @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 `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", @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 `users`.`id` = `photo_count`.`user_id`
+ """)
+ end
+ end
+
+ it "keeps selects on the aggregation within the derived table" do
+ 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 `users`.`id` = `photo_count`.`user_id`
+ """)
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/order_spec.rb b/spec/active_relation/unit/relations/order_spec.rb
new file mode 100644
index 0000000000..15f08d70ee
--- /dev/null
+++ b/spec/active_relation/unit/relations/order_spec.rb
@@ -0,0 +1,35 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Order do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+
+ describe '#qualify' do
+ it "descends" do
+ Order.new(@relation, @attribute).qualify. \
+ should == Order.new(@relation, @attribute).descend(&:qualify)
+ end
+ end
+
+ describe '#descend' do
+ it "distributes over the relation and attributes" do
+ Order.new(@relation, @attribute).descend(&:qualify). \
+ should == Order.new(@relation.descend(&:qualify), @attribute.qualify)
+ end
+ end
+
+ describe '#to_sql' do
+ it "manufactures sql with an order clause" do
+ Order.new(@relation, @attribute).to_sql.should be_like("""
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ ORDER BY `users`.`id`
+ """)
+ end
+ end
+ end
+end
+ \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/projection_spec.rb b/spec/active_relation/unit/relations/projection_spec.rb
new file mode 100644
index 0000000000..75a4672642
--- /dev/null
+++ b/spec/active_relation/unit/relations/projection_spec.rb
@@ -0,0 +1,62 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Projection do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+
+ describe '#attributes' do
+ before do
+ @projection = Projection.new(@relation, @attribute)
+ end
+
+ it "manufactures attributes associated with the projection relation" do
+ @projection.attributes.should == [@attribute].collect { |a| a.bind(@projection) }
+ end
+ end
+
+ describe '==' do
+ before do
+ @another_relation = Table.new(:photos)
+ @another_attribute = @relation[:name]
+ end
+
+ it "obtains if the relations and attributes are identical" do
+ Projection.new(@relation, @attribute).should == Projection.new(@relation, @attribute)
+ Projection.new(@relation, @attribute).should_not == Projection.new(@another_relation, @attribute)
+ Projection.new(@relation, @attribute).should_not == Projection.new(@relation, @another_attribute)
+ end
+ end
+
+ describe '#qualify' do
+ it "distributes over the relation and attributes" do
+ Projection.new(@relation, @attribute).qualify. \
+ should == Projection.new(@relation, @attribute).descend(&:qualify)
+ end
+ end
+
+ describe '#descend' do
+ it "distributes over the relation and attributes" do
+ Projection.new(@relation, @attribute).descend(&:qualify). \
+ should == Projection.new(@relation.descend(&:qualify), @attribute.qualify)
+ end
+ end
+
+ describe '#to_sql' do
+ it "manufactures sql with a limited select clause" do
+ Projection.new(@relation, @attribute).to_sql.should be_like("""
+ SELECT `users`.`id`
+ FROM `users`
+ """)
+ end
+
+ it "manufactures sql with scalar selects" do
+ Projection.new(@relation, Projection.new(@relation, @relation[:name])).to_sql.should be_like("""
+ SELECT (SELECT `users`.`name` FROM `users`) FROM `users`
+ """)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/range_spec.rb b/spec/active_relation/unit/relations/range_spec.rb
new file mode 100644
index 0000000000..501a8d3641
--- /dev/null
+++ b/spec/active_relation/unit/relations/range_spec.rb
@@ -0,0 +1,35 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Range do
+ before do
+ @relation = Table.new(:users)
+ @range = 4..9
+ end
+
+ describe '#qualify' do
+ it "descends" do
+ Range.new(@relation, @range).qualify.should == Range.new(@relation, @range).descend(&:qualify)
+ end
+ end
+
+ describe '#descend' do
+ it "distributes over the relation" do
+ Range.new(@relation, @range).descend(&:qualify).should == Range.new(@relation.descend(&:qualify), @range)
+ end
+ end
+
+ describe '#to_sql' do
+ it "manufactures sql with limit and offset" do
+ range_size = @range.last - @range.first + 1
+ range_start = @range.first
+ Range.new(@relation, @range).to_s.should be_like("""
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ LIMIT #{range_size}
+ OFFSET #{range_start}
+ """)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/relation_spec.rb b/spec/active_relation/unit/relations/relation_spec.rb
new file mode 100644
index 0000000000..a80df928c7
--- /dev/null
+++ b/spec/active_relation/unit/relations/relation_spec.rb
@@ -0,0 +1,124 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Relation do
+ before do
+ @relation = Table.new(:users)
+ @attribute1 = @relation[:id]
+ @attribute2 = @relation[:name]
+ end
+
+ describe '[]' do
+ describe 'when given a', Range do
+ it "manufactures a range relation when given a range" do
+ @relation[1..2].should == Range.new(@relation, 1..2)
+ end
+ end
+
+ describe 'when given an', Attribute do
+ it "return the attribute congruent to the provided attribute" do
+ @relation[@attribute1].should == @attribute1
+ end
+ end
+
+ describe 'when given a', Symbol, String do
+ it "returns the attribute with the same name, if it exists" do
+ @relation[:id].should == @attribute1
+ @relation['id'].should == @attribute1
+ @relation[:does_not_exist].should be_nil
+ end
+ end
+ end
+
+ describe '#include?' do
+ it "manufactures an inclusion predicate" do
+ @relation.include?(@attribute1).should be_kind_of(RelationInclusion)
+ end
+ end
+
+ describe '#Expression?' do
+ it "returns false" do
+ @relation.should_not be_aggregation
+ end
+ end
+
+ describe 'read operations' do
+ describe 'joins' do
+ before do
+ @predicate = @relation[:id].equals(@relation[:id])
+ end
+
+ describe '#join' do
+ it "manufactures an inner join operation between those two relations" do
+ @relation.join(@relation).on(@predicate).should == Join.new("INNER JOIN", @relation, @relation, @predicate)
+ end
+ end
+
+ describe '#outer_join' do
+ it "manufactures a left outer join operation between those two relations" do
+ @relation.outer_join(@relation).on(@predicate).should == Join.new("LEFT OUTER JOIN", @relation, @relation, @predicate)
+ end
+ end
+ end
+
+ describe '#project' do
+ it "manufactures a projection relation" do
+ @relation.project(@attribute1, @attribute2).should == Projection.new(@relation, @attribute1, @attribute2)
+ end
+ end
+
+ describe '#as' do
+ it "manufactures an alias relation" do
+ @relation.as(:thucydides).should == Alias.new(@relation, :thucydides)
+ end
+ end
+
+ describe '#rename' do
+ it "manufactures a rename relation" do
+ @relation.rename(@attribute1, :users).should == Rename.new(@relation, @attribute1 => :users)
+ end
+ end
+
+ describe '#select' do
+ before do
+ @predicate = Equality.new(@attribute1, @attribute2)
+ end
+
+ it "manufactures a selection relation" do
+ @relation.select(@predicate).should == Selection.new(@relation, @predicate)
+ end
+
+ it "accepts arbitrary strings" do
+ @relation.select("arbitrary").should == Selection.new(@relation, "arbitrary")
+ end
+ end
+
+ describe '#order' do
+ it "manufactures an order relation" do
+ @relation.order(@attribute1, @attribute2).should == Order.new(@relation, @attribute1, @attribute2)
+ end
+ end
+
+ describe '#aggregate' do
+ it 'manufactures a group relation' do
+ @relation.aggregate(@expression1, @expression2).group(@attribute1, @attribute2). \
+ should == Aggregation.new(@relation, :expressions => [@expresion, @expression2], :groupings => [@attribute1, @attribute2])
+ end
+ end
+ end
+
+ describe 'write operations' do
+ describe '#delete' do
+ it 'manufactures a deletion relation' do
+ @relation.delete.should be_kind_of(Deletion)
+ end
+ end
+
+ describe '#insert' do
+ it 'manufactures an insertion relation' do
+ @relation.insert(record = {:id => 1}).should be_kind_of(Insertion)
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/rename_spec.rb b/spec/active_relation/unit/relations/rename_spec.rb
new file mode 100644
index 0000000000..33a89e6a49
--- /dev/null
+++ b/spec/active_relation/unit/relations/rename_spec.rb
@@ -0,0 +1,64 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Rename do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '#initialize' do
+ it "manufactures nested rename relations if multiple renames are provided" do
+ 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(@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(@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 "descends" do
+ Rename.new(@relation, @relation[:id] => :schmid).qualify. \
+ should == Rename.new(@relation, @relation[:id] => :schmid).descend(&:qualify)
+ end
+ end
+
+ describe '#descend' do
+ it "distributes over the relation and renames" do
+ Rename.new(@relation, @relation[:id] => :schmid).descend(&:qualify). \
+ should == Rename.new(@relation.descend(&:qualify), @relation[:id].qualify => :schmid)
+ end
+ end
+
+ describe '#to_sql' do
+ it 'manufactures sql renaming the attribute' do
+ Rename.new(@relation, @relation[:id] => :schmid).to_sql.should be_like("""
+ SELECT `users`.`id` AS 'schmid', `users`.`name`
+ FROM `users`
+ """)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/selection_spec.rb b/spec/active_relation/unit/relations/selection_spec.rb
new file mode 100644
index 0000000000..afe002186e
--- /dev/null
+++ b/spec/active_relation/unit/relations/selection_spec.rb
@@ -0,0 +1,50 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Selection do
+ before do
+ @relation = Table.new(:users)
+ @predicate = Equality.new(@relation[:id], 1)
+ end
+
+ describe '#initialize' do
+ it "manufactures nested selection relations if multiple predicates are provided" do
+ @predicate2 = LessThan.new(@relation[:age], 2)
+ Selection.new(@relation, @predicate, @predicate2). \
+ should == Selection.new(Selection.new(@relation, @predicate2), @predicate)
+ end
+ end
+
+ describe '#qualify' do
+ it "descends" do
+ Selection.new(@relation, @predicate).qualify. \
+ should == Selection.new(@relation, @predicate).descend(&:qualify)
+ end
+ end
+
+ describe '#descend' do
+ it "distributes over the relation and predicates" do
+ Selection.new(@relation, @predicate).descend(&:qualify). \
+ should == Selection.new(@relation.descend(&:qualify), @predicate.descend(&:qualify))
+ end
+ end
+
+ describe '#to_sql' do
+ it "manufactures sql with where clause conditions" do
+ Selection.new(@relation, @predicate).to_sql.should be_like("""
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ WHERE `users`.`id` = 1
+ """)
+ end
+
+ it "allows arbitrary sql" do
+ Selection.new(@relation, "asdf").to_sql.should be_like("""
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ WHERE asdf
+ """)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/active_relation/unit/relations/table_spec.rb b/spec/active_relation/unit/relations/table_spec.rb
new file mode 100644
index 0000000000..3b02f80701
--- /dev/null
+++ b/spec/active_relation/unit/relations/table_spec.rb
@@ -0,0 +1,70 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+
+module ActiveRelation
+ describe Table do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '[]' do
+ describe 'when given a', Symbol do
+ it "manufactures an attribute if the symbol names an attribute within the relation" do
+ @relation[:id].should == Attribute.new(@relation, :id)
+ @relation[:does_not_exist].should be_nil
+ end
+ end
+
+ describe 'when given an', Attribute do
+ it "returns the attribute if the attribute is within the relation" do
+ @relation[@relation[:id]].should == @relation[:id]
+ end
+
+ it "returns nil if the attribtue is not within the relation" do
+ another_relation = Table.new(:photos)
+ @relation[another_relation[:id]].should be_nil
+ end
+ end
+
+ describe 'when given an', Expression do
+ before do
+ @expression = @relation[:id].count
+ end
+
+ it "returns the Expression if the Expression is within the relation" do
+ @relation[@expression].should be_nil
+ end
+ end
+ end
+
+ describe '#to_sql' do
+ it "manufactures a simple select query" do
+ @relation.to_sql.should be_like("""
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ """)
+ end
+ end
+
+ describe '#prefix_for' do
+ 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 '#attributes' do
+ it 'manufactures attributes corresponding to columns in the table' do
+ @relation.attributes.should == [
+ Attribute.new(@relation, :id),
+ Attribute.new(@relation, :name)
+ ]
+ end
+ end
+
+ describe '#qualify' do
+ it 'manufactures a rename relation with all attribute names qualified' do
+ @relation.qualify.should == Rename.new(@relation, @relation[:name] => 'users.name', @relation[:id] => 'users.id')
+ end
+ end
+ end
+end \ No newline at end of file