From c49c76e2a62a5aee991f01d57a4d5c1d1e214aea Mon Sep 17 00:00:00 2001
From: danielgrippi <danielgrippi@gmail.com>
Date: Wed, 19 Oct 2011 15:47:11 -0700
Subject: [PATCH] DG MS; some refactoring

---
 app/models/post.rb                 |  9 +--
 app/models/status_message.rb       | 10 ----
 lib/diaspora/shareable.rb          | 22 +++++++
 lib/diaspora/user/querying.rb      | 35 ++++++++---
 spec/models/post_spec.rb           | 94 +++++++++++++++++++++++++-----
 spec/models/status_message_spec.rb | 32 +---------
 spec/models/user/querying_spec.rb  | 12 +++-
 7 files changed, 138 insertions(+), 76 deletions(-)

diff --git a/app/models/post.rb b/app/models/post.rb
index 9dd5c8a869..559564e695 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -24,15 +24,10 @@ class Post < ActiveRecord::Base
   scope :includes_for_a_stream,  includes(:o_embed_cache, {:author => :profile}, :mentions => {:person => :profile}) #note should include root and photos, but i think those are both on status_message
 
   def self.for_a_stream(max_time, order)
-    by_max_time(max_time, order).
-    includes_for_a_stream.
-    where(:type => Stream::Base::TYPES_OF_POST_IN_STREAM).
-    limit(15)
+    self.for_visible_shareable_sql(max_time, order).
+    includes_for_a_stream
   end
 
-  def self.by_max_time(max_time, order='created_at')
-    where("posts.#{order} < ?", max_time).order("posts.#{order} desc")
-  end
   #############
 
   def self.diaspora_initialize params
diff --git a/app/models/status_message.rb b/app/models/status_message.rb
index 9601479aeb..2860afab9f 100644
--- a/app/models/status_message.rb
+++ b/app/models/status_message.rb
@@ -35,16 +35,6 @@ class StatusMessage < Post
   #scopes
   scope :where_person_is_mentioned, lambda{|person| joins(:mentions).where(:mentions => {:person_id => person.id})}
 
-  def self.owned_or_visible_by_user(user)
-    joins("LEFT OUTER JOIN share_visibilities ON share_visibilities.shareable_id = posts.id AND share_visibilities.shareable_type = 'Post'").
-    joins("LEFT OUTER JOIN contacts ON contacts.id = share_visibilities.contact_id").
-    where(Contact.arel_table[:user_id].eq(user.id).or(
-      StatusMessage.arel_table[:public].eq(true).or(
-        StatusMessage.arel_table[:author_id].eq(user.person.id)
-      )
-    )).select('DISTINCT posts.*')
-  end
-
   def self.tag_stream(user, tag_array, max_time, order)
     owned_or_visible_by_user(user).
       joins(:tags).where(:tags => {:name => tag_array}).
diff --git a/lib/diaspora/shareable.rb b/lib/diaspora/shareable.rb
index e91658b20a..1ba5746ef8 100644
--- a/lib/diaspora/shareable.rb
+++ b/lib/diaspora/shareable.rb
@@ -25,6 +25,28 @@ module Diaspora
         #scopes
         scope :all_public, where(:public => true, :pending => false)
 
+        def self.owned_or_visible_by_user(user)
+          self.joins("LEFT OUTER JOIN share_visibilities ON share_visibilities.shareable_id = posts.id AND share_visibilities.shareable_type = 'Post'").
+               joins("LEFT OUTER JOIN contacts ON contacts.id = share_visibilities.contact_id").
+               where(Contact.arel_table[:user_id].eq(user.id).or(
+                 self.arel_table[:public].eq(true).or(
+                   self.arel_table[:author_id].eq(user.person.id)
+                   )
+                 )
+               ).
+               select("DISTINCT #{self.table_name}.*")
+        end
+
+        def self.for_visible_shareable_sql(max_time, order, limit = 15, types = Stream::Base::TYPES_OF_POST_IN_STREAM)
+          by_max_time(max_time, order).
+          where(:type => types).
+          limit(limit)
+        end
+
+        def self.by_max_time(max_time, order='created_at')
+          where("#{self.table_name}.#{order} < ?", max_time).order("#{self.table_name}.#{order} desc")
+        end
+
         xml_attr :diaspora_handle
         xml_attr :public
         xml_attr :created_at
diff --git a/lib/diaspora/user/querying.rb b/lib/diaspora/user/querying.rb
index 18b23cc871..d6c3b7274c 100644
--- a/lib/diaspora/user/querying.rb
+++ b/lib/diaspora/user/querying.rb
@@ -48,26 +48,43 @@ module Diaspora
       def visible_shareable_sql(klass, opts={})
         table = klass.table_name
         opts = prep_opts(klass, opts)
+        opts[:klass] = klass
+
+        shareable_from_others = construct_shareable_from_others_query(opts)
+        shareable_from_self = construct_shareable_from_self_query(opts)
+
+        "(#{shareable_from_others.to_sql} LIMIT #{opts[:limit]}) UNION ALL (#{shareable_from_self.to_sql} LIMIT #{opts[:limit]}) ORDER BY #{opts[:order]} LIMIT #{opts[:limit]}"
+      end
+
+      def ugly_select_clause(query, opts)
+        klass = opts[:klass]
         select_clause ='DISTINCT %s.id, %s.updated_at AS updated_at, %s.created_at AS created_at' % [klass.table_name, klass.table_name, klass.table_name]
+        query.select(select_clause).order(opts[:order_with_table]).where(klass.arel_table[opts[:order_field]].lt(opts[:max_time]))
+      end
 
+      def construct_shareable_from_others_query(opts)
         conditions = {:pending => false, :share_visibilities => {:hidden => opts[:hidden]}, :contacts => {:user_id => self.id} }
         conditions[:type] = opts[:type] if opts.has_key?(:type)
-        shareable_from_others = klass.joins(:contacts).where(conditions)
+        query = opts[:klass].joins(:contacts).where(conditions)
 
+        if opts[:by_members_of]
+          query = query.joins(:contacts => :aspect_memberships).where(
+            :aspect_memberships => {:aspect_id => opts[:by_members_of]})
+        end
+
+        ugly_select_clause(query, opts)
+      end
+
+      def construct_shareable_from_self_query(opts)
         conditions = {:pending => false }
         conditions[:type] = opts[:type] if opts.has_key?(:type)
-        shareable_from_self = self.person.send(klass.to_s.tableize).where(conditions)
+        query = self.person.send(opts[:klass].to_s.tableize).where(conditions)
 
         if opts[:by_members_of]
-          shareable_from_others = shareable_from_others.joins(:contacts => :aspect_memberships).where(
-            :aspect_memberships => {:aspect_id => opts[:by_members_of]})
-          shareable_from_self = shareable_from_self.joins(:aspect_visibilities).where(:aspect_visibilities => {:aspect_id => opts[:by_members_of]})
+          query = query.joins(:aspect_visibilities).where(:aspect_visibilities => {:aspect_id => opts[:by_members_of]})
         end
 
-        shareable_from_others = shareable_from_others.select(select_clause).order(opts[:order_with_table]).where(klass.arel_table[opts[:order_field]].lt(opts[:max_time]))
-        shareable_from_self = shareable_from_self.select(select_clause).order(opts[:order_with_table]).where(klass.arel_table[opts[:order_field]].lt(opts[:max_time]))
-
-        "(#{shareable_from_others.to_sql} LIMIT #{opts[:limit]}) UNION ALL (#{shareable_from_self.to_sql} LIMIT #{opts[:limit]}) ORDER BY #{opts[:order]} LIMIT #{opts[:limit]}"
+        ugly_select_clause(query, opts)
       end
 
       def contact_for(person)
diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb
index 43da5b8e39..648a0c24b6 100644
--- a/spec/models/post_spec.rb
+++ b/spec/models/post_spec.rb
@@ -11,7 +11,56 @@ describe Post do
   end
 
   describe 'scopes' do
+    describe '.owned_or_visible_by_user' do
+      before do
+        @you = bob
+        @public_post = Factory(:status_message, :public => true)
+        @your_post = Factory(:status_message, :author => @you.person)
+        @post_from_contact = eve.post(:status_message, :text => 'wooo', :to => eve.aspects.where(:name => 'generic').first)
+        @post_from_stranger = Factory(:status_message, :public => false)
+      end
+
+      it 'returns post from your contacts' do
+        StatusMessage.owned_or_visible_by_user(@you).should include(@post_from_contact)
+      end
+
+      it 'returns your posts' do 
+        StatusMessage.owned_or_visible_by_user(@you).should include(@your_post)
+      end
+
+      it 'returns public posts' do
+        StatusMessage.owned_or_visible_by_user(@you).should include(@public_post)
+      end
+
+      it 'returns public post from your contact' do
+        sm = Factory(:status_message, :author => eve.person, :public => true)
+
+        StatusMessage.owned_or_visible_by_user(@you).should include(sm)
+      end
+
+      it 'does not return non contacts, non-public post' do
+        StatusMessage.owned_or_visible_by_user(@you).should_not include(@post_from_stranger)
+      end
+
+      it 'should return the three visible posts' do
+        StatusMessage.owned_or_visible_by_user(@you).count.should == 3
+      end
+    end
+
     describe '.for_a_stream' do
+      it 'calls #for_visible_shareable_sql' do
+        time, order = stub, stub
+        Post.should_receive(:for_visible_shareable_sql).with(time, order).and_return(Post)
+        Post.for_a_stream(time, order)
+      end
+
+      it 'calls includes_for_a_stream' do
+        Post.should_receive(:includes_for_a_stream)
+        Post.for_a_stream(stub, stub)
+      end
+    end
+
+    context 'having some posts' do
       before do
         time_interval = 1000
         time_past = 1000000
@@ -26,28 +75,41 @@ describe Post do
         end
       end
 
-      it 'returns the posts ordered and limited by unix time' do
-        Post.for_a_stream(Time.now + 1, "created_at").should == @posts
-        Post.for_a_stream(Time.now + 1, "updated_at").should == @posts.reverse
+      describe '.by_max_time' do
+        it 'respects time and order' do
+        end
+
+        it 'returns the posts ordered and limited by unix time' do
+          Post.for_a_stream(Time.now + 1, "created_at").should == @posts
+          Post.for_a_stream(Time.now + 1, "updated_at").should == @posts.reverse
+        end
       end
 
-      it 'includes everything in .includes_for_a_stream' do
-        Post.should_receive(:includes_for_a_stream)
-        Post.for_a_stream(Time.now + 1, "created_at")
+
+      describe '.for_visible_shareable_sql' do
+        it 'calls max_time' do
+          time = Time.now + 1
+          Post.should_receive(:by_max_time).with(time, 'created_at').and_return(Post)
+          Post.for_visible_shareable_sql(time, 'created_at')
+        end
+
+        it 'defaults to 15 posts' do
+          chain = stub.as_null_object
+
+          Post.stub(:by_max_time).and_return(chain)
+          chain.should_receive(:limit).with(15).and_return(Post)
+          Post.for_visible_shareable_sql(Time.now + 1, "created_at")
+        end
+
+        it 'respects the type option'
       end
-      it 'is limited to 15 posts' do
-        Post.stub(:by_max_time).and_return(Post)
-        Post.stub(:includes_for_a_stream).and_return(stub(:where => Post))
-        Post.should_receive(:limit)
-        Post.for_a_stream(Time.now + 1, "created_at")
+
+      describe 'includes for a stream' do
+        it 'inclues author profile and mentions'
+        it 'should include photos and root of reshares(but does not)'
       end
-    end
 
-    describe 'includes for a stream' do
-      it 'inclues author profile and mentions'
-      it 'should include photos and root of reshares(but does not)'
     end
-
   end
 
 
diff --git a/spec/models/status_message_spec.rb b/spec/models/status_message_spec.rb
index 849fa796b4..92c591f32c 100644
--- a/spec/models/status_message_spec.rb
+++ b/spec/models/status_message_spec.rb
@@ -26,38 +26,8 @@ describe StatusMessage do
        Factory.create(:status_message, :text => @test_string )
        Factory.create(:status_message, :text => @test_string )
        Factory(:status_message)
-      
-       StatusMessage.where_person_is_mentioned(@bo).count.should == 2
-      end
-    end
-
-    describe '.owned_or_visible_by_user' do
-      before do
-        @you = bob
-        @public_post = Factory(:status_message, :public => true)
-        @your_post = Factory(:status_message, :author => @you.person)
-        @post_from_contact = eve.post(:status_message, :text => 'wooo', :to => eve.aspects.where(:name => 'generic').first)
-        @post_from_stranger = Factory(:status_message, :public => false)
-      end
-
-      it 'returns post from your contacts' do
-        StatusMessage.owned_or_visible_by_user(@you).should include(@post_from_contact)
-      end
 
-      it 'returns your posts' do 
-        StatusMessage.owned_or_visible_by_user(@you).should include(@your_post)
-      end
-
-      it 'returns public posts' do
-        StatusMessage.owned_or_visible_by_user(@you).should include(@public_post)
-      end
-
-      it 'does not return non contacts, non-public post' do
-        StatusMessage.owned_or_visible_by_user(@you).should_not include(@post_from_stranger)
-      end
-
-      it 'should return the three visible posts' do
-        StatusMessage.owned_or_visible_by_user(@you).count.should == 3
+       StatusMessage.where_person_is_mentioned(@bo).count.should == 2
       end
     end
   end
diff --git a/spec/models/user/querying_spec.rb b/spec/models/user/querying_spec.rb
index 90c94b2fd8..c028aa01d3 100644
--- a/spec/models/user/querying_spec.rb
+++ b/spec/models/user/querying_spec.rb
@@ -24,8 +24,10 @@ describe User do
     end
 
     it "contains public posts from people you're following" do
+      pending
       dogs = bob.aspects.create(:name => "dogs")
-      bobs_public_post = bob.post(:status_message, :text => "hello", :public => true, :to => dogs.id)
+      bobs_public_post = Factory(:status_message, :text => "hello", :public => true, :author => bob.person)
+
       alice.visible_shareable_ids(Post).should include(bobs_public_post.id)
     end
 
@@ -158,6 +160,10 @@ describe User do
   end
 
   describe "#visible_shareables" do
+    it 'never contains posts from people not in your aspects' do
+      Factory(:status_message, :public => true)
+      bob.visible_shareables(Post).count.should == 0
+    end
     context 'with many posts' do
       before do
         bob.move_contact(eve.person, @bobs_aspect, bob.aspects.create(:name => 'new aspect'))
@@ -177,7 +183,7 @@ describe User do
 
       it 'works' do # The set up takes a looong time, so to save time we do several tests in one
         bob.visible_shareables(Post).length.should == 15 #it returns 15 by default
-        bob.visible_shareables(Post).should == bob.visible_shareables(Post, :by_members_of => bob.aspects.map { |a| a.id }) # it is the same when joining through aspects
+        bob.visible_shareables(Post).map(&:id).should == bob.visible_shareables(Post, :by_members_of => bob.aspects.map { |a| a.id }).map(&:id) # it is the same when joining through aspects
 
         # checks the default sort order
         bob.visible_shareables(Post).sort_by { |p| p.created_at }.map { |p| p.id }.should == bob.visible_shareables(Post).map { |p| p.id }.reverse #it is sorted updated_at desc by default
@@ -193,7 +199,7 @@ describe User do
         # It should respect the limit option
         opts = {:limit => 40}
         bob.visible_shareables(Post, opts).length.should == 40
-        bob.visible_shareables(Post, opts).should == bob.visible_shareables(Post, opts.merge(:by_members_of => bob.aspects.map { |a| a.id }))
+        bob.visible_shareables(Post, opts).map(&:id).should == bob.visible_shareables(Post, opts.merge(:by_members_of => bob.aspects.map { |a| a.id })).map(&:id)
         bob.visible_shareables(Post, opts).sort_by { |p| p.created_at }.map { |p| p.id }.should == bob.visible_shareables(Post, opts).map { |p| p.id }.reverse
 
         # It should paginate using a datetime timestamp
-- 
GitLab