From 92db013ba3ee4d0a9d92281e614d05f064c22e15 Mon Sep 17 00:00:00 2001
From: Nick Kallen <nkallen@nick-kallens-computer-2.local>
Date: Sun, 24 Feb 2008 22:19:32 -0800
Subject: quoting issues

---
 spec/active_relation/integration/scratch_spec.rb   | 252 ---------------------
 .../active_relation/unit/predicates/binary_spec.rb |   3 +
 .../unit/predicates/equality_spec.rb               |   8 +-
 .../unit/primitives/attribute_spec.rb              |  41 ++--
 .../unit/primitives/expression_spec.rb             |   2 +-
 spec/active_relation/unit/relations/table_spec.rb  |   8 +-
 6 files changed, 35 insertions(+), 279 deletions(-)
 delete mode 100644 spec/active_relation/integration/scratch_spec.rb

(limited to 'spec/active_relation')

diff --git a/spec/active_relation/integration/scratch_spec.rb b/spec/active_relation/integration/scratch_spec.rb
deleted file mode 100644
index d6ec030518..0000000000
--- a/spec/active_relation/integration/scratch_spec.rb
+++ /dev/null
@@ -1,252 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
-
-describe 'ActiveRelation', 'A proposed refactoring to ActiveRecord, introducing both a SQL
-                            Builder and a Relational Algebra to mediate between
-                            ActiveRecord and the database. The goal of the refactoring is
-                            to remove code duplication concerning AR associations; remove
-                            complexity surrounding eager loading; comprehensively solve
-                            quoting issues; remove the with_scope merging logic; minimize
-                            the need for with_scope in general; simplify the
-                            implementation of plugins like HasFinder and ActsAsParanoid;
-                            introduce an identity map; and allow for query optimization.
-                            All this while remaining backwards-compatible with the
-                            existing ActiveRecord interface.
-                              The Relational Algebra makes these ambitious goals
-                            possible. There\'s no need to be scared by the math, it\'s
-                            actually quite simple. Relational Algebras have some nice
-                            advantages over flexible SQL builders like Sequel and and
-                            SqlAlchemy (a beautiful Python library). Principally, a
-                            relation is writable as well as readable. This obviates the
-                            :create with_scope, and perhaps also
-                            #set_belongs_to_association_for.
-                              With so much complexity removed from ActiveRecord, I
-                            propose a mild reconsideration of the architecture of Base,
-                            AssocationProxy, AssociationCollection, and so forth. These
-                            should all be understood as \'Repositories\': a factory that
-                            given a relation can manufacture objects, and given an object
-                            can manipulate a relation. This may sound trivial, but I
-                            think it has the potential to make the code smaller and
-                            more consistent.' do
-  before do
-    class User < ActiveRecord::Base; has_many :photos end
-    class Photo < ActiveRecord::Base; belongs_to :camera end
-    class Camera < ActiveRecord::Base; end
-  end
-  
-  before do
-    # Rather than being associated with a table, an ActiveRecord is now associated with
-    # a relation.
-    @users = User.relation
-    @photos = Photo.relation
-    @cameras = Camera.relation
-    # A first taste of a Relational Algebra: User.find(1)
-    @user = @users.select(@users[:id].equals(1))    
-    # == is overridden on attributes to return a predicate, not true or false
-  end
-
-  # In a Relational Algebra, the various ActiveRecord associations become a simple
-  # mapping from one relation to another. The Reflection object parameterizes the
-  # mapping.
-  def user_has_many_photos(user_relation)
-    primary_key = User.reflections[:photos].klass.primary_key.to_sym
-    foreign_key = User.reflections[:photos].primary_key_name.to_sym
-    
-    user_relation.outer_join(@photos).on(user_relation[primary_key].equals(@photos[foreign_key]))
-  end
-  
-  def photo_belongs_to_camera(photo_relation)
-    primary_key = Photo.reflections[:camera].klass.primary_key.to_sym
-    foreign_key = Photo.reflections[:camera].primary_key_name.to_sym
-
-    photo_relation.outer_join(@cameras).on(photo_relation[foreign_key].equals(@cameras[primary_key]))
-  end
-
-  describe 'Relational Algebra', 'a relational algebra allows the implementation of
-                                  associations like has_many to be specified once,
-                                  regardless of eager-joins, has_many :through, and so
-                                  forth' do    
-    it 'generates the query for User.has_many :photos' do
-      user_photos = user_has_many_photos(@user)
-      # the 'project' operator limits the columns that come back from the query.
-      # Note how all the operators are compositional: 'project' is applied to a query
-      # that previously had been joined and selected.
-      user_photos.project(*@photos.attributes).to_sql.should be_like("""
-        SELECT `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
-        FROM `users`
-          LEFT OUTER JOIN `photos`
-            ON `users`.`id` = `photos`.`user_id`
-        WHERE
-          `users`.`id` = 1
-      """)
-      # Also note the correctly quoted columns and tables. In this instance the
-      # MysqlAdapter from ActiveRecord is used to do the escaping.
-    end
-  
-    it 'generates the query for User.has_many :cameras, :through => :photos' do
-      # note, again, the compositionality of the operators:
-      user_cameras = photo_belongs_to_camera(user_has_many_photos(@user))
-      user_cameras.project(*@cameras.attributes).to_sql.should be_like("""
-        SELECT `cameras`.`id`
-        FROM `users`
-          LEFT OUTER JOIN `photos`
-            ON `users`.`id` = `photos`.`user_id`
-          LEFT OUTER JOIN `cameras`
-            ON `photos`.`camera_id` = `cameras`.`id`
-        WHERE
-          `users`.`id` = 1
-      """)
-    end
-    
-    it 'generates the query for an eager join for a collection using the same logic as
-        for an association on an individual row' do
-      users_cameras = photo_belongs_to_camera(user_has_many_photos(@users))
-      users_cameras.to_sql.should be_like("""
-        SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`, `cameras`.`id`
-        FROM `users`
-          LEFT OUTER JOIN `photos`
-            ON `users`.`id` = `photos`.`user_id`
-          LEFT OUTER JOIN `cameras`
-            ON `photos`.`camera_id` = `cameras`.`id`
-      """)
-    end
-    
-    it 'is trivial to disambiguate columns' do
-      users_cameras = photo_belongs_to_camera(user_has_many_photos(@users)).qualify
-      users_cameras.to_sql.should be_like("""
-        SELECT `users`.`id` AS 'users.id', `users`.`name` AS 'users.name', `photos`.`id` AS 'photos.id', `photos`.`user_id` AS 'photos.user_id', `photos`.`camera_id` AS 'photos.camera_id', `cameras`.`id` AS 'cameras.id'
-        FROM `users`
-          LEFT OUTER JOIN `photos`
-            ON `users`.`id` = `photos`.`user_id`
-          LEFT OUTER JOIN `cameras`
-            ON `photos`.`camera_id` = `cameras`.`id`
-      """)
-    end
-    
-    it 'allows arbitrary sql to be passed through' do
-      @users.outer_join(@photos).on("asdf").to_sql.should be_like("""
-        SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
-        FROM `users`
-          LEFT OUTER JOIN `photos`
-            ON asdf
-      """)
-      @users.select("asdf").to_sql.should be_like("""
-        SELECT `users`.`id`, `users`.`name`
-        FROM `users`
-        WHERE asdf
-      """)
-    end
-
-    describe 'with_scope' do
-      it 'obviates the need for with_scope merging logic since, e.g.,
-            `with_scope :conditions => ...` is just a #select operation on the relation' do
-      end
-    
-      it 'may eliminate the need for with_scope altogether since the associations no longer
-          need it: the relation underlying the association fully encapsulates the scope' do
-      end
-    end
-  end
-
-  describe 'Repository', 'ActiveRecord::Base, HasManyAssociation, and so forth are
-                          all repositories: given a relation, they manufacture objects' do
-    before do
-      class << ActiveRecord::Base; public :instantiate end
-    end
-  
-    it 'manufactures objects' do
-      User.instantiate(@users.first).attributes.should == {"name" => "hai", "id" => 1}
-    end
-    
-    it 'frees ActiveRecords from being tied to tables' do
-      pending # pending, but trivial to implement:
-      
-      class User < ActiveRecord::Base
-        # acts_as_paranoid without alias_method_chain:
-        set_relation @users.select(@users[:deleted_at] != nil)
-      end
-      
-      class Person < ActiveRecord::Base
-        set_relation @accounts.join(@profiles).on(@accounts[:id].equals(@profiles[:account_id]))
-      end
-      # I know this sounds crazy, but even writes are possible in the last example.
-      # calling #save on a person can write to two tables!
-    end
-    
-    describe 'the n+1 problem' do      
-      describe 'the eager join algorithm is vastly simpler' do
-        it 'loads three active records with only one query' do
-          # using 'rr' mocking framework: the real #select_all is called, but we assert
-          # that it only happens once:
-          mock.proxy(ActiveRecord::Base.connection).select_all.with_any_args.once
-          
-          users_cameras = photo_belongs_to_camera(user_has_many_photos(@users)).qualify
-          user = User.instantiate(users_cameras.first, [:photos => [:camera]])
-          user.photos.first.camera.attributes.should == {"id" => 1}
-        end
-
-        before do
-          class << ActiveRecord::Base
-            # An identity map makes this algorithm efficient.
-            def instantiate_with_cache(record)
-              cache.get(record) { instantiate_without_cache(record) }
-            end
-            alias_method_chain :instantiate, :cache
-
-            # for each row in the result set, which may contain data from n tables,
-            #  - instantiate that slice of the data corresponding to the current class
-            #  - recusively walk the dependency chain and repeat.
-            def instantiate_with_joins(data, joins = [])
-              record = unqualify(data)
-              returning instantiate_without_joins(record) do |object|
-                joins.each do |join|
-                  case join
-                  when Symbol
-                    object.send(association = join).instantiate(data)
-                  when Hash
-                    join.each do |association, nested_associations|
-                      object.send(association).instantiate(data, nested_associations)
-                    end
-                  end
-                end
-              end
-            end
-            alias_method_chain :instantiate, :joins
-            
-            private
-            # Sometimes, attributes are qualified to remove ambiguity. Here, bring back
-            # ambiguity by translating 'users.id' to 'id' so we can call #attributes=.
-            # This code should work correctly if the attributes are qualified or not.
-            def unqualify(qualified_attributes)
-              qualified_attributes_for_this_class = qualified_attributes. \
-                slice(*relation.attributes.collect(&:qualified_name))
-              qualified_attributes_for_this_class.alias do |qualified_name|
-                qualified_name.split('.')[1] || qualified_name # the latter means it must not really be qualified
-              end
-            end
-          end
-        end
-        
-        it "is possible to be smarter about eager loading. DataMapper is smart enough
-            to notice when you do users.each { |u| u.photos } and make this two queries
-            rather than n+1: the first invocation of #photos is lazy but it preloads
-            photos for all subsequent users. This is substantially easier with the
-            Algebra since we can do @user.join(@photos).on(...) and transform that to
-            @users.join(@photos).on(...), relying on the IdentityMap to eliminate
-            the n+1 problem." do
-          pending
-        end
-      end
-    end
-  end
-  
-  describe 'The Architecture', 'I propose to produce a new gem, ActiveRelation, which encaplulates
-                                the existing ActiveRecord Connection Adapter, the new SQL Builder,
-                                and the Relational Algebra. ActiveRecord, then, should no longer
-                                interact with the connection object directly.' do
-  end
-  
-  describe 'Miscellaneous Ideas' do
-    it 'may be easy to write a SQL parser that can take arbitrary SQL and produce a relation.
-        This has the advantage of permitting e.g., pagination with custom finder_sql'
-  end
-end
\ No newline at end of file
diff --git a/spec/active_relation/unit/predicates/binary_spec.rb b/spec/active_relation/unit/predicates/binary_spec.rb
index 44c1a1a7a0..1f6656b9d1 100644
--- a/spec/active_relation/unit/predicates/binary_spec.rb
+++ b/spec/active_relation/unit/predicates/binary_spec.rb
@@ -53,6 +53,9 @@ module ActiveRelation
           `users`.`id` <=> `photos`.`id`
         """)
       end
+      
+      it 'appropriately cooerces scalars' do
+      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
index fd30846c70..499b13383d 100644
--- a/spec/active_relation/unit/predicates/equality_spec.rb
+++ b/spec/active_relation/unit/predicates/equality_spec.rb
@@ -3,10 +3,10 @@ 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)
+      @relation1 = Table.new(:users)
+      @relation2 = Table.new(:photos)
+      @attribute1 = @relation1[:name]
+      @attribute2 = @relation2[:name]
     end
   
     describe '==' do 
diff --git a/spec/active_relation/unit/primitives/attribute_spec.rb b/spec/active_relation/unit/primitives/attribute_spec.rb
index e5a3792d85..8b4f52c432 100644
--- a/spec/active_relation/unit/primitives/attribute_spec.rb
+++ b/spec/active_relation/unit/primitives/attribute_spec.rb
@@ -4,23 +4,20 @@ module ActiveRelation
   describe Attribute do
     before do
       @relation = Table.new(:users)
+      @attribute = Attribute.new(@relation, :id)
     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)
+          @attribute.as(:alias).should == Attribute.new(@relation, @attribute.name, :alias => :alias, :ancestor => @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)
+          @attribute.bind(derived_relation).should == Attribute.new(derived_relation, @attribute.name, :ancestor => @attribute)
         end
         
         it "returns self if the substituting to the same relation" do
@@ -30,7 +27,7 @@ module ActiveRelation
     
       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)
+          @attribute.qualify.should == Attribute.new(@attribute.relation, @attribute.name, :alias => @attribute.qualified_name, :ancestor => @attribute)
         end
       end
       
@@ -41,9 +38,16 @@ module ActiveRelation
       end
     end
     
+    describe '#column' do
+      it "" do
+        pending
+      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'
+        stub(@relation).prefix_for(anything) { 'bruisers' }
+        Attribute.new(@relation, :id).qualified_name.should == 'bruisers.id'
       end
     end
     
@@ -54,25 +58,20 @@ module ActiveRelation
         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))
+          Attribute.new(@relation, :name, :ancestor => Attribute.new(@relation, :name)).should =~ Attribute.new(@relation, :name)
+          Attribute.new(@relation, :name).should =~ Attribute.new(@relation, :name, :ancestor => 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 "" do
+        pending "this test is not sufficiently resilient"
+      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
+      it "manufactures sql with an alias" do
+        stub(@relation).prefix_for(anything) { 'bruisers' }
+        Attribute.new(@relation, :name, :alias => :alias).to_sql.should be_like("`bruisers`.`name`")
       end
     end
   
diff --git a/spec/active_relation/unit/primitives/expression_spec.rb b/spec/active_relation/unit/primitives/expression_spec.rb
index 5506f52b86..dda35157b0 100644
--- a/spec/active_relation/unit/primitives/expression_spec.rb
+++ b/spec/active_relation/unit/primitives/expression_spec.rb
@@ -31,7 +31,7 @@ module ActiveRelation
       
       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)
+          @expression.to_attribute.should == Attribute.new(@expression.relation, @expression.alias, :ancestor => @expression)
         end
       end
     end
diff --git a/spec/active_relation/unit/relations/table_spec.rb b/spec/active_relation/unit/relations/table_spec.rb
index 3b02f80701..f8d4431aa7 100644
--- a/spec/active_relation/unit/relations/table_spec.rb
+++ b/spec/active_relation/unit/relations/table_spec.rb
@@ -45,9 +45,15 @@ module ActiveRelation
       end
     end
     
+    describe '#column_for' do
+      it "" do
+        pending
+      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(@relation[:id]).should == 'users'
         @relation.prefix_for(:does_not_exist).should be_nil
       end
     end
-- 
cgit v1.2.3