EvilQuery for Participation, refactor comment creation

you can now create a comment with User#comment!(post, text)
This commit is contained in:
danielgrippi 2012-02-01 15:09:22 -08:00
parent 7389b895d6
commit 02021584a7
23 changed files with 184 additions and 128 deletions

View file

@ -15,24 +15,14 @@ class CommentsController < ApplicationController
end
def create
target = current_user.find_visible_shareable_by_id(Post, params[:post_id])
text = params[:text]
post = current_user.find_visible_shareable_by_id(Post, params[:post_id])
@comment = current_user.comment!(post, params[:text]) if post
if target
@comment = current_user.build_comment(:text => text, :post => target)
if @comment.save
Rails.logger.info("event => :create, :type => :comment, :user => #{current_user.diaspora_handle},
:status => :success, :comment => #{@comment.id}, :chars => #{params[:text].length}")
current_user.dispatch_post(@comment)
respond_to do |format|
format.json{ render :json => @comment.as_api_response(:backbone), :status => 201 }
format.html{ render :nothing => true, :status => 201 }
format.mobile{ render :partial => 'comment', :locals => {:post => @comment.post, :comment => @comment} }
end
else
render :nothing => true, :status => 422
if @comment
respond_to do |format|
format.json{ render :json => @comment.as_api_response(:backbone), :status => 201 }
format.html{ render :nothing => true, :status => 201 }
format.mobile{ render :partial => 'comment', :locals => {:post => @comment.post, :comment => @comment} }
end
else
render :nothing => true, :status => 422
@ -61,20 +51,23 @@ class CommentsController < ApplicationController
end
def index
find_post
raise(ActiveRecord::RecordNotFound.new) unless @post
@comments = @post.comments.for_a_stream
respond_with do |format|
format.json { render :json => @comments.as_api_response(:backbone), :status => 200 }
format.mobile{render :layout => false}
end
end
protected
def find_post
if user_signed_in?
@post = current_user.find_visible_shareable_by_id(Post, params[:post_id])
else
@post = Post.find_by_id_and_public(params[:post_id], true)
end
if @post
@comments = @post.comments.for_a_stream
respond_with do |format|
format.json { render :json => @comments.as_api_response(:backbone), :status => 200 }
format.mobile{render :layout => false}
end
else
raise ActiveRecord::RecordNotFound.new
end
end
end

View file

@ -277,6 +277,16 @@ class User < ActiveRecord::Base
Salmon::EncryptedSlap.create_by_user_and_activity(self, post.to_diaspora_xml)
end
def comment!(post, text, opts={})
comment = build_comment(opts.merge!(:post => post, :text => text))
if comment.save
dispatch_post(comment)
comment
else
false
end
end
def build_relayable(model, options = {})
r = model.new(options.merge(:author_id => self.person.id))
r.set_guid

View file

@ -2,7 +2,45 @@ module EvilQuery
class Base
def fetch_ids!(relation, id_column)
#the relation should be ordered and limited by here
@class.connection.select_values(relation.select(id_column).to_sql)
@class.connection.select_values(id_sql(relation, id_column))
end
def id_sql(relation, id_column)
relation.select(id_column).to_sql
end
end
class Participation < Base
def initialize(user)
@user = user
@class = Post
end
def posts
liked_post_ids = fetch_ids!(LikedPosts.new(@user).posts, "posts.id")
commented_post_ids = fetch_ids!(CommentedPosts.new(@user).posts, "posts.id")
Post.where(:id => liked_post_ids + commented_post_ids)
end
end
class LikedPosts < Base
def initialize(user)
@user = user
end
def posts
StatusMessage.liked_by(@user.person)
end
end
class CommentedPosts < Base
def initialize(user)
@user = user
end
def posts
StatusMessage.commented_by(@user.person)
end
end

View file

@ -13,7 +13,7 @@ class Stream::Comments < Stream::Base
# @return [ActiveRecord::Association<Post>] AR association of posts
def posts
@posts ||= StatusMessage.commented_by(self.user.person)
@posts ||= EvilQuery::CommentedPosts.new(user).posts
end
def contacts_title

View file

@ -13,7 +13,7 @@ class Stream::Likes < Stream::Base
# @return [ActiveRecord::Association<Post>] AR association of posts
def posts
@posts ||= StatusMessage.liked_by(self.user.person)
@posts ||= EvilQuery::LikedPosts.new(user).posts
end
def contacts_title

View file

@ -27,6 +27,7 @@ describe CommentsController do
response.code.should == '201'
response.body.should match comment_hash[:text]
end
it 'responds to format mobile' do
post :create, comment_hash.merge(:format => 'mobile')
response.should be_success
@ -52,7 +53,7 @@ describe CommentsController do
end
it "doesn't overwrite id" do
old_comment = alice.comment("hello", :post => @post)
old_comment = alice.comment!(@post, "hello")
comment_hash[:id] = old_comment.id
post :create, comment_hash
old_comment.reload.text.should == 'hello'
@ -82,25 +83,25 @@ describe CommentsController do
end
it 'lets the user delete his comment' do
comment = bob.comment("hey", :post => @message)
comment = bob.comment!(@message, "hey")
bob.should_receive(:retract).with(comment)
delete :destroy, :format => "js", :post_id => 1, :id => comment.id
delete :destroy, :format => "js", :post_id => 1, :id => comment.id
response.status.should == 204
end
it "lets the user destroy other people's comments" do
comment = alice.comment("hey", :post => @message)
comment = alice.comment!(@message, "hey")
bob.should_receive(:retract).with(comment)
delete :destroy, :format => "js", :post_id => 1, :id => comment.id
delete :destroy, :format => "js", :post_id => 1, :id => comment.id
response.status.should == 204
end
end
context "another user's post" do
it 'let the user delete his comment' do
comment = alice.comment("hey", :post => @message)
comment = alice.comment!(@message, "hey")
alice.should_receive(:retract).with(comment)
delete :destroy, :format => "js", :post_id => 1, :id => comment.id
@ -108,8 +109,8 @@ describe CommentsController do
end
it 'does not let the user destroy comments he does not own' do
comment1 = bob.comment("hey", :post => @message)
comment2 = eve.comment("hey", :post => @message)
comment1 = bob.comment!(@message, "hey")
comment2 = eve.comment!(@message, "hey")
alice.should_not_receive(:retract).with(comment1)
delete :destroy, :format => "js", :post_id => 1, :id => comment2.id
@ -136,7 +137,7 @@ describe CommentsController do
end
it 'returns all the comments for a post' do
comments = [alice, bob, eve].map{ |u| u.comment("hey", :post => @message) }
comments = [alice, bob, eve].map{ |u| u.comment!(@message, "hey") }
get :index, :post_id => @message.id, :format => 'js'
assigns[:comments].should == comments
@ -146,11 +147,12 @@ describe CommentsController do
get :index, :post_id => 235236, :format => 'js'
response.status.should == 404
end
it 'returns a 404 on a post that is not visible to the signed in user' do
aspect_to_post = eve.aspects.where(:name => "generic").first
message = eve.post(:status_message, :text => "hey", :to => aspect_to_post.id)
bob.comment("hey", :post => @message)
get :index, :post_id => message.id, :format => 'js'
bob.comment!(@message, "hey")
get :index, :post_id => message.id, :format => :json
response.status.should == 404
end
end

View file

@ -38,7 +38,7 @@ describe StreamsController do
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) }
5.times { bob.comment!(message, "what") }
get :aspects
save_fixture(html_for("body"), "aspects_index_with_posts")
end
@ -53,7 +53,7 @@ describe StreamsController do
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) }
5.times { bob.comment!(message, "what") }
get :aspects
save_fixture(html_for("body"), "aspects_index_post_with_comments")
end

View file

@ -31,7 +31,7 @@ describe LikesController do
context "on my own post" do
it 'succeeds' do
@target = alice.post :status_message, :text => "AWESOME", :to => @alices_aspect.id
@target = alice.comment "hey", :post => @target if class_const == Comment
@target = alice.comment!(@target, "hey") if class_const == Comment
post :create, like_hash.merge(:format => :json)
response.code.should == '201'
end
@ -40,7 +40,7 @@ describe LikesController do
context "on a post from a contact" do
before do
@target = bob.post :status_message, :text => "AWESOME", :to => @bobs_aspect.id
@target = bob.comment "hey", :post => @target if class_const == Comment
@target = bob.comment!(@target, "hey") if class_const == Comment
end
it 'likes' do
@ -63,7 +63,7 @@ describe LikesController do
context "on a post from a stranger" do
before do
@target = eve.post :status_message, :text => "AWESOME", :to => eve.aspects.first.id
@target = eve.comment "hey", :post => @target if class_const == Comment
@target = eve.comment!(@target, "hey") if class_const == Comment
end
it "doesn't post" do
@ -77,7 +77,7 @@ describe LikesController do
describe '#index' do
before do
@message = alice.post(:status_message, :text => "hey", :to => @alices_aspect.id)
@message = alice.comment( "hey", :post => @message) if class_const == Comment
@message = alice.comment!(@message, "hey") if class_const == Comment
end
it 'generates a jasmine fixture', :fixture => true do
@ -108,7 +108,7 @@ describe LikesController do
describe '#destroy' do
before do
@message = bob.post(:status_message, :text => "hey", :to => @alices_aspect.id)
@message = bob.comment( "hey", :post => @message) if class_const == Comment
@message = bob.comment!(@message, "hey") if class_const == Comment
@like = alice.build_like(:positive => true, :target => @message)
@like.save
end

View file

@ -148,7 +148,7 @@ describe PeopleController do
end
@posts.each do |post|
@users.each do |user|
user.comment "yo#{post.text}", :post => post
user.comment!(post, "yo#{post.text}")
end
end
end
@ -222,7 +222,7 @@ describe PeopleController do
it "renders the comments on the user's posts" do
message = @user.post :status_message, :text => 'test more', :to => @aspect.id
@user.comment 'I mean it', :post => message
@user.comment!(message, 'I mean it')
get :show, :id => @user.person.to_param
response.should be_success
end

View file

@ -17,7 +17,7 @@ describe 'deleteing your account' do
#objects on post
@bob2.like(true, :target => @alices_post)
@bob2.comment("here are some thoughts on your post", :post => @alices_post)
@bob2.comment!(@alices_post, "here are some thoughts on your post")
#conversations
create_conversation_with_message(alice, @bob2.person, "Subject", "Hey @bob2")

View file

@ -4,16 +4,15 @@ describe "Dispatching" do
context "a comment retraction on a public post" do
it "should trigger a private dispatch" do
luke, leia, raph = set_up_friends
# Luke has a public post and comments on it
p = Factory(:status_message, :public => true, :author => luke.person)
c = luke.comment("awesomesauseum", :post => p)
post = Factory(:status_message, :public => true, :author => luke.person)
# Luke now retracts his comment
Postzord::Dispatcher::Public.should_not_receive(:new)
Postzord::Dispatcher::Private.should_receive(:new).and_return(stub(:post => true))
fantasy_resque do
luke.retract(c)
comment = luke.comment!(post, "awesomesauseum")
# Luke now retracts his comment
Postzord::Dispatcher::Public.should_not_receive(:new)
Postzord::Dispatcher::Private.should_receive(:new).and_return(stub(:post => true))
luke.retract(comment)
end
end
end

View file

@ -173,18 +173,20 @@ describe 'a user receives a post' do
context 'remote' do
before do
connect_users(alice, @alices_aspect, eve, @eves_aspect)
@post = alice.post(:status_message, :text => "hello", :to => @alices_aspect.id)
fantasy_resque do
connect_users(alice, @alices_aspect, eve, @eves_aspect)
@post = alice.post(:status_message, :text => "hello", :to => @alices_aspect.id)
xml = @post.to_diaspora_xml
xml = @post.to_diaspora_xml
receive_with_zord(bob, alice.person, xml)
receive_with_zord(eve, alice.person, xml)
receive_with_zord(bob, alice.person, xml)
receive_with_zord(eve, alice.person, xml)
comment = eve.comment('tada',:post => @post)
comment.parent_author_signature = comment.sign_with_key(alice.encryption_key)
@xml = comment.to_diaspora_xml
comment.delete
comment = eve.comment!(@post, 'tada')
comment.parent_author_signature = comment.sign_with_key(alice.encryption_key)
@xml = comment.to_diaspora_xml
comment.delete
end
end
it 'should correctly attach the user already on the pod' do
@ -235,12 +237,14 @@ describe 'a user receives a post' do
end
it 'does not raise a `Mysql2::Error: Duplicate entry...` exception on save' do
@comment = bob.comment('tada',:post => @post)
@xml = @comment.to_diaspora_xml
fantasy_resque do
@comment = bob.comment!(@post, 'tada')
@xml = @comment.to_diaspora_xml
lambda {
receive_with_zord(alice, bob.person, @xml)
}.should_not raise_exception
lambda {
receive_with_zord(alice, bob.person, @xml)
}.should_not raise_exception
end
end
end
end

View file

@ -0,0 +1,17 @@
require 'spec_helper'
describe EvilQuery::Participation do
before do
@status_message = Factory(:status_message, :author => bob.person)
end
it "includes posts liked by the user" do
Factory(:like, :target => @status_message, :author => alice.person)
EvilQuery::Participation.new(alice).posts.should include(@status_message)
end
it "includes posts commented by the user" do
alice.comment!(@status_message, "hey")
EvilQuery::Participation.new(alice).posts.should include(@status_message)
end
end

View file

@ -32,8 +32,8 @@ describe Statistics do
describe '#comments_count_sql' do
it "pulls back an array of post counts and ids" do
sm = Factory(:status_message, :author => alice.person)
bob.comment("sup", :post => sm)
status_message = Factory(:status_message, :author => alice.person)
bob.comment!(status_message, "sup")
result_should_equal User.connection.select_all(@stats.comments_count_sql)
end
end

View file

@ -215,7 +215,7 @@ describe Notifier do
context "comments" do
let(:commented_post) {bob.post(:status_message, :text => "It's really sunny outside today, and this is a super long status message! #notreally", :to => :all)}
let(:comment) { eve.comment("Totally is", :post => commented_post)}
let(:comment) { eve.comment!(commented_post, "Totally is")}
describe ".comment_on_post" do
let(:comment_mail) {Notifier.comment_on_post(bob.id, person.id, comment.id).deliver}

View file

@ -50,18 +50,6 @@ describe 'making sure the spec runner works' do
end
end
describe '#comment' do
it "should send a user's comment on a person's post to that person" do
person = Factory(:person)
person_status = Factory(:status_message, :author => person)
m = mock()
m.stub!(:post)
Postzord::Dispatcher.should_receive(:build).and_return(m)
alice.comment "yo", :post => person_status
end
end
describe '#post' do
it 'creates a notification with a mention' do
lambda{

View file

@ -13,19 +13,19 @@ describe Comment do
describe 'comment#notification_type' do
it "returns 'comment_on_post' if the comment is on a post you own" do
comment = alice.comment("why so formal?", :post => @status)
comment = alice.comment!(@status, "why so formal?")
comment.notification_type(bob, alice.person).should == Notifications::CommentOnPost
end
it 'returns false if the comment is not on a post you own and no one "also_commented"' do
comment = alice.comment("I simply felt like issuing a greeting. Do step off.", :post => @status)
comment = alice.comment!(@status, "I simply felt like issuing a greeting. Do step off.")
comment.notification_type(eve, alice.person).should be_false
end
context "also commented" do
before do
alice.comment("a-commenta commenta", :post => @status)
@comment = eve.comment("I also commented on the first user's post", :post => @status)
alice.comment!(@status, "a-commenta commenta")
@comment = eve.comment!(@status, "I also commented on the first user's post")
end
it 'does not return also commented if the user commented' do
@ -40,18 +40,18 @@ describe Comment do
describe 'User#comment' do
it "should be able to comment on one's own status" do
alice.comment("Yeah, it was great", :post => @status)
alice.comment!(@status, "Yeah, it was great")
@status.reload.comments.first.text.should == "Yeah, it was great"
end
it "should be able to comment on a contact's status" do
bob.comment("sup dog", :post => @status)
bob.comment!(@status, "sup dog")
@status.reload.comments.first.text.should == "sup dog"
end
it 'does not multi-post a comment' do
lambda {
alice.comment 'hello', :post => @status
alice.comment!(@status, 'hello')
}.should change { Comment.count }.by(1)
end
end
@ -59,7 +59,7 @@ describe Comment do
describe 'counter cache' do
it 'increments the counter cache on its post' do
lambda {
alice.comment("oh yeah", :post => @status)
alice.comment!(@status, "oh yeah")
}.should change{
@status.reload.comments_count
}.by(1)
@ -72,7 +72,7 @@ describe Comment do
@commenter_aspect = @commenter.aspects.create(:name => "bruisers")
connect_users(alice, @alices_aspect, @commenter, @commenter_aspect)
@post = alice.post :status_message, :text => "hello", :to => @alices_aspect.id
@comment = @commenter.comment "Fool!", :post => @post
@comment = @commenter.comment!(@post, "Fool!")
@xml = @comment.to_xml.to_s
end
@ -99,18 +99,17 @@ describe Comment do
end
end
describe 'it is relayable' do
before do
@local_luke, @local_leia, @remote_raphael = set_up_friends
@remote_parent = Factory(:status_message, :author => @remote_raphael)
@local_parent = @local_luke.post :status_message, :text => "hi", :to => @local_luke.aspects.first
@object_by_parent_author = @local_luke.comment("yo", :post => @local_parent)
@object_by_parent_author = @local_luke.comment!(@local_parent, "yo")
@object_by_recipient = @local_leia.build_comment(:text => "yo", :post => @local_parent)
@dup_object_by_parent_author = @object_by_parent_author.dup
@object_on_remote_parent = @local_luke.comment("Yeah, it was great", :post => @remote_parent)
@object_on_remote_parent = @local_luke.comment!(@remote_parent, "Yeah, it was great")
end
it_should_behave_like 'it is relayable'
end

View file

@ -113,8 +113,8 @@ describe Notification do
before do
@user3 = bob
@sm = @user3.post(:status_message, :text => "comment!", :to => :all)
Postzord::Receiver::Private.new(@user3, :person => @user2.person, :object => @user2.comment("hey", :post => @sm)).receive_object
Postzord::Receiver::Private.new(@user3, :person => @user.person, :object => @user.comment("hey", :post => @sm)).receive_object
Postzord::Receiver::Private.new(@user3, :person => @user2.person, :object => @user2.comment!(@sm, "hey")).receive_object
Postzord::Receiver::Private.new(@user3, :person => @user.person, :object => @user.comment!(@sm, "hey")).receive_object
end
it "updates the notification with a more people if one already exists" do
@ -122,7 +122,7 @@ describe Notification do
end
it 'handles double comments from the same person without raising' do
Postzord::Receiver::Private.new(@user3, :person => @user2.person, :object => @user2.comment("hey", :post => @sm)).receive_object
Postzord::Receiver::Private.new(@user3, :person => @user2.person, :object => @user2.comment!(@sm, "hey")).receive_object
Notification.where(:recipient_id => @user3.id, :target_type => @sm.class.base_class, :target_id => @sm.id).first.actors.count.should == 2
end
end

View file

@ -202,7 +202,7 @@ describe Photo do
context "commenting" do
it "accepts comments if there is no parent status message" do
proc{ @user.comment("big willy style", :post => @photo) }.should change(@photo.comments, :count).by(1)
proc{ @user.comment!(@photo, "big willy style") }.should change(@photo.comments, :count).by(1)
end
end

View file

@ -170,7 +170,7 @@ describe Post do
describe 'deletion' do
it 'should delete a posts comments on delete' do
post = Factory(:status_message, :author => @user.person)
@user.comment "hey", :post => post
@user.comment!(post, "hey")
post.destroy
Post.where(:id => post.id).empty?.should == true
Comment.where(:text => "hey").empty?.should == true

View file

@ -14,9 +14,10 @@ describe RelayableRetraction do
describe '#subscribers' do
before do
@comment= @local_luke.comment("yo", :post => @local_parent)
@comment= @local_luke.comment!(@local_parent, "yo")
@retraction= @local_luke.retract(@comment)
end
it 'delegates it to target' do
arg = mock()
@retraction.target.should_receive(:subscribers).with(arg)
@ -26,7 +27,7 @@ describe RelayableRetraction do
describe '#receive' do
it 'discards a retraction with a nil target' do
@comment= @local_luke.comment("yo", :post => @local_parent)
@comment= @local_luke.comment!(@local_parent, "yo")
@retraction= @local_luke.retract(@comment)
@retraction.instance_variable_set(:@target, nil)
@ -34,42 +35,49 @@ describe RelayableRetraction do
@retraction.should_not_receive(:perform)
@retraction.receive(@local_luke, @remote_raphael)
end
context 'from the downstream author' do
before do
@comment = @local_leia.comment("yo", :post => @local_parent)
@comment = @local_leia.comment!(@local_parent, "yo")
@retraction = @local_leia.retract(@comment)
@recipient = @local_luke
end
it 'signs' do
@retraction.should_receive(:sign_with_key) do |key|
key.to_s.should == @recipient.encryption_key.to_s
end
@retraction.receive(@recipient, @comment.author)
end
it 'dispatches' do
zord = mock()
zord.should_receive(:post)
Postzord::Dispatcher.should_receive(:build).with(@local_luke, @retraction).and_return zord
@retraction.receive(@recipient, @comment.author)
end
it 'performs' do
@retraction.should_receive(:perform).with(@local_luke)
@retraction.receive(@recipient, @comment.author)
end
end
context 'from the upstream owner' do
before do
@comment = @local_luke.comment("Yeah, it was great", :post => @remote_parent)
@comment = @local_luke.comment!(@remote_parent, "Yeah, it was great")
@retraction = RelayableRetraction.allocate
@retraction.sender = @remote_raphael
@retraction.target = @comment
@retraction.stub!(:parent_author_signature_valid?).and_return(true)
@recipient = @local_luke
end
it 'performs' do
@retraction.should_receive(:perform).with(@recipient)
@retraction.receive(@recipient, @remote_raphael)
end
it 'does not dispatch' do
Postzord::Dispatcher.should_not_receive(:build)
@retraction.receive(@recipient, @remote_raphael)
@ -79,37 +87,45 @@ describe RelayableRetraction do
describe 'xml' do
before do
@comment = @local_leia.comment("yo", :post => @local_parent)
@comment = @local_leia.comment!(@local_parent, "yo")
@retraction = RelayableRetraction.build(@local_leia, @comment)
@retraction.parent_author_signature = 'PARENTSIGNATURE'
@retraction.target_author_signature = 'TARGETSIGNATURE'
@xml = @retraction.to_xml.to_s
end
describe '#to_xml' do
it 'serializes target_guid' do
@xml.should include(@comment.guid)
end
it 'serializes target_type' do
@xml.should include(@comment.class.to_s)
end
it 'serializes sender_handle' do
@xml.should include(@local_leia.diaspora_handle)
end
it 'serializes signatures' do
@xml.should include('TARGETSIGNATURE')
@xml.should include('PARENTSIGNATURE')
end
end
describe '.from_xml' do
before do
@marshalled = RelayableRetraction.from_xml(@xml)
end
it 'marshals the target' do
@marshalled.target.should == @comment
end
it 'marshals the sender' do
@marshalled.sender.should == @local_leia.person
end
it 'marshals the signature' do
@marshalled.target_author_signature.should == 'TARGETSIGNATURE'
@marshalled.parent_author_signature.should == 'PARENTSIGNATURE'

View file

@ -1,9 +1,9 @@
class Object
def id
if self.class.ancestors.include?(ActiveRecord::Base)
super
else
raise "You are calling id on a non-ActiveRecord object. STOP IT."
end
end
end
#class Object
# def id
# if self.class.ancestors.include?(ActiveRecord::Base)
# super
# else
# raise "You are calling id on a non-ActiveRecord object. STOP IT."
# end
# end
#end

View file

@ -32,16 +32,6 @@ class User
end
end
def comment(text, options = {})
fantasy_resque do
c = build_comment(options.merge(:text => text))
if c.save!
Postzord::Dispatcher.build(self, c).post
end
c
end
end
def like(positive, options ={})
fantasy_resque do
l = build_like(options.merge(:positive => positive))