Merge branch 'stream-object'
This commit is contained in:
commit
586746868c
15 changed files with 385 additions and 232 deletions
|
|
@ -2,6 +2,8 @@
|
|||
# licensed under the Affero General Public License version 3 or later. See
|
||||
# the COPYRIGHT file.
|
||||
|
||||
require File.join(Rails.root, "lib", "aspect_stream")
|
||||
|
||||
class AspectsController < ApplicationController
|
||||
before_filter :authenticate_user!
|
||||
before_filter :save_sort_order, :only => :index
|
||||
|
|
@ -11,38 +13,19 @@ class AspectsController < ApplicationController
|
|||
respond_to :json, :only => [:show, :create]
|
||||
|
||||
helper_method :tags, :tag_followings
|
||||
helper_method :all_aspects_selected?
|
||||
helper_method :selected_people
|
||||
|
||||
def index
|
||||
@aspects = current_user.aspects
|
||||
@aspects = @aspects.where(:id => params[:a_ids]) if params[:a_ids]
|
||||
|
||||
@aspect_ids = @aspects.map { |a| a.id }
|
||||
@posts = current_user.visible_posts(:by_members_of => @aspect_ids,
|
||||
:type => ['StatusMessage','Reshare', 'ActivityStreams::Photo'],
|
||||
:order => session[:sort_order] + ' DESC',
|
||||
:max_time => params[:max_time].to_i
|
||||
).includes(:mentions => {:person => :profile}, :author => :profile)
|
||||
aspect_ids = (params[:a_ids] ? params[:a_ids] : [])
|
||||
@stream = AspectStream.new(current_user, aspect_ids,
|
||||
:order => session[:sort_order],
|
||||
:max_time => params[:max_time].to_i)
|
||||
|
||||
if params[:only_posts]
|
||||
render :partial => 'shared/stream', :locals => {:posts => @posts}
|
||||
else
|
||||
@contact_count = selected_people(@aspect_ids).count
|
||||
|
||||
# aspects.first is used for mobile
|
||||
# the :all is currently used for view switching logic
|
||||
@aspect = (params[:a_ids] ? @aspects.first : :all)
|
||||
render :partial => 'shared/stream', :locals => {:posts => @stream.posts}
|
||||
end
|
||||
end
|
||||
|
||||
def selected_people(aspect_ids)
|
||||
@selected_people ||= Person.joins(:contacts => :aspect_memberships).
|
||||
where(:contacts => {:user_id => current_user.id},
|
||||
:aspect_memberships => {:aspect_id => @aspect_ids}).
|
||||
select("DISTINCT people.*").includes(:profile)
|
||||
end
|
||||
|
||||
def create
|
||||
@aspect = current_user.aspects.create(params[:aspect])
|
||||
|
||||
|
|
@ -100,8 +83,7 @@ class AspectsController < ApplicationController
|
|||
end
|
||||
|
||||
def show
|
||||
@aspect = current_user.aspects.where(:id => params[:id]).first
|
||||
if @aspect
|
||||
if @aspect = current_user.aspects.where(:id => params[:id]).first
|
||||
redirect_to aspects_path('a_ids[]' => @aspect.id)
|
||||
else
|
||||
redirect_to aspects_path
|
||||
|
|
@ -156,10 +138,6 @@ class AspectsController < ApplicationController
|
|||
params[:max_time] ||= Time.now + 1
|
||||
end
|
||||
|
||||
def all_aspects_selected?
|
||||
@aspect == :all
|
||||
end
|
||||
|
||||
def tag_followings
|
||||
if current_user
|
||||
if @tag_followings == nil
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module StreamHelper
|
|||
elsif controller.instance_of?(PeopleController)
|
||||
person_path(@person, :max_time => @posts.last.created_at.to_i)
|
||||
elsif controller.instance_of?(AspectsController)
|
||||
aspects_path(:max_time => @posts.last.send(session[:sort_order].to_sym).to_i, :a_ids => params[:a_ids])
|
||||
aspects_path(:max_time => @stream.posts.last.send(@stream.order.to_sym).to_i, :a_ids => @stream.aspect_ids)
|
||||
else
|
||||
raise 'in order to use pagination for this new controller, update next_page_path in stream helper'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,6 +48,14 @@ class Person < ActiveRecord::Base
|
|||
scope :local, where('people.owner_id IS NOT NULL')
|
||||
scope :for_json, select('DISTINCT people.id, people.diaspora_handle').includes(:profile)
|
||||
|
||||
# @note user is passed in here defensively
|
||||
scope :all_from_aspects, lambda { |aspect_ids, user|
|
||||
joins(:contacts => :aspect_memberships).
|
||||
where(:contacts => {:user_id => user.id},
|
||||
:aspect_memberships => {:aspect_id => aspect_ids}).
|
||||
select("DISTINCT people.*").includes(:profile)
|
||||
}
|
||||
|
||||
def self.featured_users
|
||||
AppConfig[:featured_users].present? ? Person.where(:diaspora_handle => AppConfig[:featured_users]) : []
|
||||
end
|
||||
|
|
@ -68,13 +76,13 @@ class Person < ActiveRecord::Base
|
|||
|
||||
|
||||
def self.find_from_id_or_username(params)
|
||||
p = if params[:id].present?
|
||||
Person.where(:id => params[:id]).first
|
||||
elsif params[:username].present? && u = User.find_by_username(params[:username])
|
||||
u.person
|
||||
else
|
||||
nil
|
||||
end
|
||||
p = if params[:id].present?
|
||||
Person.where(:id => params[:id]).first
|
||||
elsif params[:username].present? && u = User.find_by_username(params[:username])
|
||||
u.person
|
||||
else
|
||||
nil
|
||||
end
|
||||
raise ActiveRecord::RecordNotFound unless p.present?
|
||||
p
|
||||
end
|
||||
|
|
@ -126,6 +134,8 @@ class Person < ActiveRecord::Base
|
|||
Person.searchable.where(sql, *tokens)
|
||||
end
|
||||
|
||||
|
||||
|
||||
def name(opts = {})
|
||||
if self.profile.nil?
|
||||
fix_profile
|
||||
|
|
|
|||
|
|
@ -6,24 +6,24 @@
|
|||
#sort_by
|
||||
= t('.recently')
|
||||
%span.controls
|
||||
= link_to_if(session[:sort_order] == 'created_at', t('.commented_on'), aspects_path(:a_ids => params[:a_ids], :sort_order => 'updated_at'))
|
||||
= link_to_if(session[:sort_order] == 'created_at', t('.commented_on'), aspects_path(:a_ids => stream.aspect_ids, :sort_order => 'updated_at'))
|
||||
·
|
||||
= link_to_if(session[:sort_order] == 'updated_at', t('.posted'), aspects_path(:a_ids => params[:a_ids], :sort_order => 'created_at' ))
|
||||
= link_to_if(session[:sort_order] == 'updated_at', t('.posted'), aspects_path(:a_ids => stream.aspect_ids, :sort_order => 'created_at' ))
|
||||
|
||||
%h3
|
||||
- if all_aspects_selected?
|
||||
- if stream.for_all_aspects?
|
||||
= t('.stream')
|
||||
- else
|
||||
= @aspects.to_sentence
|
||||
= stream.aspects.to_sentence
|
||||
|
||||
= render 'shared/publisher', :selected_aspects => @aspects, :aspect_ids => aspect_ids, :aspect => @aspect
|
||||
= render 'shared/publisher', :selected_aspects => stream.aspects, :aspect_ids => stream.aspect_ids, :for_all_aspects => stream.for_all_aspects?, :aspect => stream.aspect
|
||||
= render 'aspects/no_posts_message'
|
||||
|
||||
- if current_user.contacts.size < 2
|
||||
= render 'aspects/no_contacts_message'
|
||||
|
||||
#main_stream.stream{:data => {:guids => aspect_ids.join(',')}}
|
||||
- if posts.length > 0
|
||||
= render 'shared/stream', :posts => posts
|
||||
#main_stream.stream{:data => {:guids => stream.aspect_ids.join(',')}}
|
||||
- if stream.posts.length > 0
|
||||
= render 'shared/stream', :posts => stream.posts
|
||||
#pagination
|
||||
=link_to(t('more'), next_page_path, :class => 'paginate')
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
#selected_aspect_contacts.section
|
||||
.title.no_icon
|
||||
%h5
|
||||
- if all_aspects_selected? || !@aspect || @aspect_ids.size > 1
|
||||
- if @stream.for_all_aspects? || @stream.aspect_ids.size > 1
|
||||
= "#{t('_contacts')}"
|
||||
- else
|
||||
= @aspect.name
|
||||
= "(#{count})"
|
||||
= @stream.aspect.name
|
||||
= "(#{@stream.people.size})"
|
||||
|
||||
|
||||
.content
|
||||
- if people.size > 0
|
||||
- for person in people
|
||||
= person_image_link person
|
||||
- if @stream.people.size > 0
|
||||
- for person in @stream.people
|
||||
= person_image_link(person)
|
||||
|
||||
- if all_aspects_selected? || @aspect_ids.size > 1
|
||||
- if @stream.for_all_aspects? || @stream.aspect_ids.size > 1
|
||||
= link_to t('.view_all_contacts'), contacts_link, :id => "view_all_contacts_link"
|
||||
- else
|
||||
= link_to t('.view_all_contacts'), contacts_path(:a_id => @aspect.id), :id => "view_all_contacts_link"
|
||||
= link_to t('.view_all_contacts'), contacts_path(:a_id => @stream.aspect.id), :id => "view_all_contacts_link"
|
||||
|
||||
- else
|
||||
= t('.no_contacts')
|
||||
|
|
|
|||
|
|
@ -26,12 +26,9 @@
|
|||
|
||||
.span-13.append-1
|
||||
#aspect_stream_container.stream_container
|
||||
= render 'aspect_stream',
|
||||
:aspect => @aspect,
|
||||
:aspect_ids => @aspect_ids,
|
||||
:posts => @posts
|
||||
= render 'aspect_stream', :stream => @stream
|
||||
|
||||
.span-5.rightBar.last
|
||||
= render 'selected_contacts', :people => selected_people(@aspect_ids).sample(20), :count => @contact_count
|
||||
= render 'selected_contacts', :stream => @stream
|
||||
|
||||
= render 'shared/right_sections'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
$('#aspect_stream_container').html("<%= escape_javascript(render('aspects/aspect_stream', :aspect => @aspect, :aspect_ids => @aspect_ids, :posts => @posts)) %>");
|
||||
$('#selected_aspect_contacts').html("<%= escape_javascript(render('aspects/selected_contacts', :people => @selected_people.sample(20), :count => @contact_count )) %>");
|
||||
$('#aspect_stream_container').html("<%= escape_javascript(render('aspects/aspect_stream', :stream => @stream)) %>");
|
||||
$('#selected_aspect_contacts').html("<%= escape_javascript(render('aspects/selected_contacts', :people => @stream.people.sample(20), :count => @stream.people.size)) %>");
|
||||
$('#aspect_stream_container a[rel*=facebox]').facebox();
|
||||
|
|
|
|||
|
|
@ -3,18 +3,18 @@
|
|||
-# the COPYRIGHT file.
|
||||
|
||||
#aspect_header
|
||||
- if @aspect == :all
|
||||
- if @stream.for_all_aspects?
|
||||
= t('all_aspects')
|
||||
- else
|
||||
= @aspect
|
||||
= @stream.aspect
|
||||
= link_to t('.post_a_message'), '#publisher_page', :id => 'publisher_button'
|
||||
|
||||
|
||||
#main_stream.stream
|
||||
= render 'shared/stream', :posts => @posts
|
||||
-if @posts.length > 0
|
||||
= render 'shared/stream', :posts => @stream.posts
|
||||
-if @stream.posts.length > 0
|
||||
#pagination
|
||||
%a.more-link.paginate{:href => next_page_path}
|
||||
%h2= t("more")
|
||||
- content_for :subpages do
|
||||
= render 'shared/publisher', :aspect_ids => @aspect_ids, :selected_aspects => @aspects, :aspect => @aspect
|
||||
= render 'shared/publisher', :aspect_ids => @stream.aspect_ids, :selected_aspects => @stream.aspects, :aspect => @stream.aspect
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
.public_toggle
|
||||
%span#publisher_service_icons
|
||||
= t("shared.publisher.click_to_share_with")
|
||||
- if aspect == :all || aspect == :profile
|
||||
- if((defined?(for_all_aspects) && for_all_aspects) || aspect == :profile)
|
||||
= status.hidden_field(:public)
|
||||
= image_tag "icons/globe.png", :title => t('javascripts.publisher.limited'), :class => 'public_icon dim', :width => 16, :height => 16
|
||||
- if current_user.services
|
||||
|
|
|
|||
|
|
@ -25,12 +25,12 @@
|
|||
|
||||
= status.text_area :text
|
||||
|
||||
- for aspect_id in @aspect_ids
|
||||
- for aspect_id in aspect_ids
|
||||
= hidden_field_tag 'aspect_ids[]', aspect_id.to_s
|
||||
|
||||
|
||||
%fieldset
|
||||
- unless params[:a_ids]
|
||||
- unless aspect_ids
|
||||
%input{:type => 'checkbox', :name => 'status_message[public]', :id => 'public', :class => 'custom', :value => 'true'}
|
||||
%label{:for => 'public'}
|
||||
= t('.make_public')
|
||||
|
|
|
|||
72
lib/aspect_stream.rb
Normal file
72
lib/aspect_stream.rb
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Copyright (c) 2011, Diaspora Inc. This file is
|
||||
# licensed under the Affero General Public License version 3 or later. See
|
||||
# the COPYRIGHT file.
|
||||
|
||||
class AspectStream
|
||||
|
||||
attr_reader :max_time, :order
|
||||
|
||||
# @param user [User]
|
||||
# @param inputted_aspect_ids [Array<Integer>] Ids of aspects for given stream
|
||||
# @param aspect_ids [Array<Integer>] Aspects this stream is responsible for
|
||||
# @opt max_time [Integer] Unix timestamp of stream's post ceiling
|
||||
# @opt order [String] Order of posts (i.e. 'created_at', 'updated_at')
|
||||
# @return [void]
|
||||
def initialize(user, inputted_aspect_ids, opts={})
|
||||
@user = user
|
||||
@inputted_aspect_ids = inputted_aspect_ids
|
||||
@max_time = opts[:max_time]
|
||||
@order = opts[:order]
|
||||
end
|
||||
|
||||
# Filters aspects given the stream's aspect ids on initialization and the user.
|
||||
# Will disclude aspects from inputted aspect ids if user is not associated with their
|
||||
# target aspects.
|
||||
#
|
||||
# @return [ActiveRecord::Association<Aspect>] Filtered aspects given the stream's user
|
||||
def aspects
|
||||
@aspects ||= lambda do
|
||||
a = @user.aspects
|
||||
a = a.where(:id => @inputted_aspect_ids) if @inputted_aspect_ids.length > 0
|
||||
a
|
||||
end.call
|
||||
end
|
||||
|
||||
# Maps ids into an array from #aspects
|
||||
#
|
||||
# @return [Array<Integer>] Aspect ids
|
||||
def aspect_ids
|
||||
@aspect_ids ||= aspects.map { |a| a.id }
|
||||
end
|
||||
|
||||
# @return [ActiveRecord::Association<Post>] AR association of posts
|
||||
def posts
|
||||
# NOTE(this should be something like Post.all_for_stream(@user, aspect_ids, {}) that calls visible_posts
|
||||
@posts ||= @user.visible_posts(:by_members_of => @aspect_ids,
|
||||
:type => ['StatusMessage', 'Reshare', 'ActivityStreams::Photo'],
|
||||
:order => "#{@order} DESC",
|
||||
:max_time => @max_time
|
||||
).includes(:mentions => {:person => :profile}, :author => :profile)
|
||||
end
|
||||
|
||||
# @return [ActiveRecord::Association<Person>] AR association of people within stream's given aspects
|
||||
def people
|
||||
@people ||= Person.all_from_aspects(aspect_ids, @user)
|
||||
end
|
||||
|
||||
# @note aspects.first is used for mobile. NOTE(this is a hack and should be fixed)
|
||||
# @return [Aspect,Symbol]
|
||||
def aspect
|
||||
if !for_all_aspects? || aspects.size == 1
|
||||
aspects.first
|
||||
end
|
||||
end
|
||||
|
||||
# Determine whether or not the stream is displaying across
|
||||
# all of the user's aspects.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def for_all_aspects?
|
||||
@all_aspects ||= aspect_ids.length == @user.aspects.size
|
||||
end
|
||||
end
|
||||
|
|
@ -66,61 +66,92 @@ describe AspectsController do
|
|||
end
|
||||
|
||||
describe "#index" do
|
||||
it "generates a jasmine fixture", :fixture => true do
|
||||
context 'jasmine fixtures' do
|
||||
it "generates a jasmine fixture", :fixture => true do
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index")
|
||||
end
|
||||
|
||||
it "generates a jasmine fixture with a prefill", :fixture => true do
|
||||
get :index, :prefill => "reshare things"
|
||||
save_fixture(html_for("body"), "aspects_index_prefill")
|
||||
end
|
||||
|
||||
it 'generates a jasmine fixture with services', :fixture => true do
|
||||
alice.services << Services::Facebook.create(:user_id => alice.id)
|
||||
alice.services << Services::Twitter.create(:user_id => alice.id)
|
||||
get :index, :prefill => "reshare things"
|
||||
save_fixture(html_for("body"), "aspects_index_services")
|
||||
end
|
||||
|
||||
it 'generates a jasmine fixture with posts', :fixture => true do
|
||||
bob.post(:status_message, :text => "Is anyone out there?", :to => @bob.aspects.where(:name => "generic").first.id)
|
||||
message = alice.post(:status_message, :text => "hello "*800, :to => @alices_aspect_2.id)
|
||||
5.times { bob.comment("what", :post => message) }
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index_with_posts")
|
||||
end
|
||||
|
||||
it "generates a jasmine fixture with a post with comments", :fixture => true do
|
||||
message = bob.post(:status_message, :text => "HALO WHIRLED", :to => @bob.aspects.where(:name => "generic").first.id)
|
||||
5.times { bob.comment("what", :post => message) }
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index_post_with_comments")
|
||||
end
|
||||
|
||||
it 'generates a jasmine fixture with a followed tag', :fixture => true do
|
||||
@tag = ActsAsTaggableOn::Tag.create!(:name => "partytimeexcellent")
|
||||
TagFollowing.create!(:tag => @tag, :user => alice)
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index_with_one_followed_tag")
|
||||
end
|
||||
|
||||
it "generates a jasmine fixture with a post containing a video", :fixture => true do
|
||||
stub_request(:get, "http://gdata.youtube.com/feeds/api/videos/UYrkQL1bX4A?v=2").
|
||||
with(:headers => {'Accept'=>'*/*'}).
|
||||
to_return(:status => 200, :body => "<title>LazyTown song - Cooking By The Book</title>", :headers => {})
|
||||
alice.post(:status_message, :text => "http://www.youtube.com/watch?v=UYrkQL1bX4A", :to => @alices_aspect_2.id)
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index_with_video_post")
|
||||
end
|
||||
|
||||
it "generates a jasmine fixture with a post that has been liked", :fixture => true do
|
||||
message = alice.post(:status_message, :text => "hello "*800, :to => @alices_aspect_2.id)
|
||||
alice.build_like(:positive => true, :target => message).save
|
||||
bob.build_like(:positive => true, :target => message).save
|
||||
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index_with_a_post_with_likes")
|
||||
end
|
||||
end
|
||||
|
||||
it 'renders just the stream with the infinite scroll param set' do
|
||||
get :index, :only_posts => true
|
||||
response.should render_template('shared/_stream')
|
||||
end
|
||||
|
||||
it 'assigns an AspectStream' do
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index")
|
||||
assigns(:stream).class.should == AspectStream
|
||||
end
|
||||
|
||||
it "generates a jasmine fixture with a prefill", :fixture => true do
|
||||
get :index, :prefill => "reshare things"
|
||||
save_fixture(html_for("body"), "aspects_index_prefill")
|
||||
end
|
||||
describe 'filtering by aspect' do
|
||||
before do
|
||||
@aspect1 = alice.aspects.create(:name => "test aspect")
|
||||
@stream = AspectStream.new(alice, [])
|
||||
@stream.stub(:posts).and_return([])
|
||||
end
|
||||
|
||||
it 'generates a jasmine fixture with services', :fixture => true do
|
||||
alice.services << Services::Facebook.create(:user_id => alice.id)
|
||||
alice.services << Services::Twitter.create(:user_id => alice.id)
|
||||
get :index, :prefill => "reshare things"
|
||||
save_fixture(html_for("body"), "aspects_index_services")
|
||||
end
|
||||
it 'respects a single aspect' do
|
||||
AspectStream.should_receive(:new).with(alice, [@aspect1.id], anything).and_return(@stream)
|
||||
get :index, :a_ids => [@aspect1.id]
|
||||
end
|
||||
|
||||
it 'generates a jasmine fixture with posts', :fixture => true do
|
||||
bob.post(:status_message, :text => "Is anyone out there?", :to => @bob.aspects.where(:name => "generic").first.id)
|
||||
message = alice.post(:status_message, :text => "hello "*800, :to => @alices_aspect_2.id)
|
||||
5.times { bob.comment("what", :post => message) }
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index_with_posts")
|
||||
end
|
||||
|
||||
it "generates a jasmine fixture with a post with comments", :fixture => true do
|
||||
message = bob.post(:status_message, :text => "HALO WHIRLED", :to => @bob.aspects.where(:name => "generic").first.id)
|
||||
5.times { bob.comment("what", :post => message) }
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index_post_with_comments")
|
||||
end
|
||||
|
||||
it 'generates a jasmine fixture with a followed tag', :fixture => true do
|
||||
@tag = ActsAsTaggableOn::Tag.create!(:name => "partytimeexcellent")
|
||||
TagFollowing.create!(:tag => @tag, :user => alice)
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index_with_one_followed_tag")
|
||||
end
|
||||
|
||||
it "generates a jasmine fixture with a post containing a video", :fixture => true do
|
||||
stub_request(:get, "http://gdata.youtube.com/feeds/api/videos/UYrkQL1bX4A?v=2").
|
||||
with(:headers => {'Accept'=>'*/*'}).
|
||||
to_return(:status => 200, :body => "<title>LazyTown song - Cooking By The Book</title>", :headers => {})
|
||||
alice.post(:status_message, :text => "http://www.youtube.com/watch?v=UYrkQL1bX4A", :to => @alices_aspect_2.id)
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index_with_video_post")
|
||||
end
|
||||
|
||||
it "generates a jasmine fixture with a post that has been liked", :fixture => true do
|
||||
message = alice.post(:status_message, :text => "hello "*800, :to => @alices_aspect_2.id)
|
||||
alice.build_like(:positive => true, :target => message).save
|
||||
bob.build_like(:positive => true, :target => message).save
|
||||
|
||||
get :index
|
||||
save_fixture(html_for("body"), "aspects_index_with_a_post_with_likes")
|
||||
it 'respects multiple aspects' do
|
||||
aspect2 = alice.aspects.create(:name => "test aspect two")
|
||||
AspectStream.should_receive(:new).with(alice, [@aspect1.id, aspect2.id], anything).and_return(@stream)
|
||||
get :index, :a_ids => [@aspect1.id, aspect2.id]
|
||||
end
|
||||
end
|
||||
|
||||
context "mobile" do
|
||||
|
|
@ -135,117 +166,6 @@ describe AspectsController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with getting_started = true' do
|
||||
before do
|
||||
alice.getting_started = true
|
||||
alice.save
|
||||
end
|
||||
|
||||
it 'does not redirect mobile users to getting_started' do
|
||||
get :index, :format => :mobile
|
||||
response.should_not be_redirect
|
||||
end
|
||||
|
||||
it 'does not redirect ajax to getting_started' do
|
||||
get :index, :format => :js
|
||||
response.should_not be_redirect
|
||||
end
|
||||
end
|
||||
|
||||
context 'with posts in multiple aspects' do
|
||||
before do
|
||||
@posts = []
|
||||
2.times do |n|
|
||||
user = Factory(:user)
|
||||
aspect = user.aspects.create(:name => 'people')
|
||||
connect_users(alice, @alices_aspect_1, user, aspect)
|
||||
target_aspect = n.even? ? @alices_aspect_1 : @alices_aspect_2
|
||||
post = alice.post(:status_message, :text=> "hello#{n}", :to => target_aspect)
|
||||
post.created_at = Time.now - (2 - n).seconds
|
||||
post.save!
|
||||
@posts << post
|
||||
end
|
||||
alice.build_comment(:text => 'lalala', :post => @posts.first).save
|
||||
end
|
||||
|
||||
describe "post visibilities" do
|
||||
before do
|
||||
aspect_to_post = bob.aspects.where(:name => "generic").first
|
||||
@status = bob.post(:status_message, :text=> "hello", :to => aspect_to_post)
|
||||
@vis = @status.post_visibilities.first
|
||||
end
|
||||
|
||||
it "pulls back non hidden posts" do
|
||||
get :index
|
||||
assigns[:posts].include?(@status).should be_true
|
||||
end
|
||||
it "does not pull back hidden posts" do
|
||||
@vis.update_attributes(:hidden => true)
|
||||
get :index
|
||||
assigns[:posts].include?(@status).should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe 'infinite scroll' do
|
||||
it 'renders with the infinite scroll param' do
|
||||
get :index, :only_posts => true
|
||||
assigns[:posts].include?(@posts.first).should be_true
|
||||
response.should be_success
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "ordering" do
|
||||
it "orders posts by updated_at by default" do
|
||||
get :index
|
||||
assigns(:posts).should == @posts
|
||||
end
|
||||
|
||||
it "orders posts by created_at on request" do
|
||||
get :index, :sort_order => 'created_at'
|
||||
assigns(:posts).should == @posts.reverse
|
||||
end
|
||||
|
||||
it "remembers your sort order and lets you override the memory" do
|
||||
get :index, :sort_order => "created_at"
|
||||
assigns(:posts).should == @posts.reverse
|
||||
get :index
|
||||
assigns(:posts).should == @posts.reverse
|
||||
get :index, :sort_order => "updated_at"
|
||||
assigns(:posts).should == @posts
|
||||
end
|
||||
|
||||
it "doesn't allow SQL injection" do
|
||||
get :index, :sort_order => "\"; DROP TABLE users;"
|
||||
assigns(:posts).should == @posts
|
||||
get :index, :sort_order => "created_at"
|
||||
assigns(:posts).should == @posts.reverse
|
||||
end
|
||||
end
|
||||
|
||||
it "returns all posts by default" do
|
||||
alice.aspects.reload
|
||||
get :index
|
||||
assigns(:posts).length.should == 2
|
||||
end
|
||||
|
||||
it "posts include reshares" do
|
||||
reshare = alice.post(:reshare, :public => true, :root_guid => Factory(:status_message, :public => true).guid, :to => alice.aspects)
|
||||
get :index
|
||||
assigns[:posts].map { |x| x.id }.should include(reshare.id)
|
||||
end
|
||||
|
||||
it "can filter to a single aspect" do
|
||||
get :index, :a_ids => [@alices_aspect_2.id.to_s]
|
||||
assigns(:posts).length.should == 1
|
||||
end
|
||||
|
||||
it "can filter to multiple aspects" do
|
||||
get :index, :a_ids => [@alices_aspect_1.id.to_s, @alices_aspect_2.id.to_s]
|
||||
assigns(:posts).length.should == 2
|
||||
end
|
||||
end
|
||||
|
||||
describe 'performance', :performance => true do
|
||||
before do
|
||||
require 'benchmark'
|
||||
|
|
|
|||
122
spec/lib/aspect_stream_spec.rb
Normal file
122
spec/lib/aspect_stream_spec.rb
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
require 'aspect_stream'
|
||||
|
||||
describe AspectStream do
|
||||
describe '#aspects' do
|
||||
it 'queries the user given initialized aspect ids' do
|
||||
alice = stub.as_null_object
|
||||
stream = AspectStream.new(alice, [1,2,3])
|
||||
|
||||
alice.aspects.should_receive(:where)
|
||||
stream.aspects
|
||||
end
|
||||
|
||||
it "returns all the user's aspects if no aspect ids are specified" do
|
||||
alice = stub.as_null_object
|
||||
stream = AspectStream.new(alice, [])
|
||||
|
||||
alice.aspects.should_not_receive(:where)
|
||||
stream.aspects
|
||||
end
|
||||
|
||||
it 'filters aspects given a user' do
|
||||
alice = stub(:aspects => [stub(:id => 1)])
|
||||
alice.aspects.stub(:where).and_return(alice.aspects)
|
||||
stream = AspectStream.new(alice, [1,2,3])
|
||||
|
||||
stream.aspects.should == alice.aspects
|
||||
end
|
||||
end
|
||||
|
||||
describe '#aspect_ids' do
|
||||
it 'maps ids from aspects' do
|
||||
alice = stub.as_null_object
|
||||
aspects = stub.as_null_object
|
||||
|
||||
stream = AspectStream.new(alice, [1,2])
|
||||
|
||||
stream.should_receive(:aspects).and_return(aspects)
|
||||
aspects.should_receive(:map)
|
||||
stream.aspect_ids
|
||||
end
|
||||
end
|
||||
|
||||
describe '#posts' do
|
||||
before do
|
||||
@alice = stub.as_null_object
|
||||
end
|
||||
|
||||
it 'calls visible posts for the given user' do
|
||||
stream = AspectStream.new(@alice, [1,2])
|
||||
|
||||
@alice.should_receive(:visible_posts).and_return(stub.as_null_object)
|
||||
stream.posts
|
||||
end
|
||||
|
||||
it 'is called with 3 types' do
|
||||
stream = AspectStream.new(@alice, [1,2], :order => 'created_at')
|
||||
@alice.should_receive(:visible_posts).with(hash_including(:type=> ['StatusMessage', 'Reshare', 'ActivityStreams::Photo'])).and_return(stub.as_null_object)
|
||||
stream.posts
|
||||
end
|
||||
|
||||
it 'respects ordering' do
|
||||
stream = AspectStream.new(@alice, [1,2], :order => 'created_at')
|
||||
@alice.should_receive(:visible_posts).with(hash_including(:order => 'created_at DESC')).and_return(stub.as_null_object)
|
||||
stream.posts
|
||||
end
|
||||
|
||||
it 'respects max_time' do
|
||||
stream = AspectStream.new(@alice, [1,2], :max_time => 123)
|
||||
@alice.should_receive(:visible_posts).with(hash_including(:max_time => 123)).and_return(stub.as_null_object)
|
||||
stream.posts
|
||||
end
|
||||
end
|
||||
|
||||
describe '#people' do
|
||||
it 'should call Person.all_from_aspects' do
|
||||
class Person ; end
|
||||
|
||||
alice = stub.as_null_object
|
||||
aspect_ids = [1,2,3]
|
||||
stream = AspectStream.new(alice, [])
|
||||
|
||||
stream.stub(:aspect_ids).and_return(aspect_ids)
|
||||
Person.should_receive(:all_from_aspects).with(stream.aspect_ids, alice)
|
||||
stream.people
|
||||
end
|
||||
end
|
||||
|
||||
describe '#aspect' do
|
||||
before do
|
||||
alice = stub.as_null_object
|
||||
@stream = AspectStream.new(alice, [1,2])
|
||||
end
|
||||
|
||||
it "returns an aspect if the stream is not for all the user's aspects" do
|
||||
@stream.stub(:for_all_aspects?).and_return(false)
|
||||
@stream.aspect.should_not be_nil
|
||||
end
|
||||
|
||||
it "returns nothing if the stream is not for all the user's aspects" do
|
||||
@stream.stub(:for_all_aspects?).and_return(true)
|
||||
@stream.aspect.should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe 'for_all_aspects?' do
|
||||
before do
|
||||
alice = stub.as_null_object
|
||||
alice.aspects.stub(:size).and_return(2)
|
||||
@stream = AspectStream.new(alice, [1,2])
|
||||
end
|
||||
|
||||
it "is true if the count of aspect_ids is equal to the size of the user's aspect count" do
|
||||
@stream.aspect_ids.stub(:length).and_return(2)
|
||||
@stream.should be_for_all_aspects
|
||||
end
|
||||
|
||||
it "is false if the count of aspect_ids is not equal to the size of the user's aspect count" do
|
||||
@stream.aspect_ids.stub(:length).and_return(1)
|
||||
@stream.should_not be_for_all_aspects
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -40,6 +40,7 @@ describe Person do
|
|||
person_ids.uniq.should == person_ids
|
||||
end
|
||||
end
|
||||
|
||||
describe '.local' do
|
||||
it 'returns only local people' do
|
||||
Person.local =~ [@person]
|
||||
|
|
@ -73,7 +74,25 @@ describe Person do
|
|||
}.to raise_error ActiveRecord::RecordNotFound
|
||||
end
|
||||
end
|
||||
|
||||
describe '.all_from_aspects' do
|
||||
it "pulls back the right people given all a user's aspects" do
|
||||
aspect_ids = bob.aspects.map(&:id)
|
||||
Person.all_from_aspects(aspect_ids, bob).map(&:id).should =~ bob.contacts.includes(:person).map{|c| c.person.id}
|
||||
end
|
||||
|
||||
it "pulls back the right people given a subset of aspects" do
|
||||
aspect_ids = bob.aspects.first.id
|
||||
Person.all_from_aspects(aspect_ids, bob).map(&:id).should =~ bob.aspects.first.contacts.includes(:person).map{|c| c.person.id}
|
||||
end
|
||||
|
||||
it "respects aspects given a user" do
|
||||
aspect_ids = alice.aspects.map(&:id)
|
||||
Person.all_from_aspects(aspect_ids, bob).map(&:id).should == []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "delegating" do
|
||||
it "delegates last_name to the profile" do
|
||||
@person.last_name.should == @person.profile.last_name
|
||||
|
|
|
|||
|
|
@ -17,38 +17,46 @@ describe User do
|
|||
public_post = alice.post(:status_message, :text => "hi", :to => @alices_aspect.id, :public => true)
|
||||
alice.visible_posts.should include(public_post)
|
||||
end
|
||||
|
||||
it "contains your non-public posts" do
|
||||
private_post = alice.post(:status_message, :text => "hi", :to => @alices_aspect.id, :public => false)
|
||||
alice.visible_posts.should include(private_post)
|
||||
end
|
||||
|
||||
it "contains public posts from people you're following" do
|
||||
dogs = bob.aspects.create(:name => "dogs")
|
||||
bobs_public_post = bob.post(:status_message, :text => "hello", :public => true, :to => dogs.id)
|
||||
alice.visible_posts.should include(bobs_public_post)
|
||||
end
|
||||
|
||||
it "contains non-public posts from people who are following you" do
|
||||
bobs_post = bob.post(:status_message, :text => "hello", :to => @bobs_aspect.id)
|
||||
alice.visible_posts.should include(bobs_post)
|
||||
end
|
||||
|
||||
it "does not contain non-public posts from aspects you're not in" do
|
||||
dogs = bob.aspects.create(:name => "dogs")
|
||||
invisible_post = bob.post(:status_message, :text => "foobar", :to => dogs.id)
|
||||
alice.visible_posts.should_not include(invisible_post)
|
||||
end
|
||||
|
||||
it "does not contain pending posts" do
|
||||
pending_post = bob.post(:status_message, :text => "hey", :public => true, :to => @bobs_aspect.id, :pending => true)
|
||||
pending_post.should be_pending
|
||||
alice.visible_posts.should_not include pending_post
|
||||
end
|
||||
|
||||
it "does not contain pending photos" do
|
||||
pending_photo = bob.post(:photo, :pending => true, :user_file=> File.open(photo_fixture_name), :to => @bobs_aspect)
|
||||
alice.visible_posts.should_not include pending_photo
|
||||
end
|
||||
|
||||
it "respects the :type option" do
|
||||
photo = bob.post(:photo, :pending => false, :user_file=> File.open(photo_fixture_name), :to => @bobs_aspect)
|
||||
alice.visible_posts.should include(photo)
|
||||
alice.visible_posts(:type => 'StatusMessage').should_not include(photo)
|
||||
end
|
||||
|
||||
it "does not contain duplicate posts" do
|
||||
bobs_other_aspect = bob.aspects.create(:name => "cat people")
|
||||
bob.add_contact_to_aspect(bob.contact_for(alice.person), bobs_other_aspect)
|
||||
|
|
@ -59,16 +67,35 @@ describe User do
|
|||
alice.visible_posts.length.should == 1
|
||||
alice.visible_posts.should include(bobs_post)
|
||||
end
|
||||
|
||||
describe 'hidden posts' do
|
||||
before do
|
||||
aspect_to_post = bob.aspects.where(:name => "generic").first
|
||||
@status = bob.post(:status_message, :text=> "hello", :to => aspect_to_post)
|
||||
@vis = @status.post_visibilities.first
|
||||
end
|
||||
|
||||
it "pulls back non hidden posts" do
|
||||
alice.visible_posts.include?(@status).should be_true
|
||||
end
|
||||
|
||||
it "does not pull back hidden posts" do
|
||||
@vis.update_attributes(:hidden => true)
|
||||
alice.visible_posts.include?(@status).should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with many posts' do
|
||||
before do
|
||||
bob.move_contact(eve.person, @bobs_aspect, bob.aspects.create(:name => 'new aspect'))
|
||||
time_interval = 1000
|
||||
time_past = 1000000
|
||||
(1..25).each do |n|
|
||||
[alice, bob, eve].each do |u|
|
||||
aspect_to_post = u.aspects.where(:name => "generic").first
|
||||
post = u.post :status_message, :text => "#{u.username} - #{n}", :to => aspect_to_post.id
|
||||
post.created_at = post.created_at - time_interval
|
||||
post.updated_at = post.updated_at - time_interval
|
||||
post.created_at = (post.created_at-time_past) - time_interval
|
||||
post.updated_at = (post.updated_at-time_past) + time_interval
|
||||
post.save
|
||||
time_interval += 1000
|
||||
end
|
||||
|
|
@ -79,6 +106,14 @@ describe User do
|
|||
bob.visible_posts.should == bob.visible_posts(:by_members_of => bob.aspects.map { |a| a.id }) # it is the same when joining through aspects
|
||||
bob.visible_posts.sort_by { |p| p.updated_at }.map { |p| p.id }.should == bob.visible_posts.map { |p| p.id }.reverse #it is sorted updated_at desc by default
|
||||
|
||||
# It should respect the order option
|
||||
opts = {:order => 'created_at DESC'}
|
||||
bob.visible_posts(opts).first.created_at.should > bob.visible_posts(opts).last.created_at
|
||||
|
||||
# It should respect the order option
|
||||
opts = {:order => 'updated_at DESC'}
|
||||
bob.visible_posts(opts).first.updated_at.should > bob.visible_posts(opts).last.updated_at
|
||||
|
||||
# It should respect the limit option
|
||||
opts = {:limit => 40}
|
||||
bob.visible_posts(opts).length.should == 40
|
||||
|
|
|
|||
Loading…
Reference in a new issue