DG MS; some refactoring

This commit is contained in:
danielgrippi 2011-10-19 15:47:11 -07:00
parent ba48a46c6e
commit c49c76e2a6
7 changed files with 147 additions and 85 deletions

View file

@ -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

View file

@ -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}).

View file

@ -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

View file

@ -48,28 +48,45 @@ module Diaspora
def visible_shareable_sql(klass, opts={})
table = klass.table_name
opts = prep_opts(klass, opts)
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]
opts[:klass] = klass
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)
conditions = {:pending => false }
conditions[:type] = opts[:type] if opts.has_key?(:type)
shareable_from_self = self.person.send(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]})
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 = 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)
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)
query = self.person.send(opts[:klass].to_s.tableize).where(conditions)
if opts[:by_members_of]
query = query.joins(:aspect_visibilities).where(:aspect_visibilities => {:aspect_id => opts[:by_members_of]})
end
ugly_select_clause(query, opts)
end
def contact_for(person)
return nil unless person
contact_for_person_id(person.id)

View file

@ -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

View file

@ -26,40 +26,10 @@ 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
end
end
end
describe '.before_create' do

View file

@ -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