Add participation counter

This commit is contained in:
Marcelo Briones 2015-04-06 02:42:27 -03:00
parent 5fb328864e
commit 6636a89118
15 changed files with 175 additions and 54 deletions

View file

@ -22,7 +22,6 @@ class Comment < ActiveRecord::Base
belongs_to :commentable, :touch => true, :polymorphic => true belongs_to :commentable, :touch => true, :polymorphic => true
alias_attribute :post, :commentable alias_attribute :post, :commentable
belongs_to :author, :class_name => 'Person' belongs_to :author, :class_name => 'Person'
has_one :participation, :dependent => :destroy, :foreign_key => :target_id, :primary_key => :commentable_id
delegate :name, to: :author, prefix: true delegate :name, to: :author, prefix: true
delegate :comment_email_subject, to: :parent delegate :comment_email_subject, to: :parent
@ -48,6 +47,8 @@ class Comment < ActiveRecord::Base
after_destroy do after_destroy do
self.parent.update_comments_counter self.parent.update_comments_counter
participation = author.participations.where(target_id: post.id).first
participation.unparticipate! if participation.present?
end end
def diaspora_handle def diaspora_handle

View file

@ -12,7 +12,6 @@ class Like < Federated::Relayable
{:target => @target, :positive => true} {:target => @target, :positive => true}
end end
end end
has_one :participation, :dependent => :destroy, :foreign_key => :target_id, :primary_key => :target_id
after_commit :on => :create do after_commit :on => :create do
self.parent.update_likes_counter self.parent.update_likes_counter
@ -20,6 +19,8 @@ class Like < Federated::Relayable
after_destroy do after_destroy do
self.parent.update_likes_counter self.parent.update_likes_counter
participation = author.participations.where(target_id: target.id).first
participation.unparticipate! if participation.present?
end end
xml_attr :positive xml_attr :positive

View file

@ -9,6 +9,14 @@ class Participation < Federated::Relayable
end end
end end
def unparticipate!
if count == 1
destroy
else
update!(count: count.pred)
end
end
# NOTE API V1 to be extracted # NOTE API V1 to be extracted
acts_as_api acts_as_api
api_accessible :backbone do |t| api_accessible :backbone do |t|

View file

@ -379,7 +379,7 @@ class User < ActiveRecord::Base
if target.is_a?(Post) if target.is_a?(Post)
opts[:additional_subscribers] = target.resharers opts[:additional_subscribers] = target.resharers
opts[:services] = self.services opts[:services] = services
end end
mailman = Postzord::Dispatcher.build(self, retraction, opts) mailman = Postzord::Dispatcher.build(self, retraction, opts)

View file

@ -1,6 +1,6 @@
module User::SocialActions module User::SocialActions
def comment!(target, text, opts={}) def comment!(target, text, opts={})
find_or_create_participation!(target) update_or_create_participation!(target)
Comment::Generator.new(self, target, text).create!(opts) Comment::Generator.new(self, target, text).create!(opts)
end end
@ -9,17 +9,17 @@ module User::SocialActions
end end
def like!(target, opts={}) def like!(target, opts={})
find_or_create_participation!(target) update_or_create_participation!(target)
Like::Generator.new(self, target).create!(opts) Like::Generator.new(self, target).create!(opts)
end end
def participate_in_poll!(target, answer, opts={}) def participate_in_poll!(target, answer, opts={})
find_or_create_participation!(target) update_or_create_participation!(target)
PollParticipation::Generator.new(self, target, answer).create!(opts) PollParticipation::Generator.new(self, target, answer).create!(opts)
end end
def reshare!(target, opts={}) def reshare!(target, opts={})
find_or_create_participation!(target) update_or_create_participation!(target)
reshare = build_post(:reshare, :root_guid => target.guid) reshare = build_post(:reshare, :root_guid => target.guid)
reshare.save! reshare.save!
Postzord::Dispatcher.defer_build_and_post(self, reshare) Postzord::Dispatcher.defer_build_and_post(self, reshare)
@ -48,7 +48,12 @@ module User::SocialActions
) )
end end
def find_or_create_participation!(target) def update_or_create_participation!(target)
participations.where(:target_id => target).first || participate!(target) participation = participations.where(target_id: target).first
if participation.present?
participation.update!(count: participation.count.next)
else
participate!(target)
end
end end
end end

View file

@ -0,0 +1,29 @@
class ParticipationCounter < ActiveRecord::Migration
def up
add_column :participations, :count, :int, null: false, default: 1
posts_count = Post.select("COUNT(posts.id)")
.where("posts.id = participations.target_id")
.where("posts.author_id = participations.author_id")
.to_sql
likes_count = Like.select("COUNT(likes.id)")
.where("likes.target_id = participations.target_id")
.where("likes.author_id = participations.author_id")
.to_sql
comments_count = Comment.select("COUNT(comments.id)")
.where("comments.commentable_id = participations.target_id")
.where("comments.author_id = participations.author_id")
.to_sql
polls_count = PollParticipation.select("COUNT(*)")
.where("poll_participations.author_id = participations.author_id")
.joins(:poll)
.where("polls.status_message_id = participations.target_id")
.to_sql
Participation.update_all("count = (#{posts_count}) + (#{likes_count}) + (#{comments_count}) + (#{polls_count})")
Participation.where(count: 0).delete_all
end
def down
remove_column :participations, :count
end
end

View file

@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150403212139) do ActiveRecord::Schema.define(version: 20150404193023) do
create_table "account_deletions", force: :cascade do |t| create_table "account_deletions", force: :cascade do |t|
t.string "diaspora_handle", limit: 255 t.string "diaspora_handle", limit: 255
@ -260,6 +260,7 @@ ActiveRecord::Schema.define(version: 20150403212139) do
t.text "parent_author_signature", limit: 65535 t.text "parent_author_signature", limit: 65535
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.integer "count", limit: 4, default: 1, null: false
end end
add_index "participations", ["guid"], name: "index_participations_on_guid", length: {"guid"=>191}, using: :btree add_index "participations", ["guid"], name: "index_participations_on_guid", length: {"guid"=>191}, using: :btree

View file

@ -11,24 +11,26 @@ Feature: The activity stream
Scenario: Sorting Scenario: Sorting
When I sign in as "bob@bob.bob" When I sign in as "bob@bob.bob"
And I post "A- I like turtles" Given I expand the publisher
And I wait for 1 second When I write the status message "A- I like turtles"
And I post "B- barack obama is your new bicycle" And I submit the publisher
And I wait for 1 second
And I post "C- barack obama is a square" Given I expand the publisher
And I wait for 1 second When I write the status message "B- barack obama is your new bicycle"
And I submit the publisher
Given I expand the publisher
When I write the status message "C- barack obama is a square"
And I submit the publisher
When I go to the activity stream page When I go to the activity stream page
Then "C- barack obama is a square" should be post 1 Then "C- barack obama is a square" should be post 1
And "B- barack obama is your new bicycle" should be post 2 And "B- barack obama is your new bicycle" should be post 2
And "A- I like turtles" should be post 3 And "A- I like turtles" should be post 3
When I like the post "A- I like turtles" When I like the post "A- I like turtles" in the stream
And I wait for 1 second
And I comment "Sassy sawfish" on "C- barack obama is a square" And I comment "Sassy sawfish" on "C- barack obama is a square"
And I wait for 1 second And I like the post "B- barack obama is your new bicycle" in the stream
And I like the post "B- barack obama is your new bicycle"
And I wait for 1 second
When I go to the activity stream page When I go to the activity stream page
Then "B- barack obama is your new bicycle" should be post 1 Then "B- barack obama is your new bicycle" should be post 1
@ -43,17 +45,14 @@ Feature: The activity stream
And I fill in the following: And I fill in the following:
| text | is that a poodle? | | text | is that a poodle? |
And I press "Comment" And I press "Comment"
And I wait for the ajax to finish
When I go to the activity stream page When I go to the activity stream page
Then I should see "Look at this dog" Then I should see "Look at this dog"
And I should see "is that a poodle?" And I should see "is that a poodle?"
When I am on "alice@alice.alice"'s page When I am on "alice@alice.alice"'s page
And I hover over the ".comment"
And I preemptively confirm the alert
And I click to delete the first comment And I click to delete the first comment
And I wait for the ajax to finish And I confirm the alert
And I go to the activity stream page And I go to the activity stream page
Then I should not see "Look at this dog" Then I should not see "Look at this dog"
@ -63,12 +62,12 @@ Feature: The activity stream
And I am on "alice@alice.alice"'s page And I am on "alice@alice.alice"'s page
Then I should see "Look at this dog" Then I should see "Look at this dog"
When I like the post "Look at this dog" When I like the post "Look at this dog" in the stream
And I go to the activity stream page And I go to the activity stream page
Then I should see "Look at this dog" Then I should see "Look at this dog"
When I am on "alice@alice.alice"'s page When I am on "alice@alice.alice"'s page
And I unlike the post "Look at this dog" And I unlike the post "Look at this dog" in the stream
And I go to the activity stream page And I go to the activity stream page
Then I should not see "Look at this dog" Then I should not see "Look at this dog"
@ -77,7 +76,7 @@ Feature: The activity stream
And I am on "alice@alice.alice"'s page And I am on "alice@alice.alice"'s page
Then I should see "Look at this dog" Then I should see "Look at this dog"
When I like the post "Look at this dog" When I like the post "Look at this dog" in the stream
And I go to the activity stream page And I go to the activity stream page
Then I should see "Look at this dog" Then I should see "Look at this dog"
@ -88,21 +87,18 @@ Feature: The activity stream
And I fill in the following: And I fill in the following:
| text | is that a poodle? | | text | is that a poodle? |
And I press "Comment" And I press "Comment"
And I wait for the ajax to finish
And I go to the activity stream page And I go to the activity stream page
Then I should see "Look at this dog" Then I should see "Look at this dog"
When I am on "alice@alice.alice"'s page When I am on "alice@alice.alice"'s page
And I unlike the post "Look at this dog" And I unlike the post "Look at this dog" in the stream
And I go to the activity stream page And I go to the activity stream page
Then I should see "Look at this dog" Then I should see "Look at this dog"
When I am on "alice@alice.alice"'s page When I am on "alice@alice.alice"'s page
And I hover over the ".comment"
And I preemptively confirm the alert
And I click to delete the first comment And I click to delete the first comment
And I wait for the ajax to finish And I confirm the alert
And I go to the activity stream page And I go to the activity stream page
Then I should not see "Look at this dog" Then I should not see "Look at this dog"

View file

@ -6,10 +6,6 @@ Then /^I should see an image in the publisher$/ do
photo_in_publisher.should be_present photo_in_publisher.should be_present
end end
Then /^I (un)?like the post "([^"]*)"$/ do |negate, post_text|
negate ? unlike_post(post_text) : like_post(post_text)
end
Then /^"([^"]*)" should be post (\d+)$/ do |post_text, position| Then /^"([^"]*)" should be post (\d+)$/ do |post_text, position|
stream_element_numbers_content(position).should have_content(post_text) stream_element_numbers_content(position).should have_content(post_text)
end end

View file

@ -106,12 +106,6 @@ module PublishingCukeHelpers
end end
end end
def unlike_post(post_text)
within_post(post_text) do
find(:css, 'a.unlike').click
end
end
def stream_posts def stream_posts
all('.stream_element') all('.stream_element')
end end

View file

@ -158,6 +158,13 @@ describe StatusMessagesController, :type => :controller do
# response.body.should include('Status message requires a message or at least one photo') # response.body.should include('Status message requires a message or at least one photo')
# end # end
it "has one participation" do
post :create, status_message_hash
new_message = StatusMessage.find_by_text(status_message_hash[:status_message][:text])
expect(new_message.participations.count).to eq(1)
expect(new_message.participations.first.count).to eq(1)
end
context 'with photos' do context 'with photos' do
before do before do
@photo1 = alice.build_post(:photo, :pending => true, :user_file=> File.open(photo_fixture_name), :to => @aspect1.id) @photo1 = alice.build_post(:photo, :pending => true, :user_file=> File.open(photo_fixture_name), :to => @aspect1.id)

View file

@ -11,6 +11,23 @@ describe Comment, :type => :model do
@status = bob.post(:status_message, :text => "hello", :to => bob.aspects.first.id) @status = bob.post(:status_message, :text => "hello", :to => bob.aspects.first.id)
end end
describe "#destroy" do
before do
@comment = alice.comment!(@status, "why so formal?")
end
it "should delete a participation" do
expect { @comment.destroy }.to change { Participation.count }.by(-1)
end
it "should decrease count participation" do
alice.comment!(@status, "Are you there?")
@comment.destroy
participations = Participation.where(target_id: @comment.commentable_id, author_id: @comment.author_id)
expect(participations.first.count).to eq(1)
end
end
describe 'comment#notification_type' do describe 'comment#notification_type' do
let (:comment) { alice.comment!(@status, "why so formal?") } let (:comment) { alice.comment!(@status, "why so formal?") }
@ -60,6 +77,12 @@ describe Comment, :type => :model do
alice.comment!(@status, 'hello') alice.comment!(@status, 'hello')
}.to change { Comment.count }.by(1) }.to change { Comment.count }.by(1)
end end
it "should create a participation" do
comment = bob.comment!(@status, "sup dog")
participations = Participation.where(target_id: comment.commentable_id, author_id: comment.author_id)
expect(participations.count).to eq(1)
end
end end
describe 'counter cache' do describe 'counter cache' do

View file

@ -14,6 +14,23 @@ describe Like, :type => :model do
expect(FactoryGirl.build(:like)).to be_valid expect(FactoryGirl.build(:like)).to be_valid
end end
describe "#destroy" do
before do
@like = alice.like!(@status)
end
it "should delete a participation" do
expect { @like.destroy }.to change { Participation.count }.by(-1)
end
it "should decrease count participation" do
alice.comment!(@status, "Are you there?")
@like.destroy
participations = Participation.where(target_id: @like.target_id, author_id: @like.author_id)
expect(participations.first.count).to eq(1)
end
end
describe '#notification_type' do describe '#notification_type' do
before do before do
@like = alice.like!(@status) @like = alice.like!(@status)

View file

@ -20,4 +20,32 @@ describe Participation, :type => :model do
it_should_behave_like 'it is relayable' it_should_behave_like 'it is relayable'
end end
describe "#unparticipate" do
before do
@status = bob.post(:status_message, text: "hello", to: bob.aspects.first.id)
@like = alice.like!(@status)
end
it "retract participation" do
@like.author.participations.first.unparticipate!
participations = Participation.where(target_id: @like.target_id, author_id: @like.author_id)
expect(participations.count).to eq(0)
end
it "retract one of multiple participations" do
comment = alice.comment!(@status, "bro")
comment.author.participations.first.unparticipate!
participations = Participation.where(target_id: @like.target_id, author_id: @like.author_id)
expect(participations.count).to eq(1)
expect(participations.first.count).to eq(1)
end
it "retract all of multiple participations" do
alice.comment!(@status, "bro")
alice.participations.first.unparticipate!
alice.participations.first.unparticipate!
expect(Participation.where(target_id: @like.target_id, author_id: @like.author_id).count).to eq(0)
end
end
end end

View file

@ -11,9 +11,10 @@ describe User::SocialActions, :type => :model do
expect(alice.comment!(@status, "unicorn_mountain").text).to eq("unicorn_mountain") expect(alice.comment!(@status, "unicorn_mountain").text).to eq("unicorn_mountain")
end end
it "creates a partcipation" do it "creates a participation" do
expect{ alice.comment!(@status, "bro") }.to change(Participation, :count).by(1) expect{ alice.comment!(@status, "bro") }.to change(Participation, :count).by(1)
expect(alice.participations.last.target).to eq(@status) expect(alice.participations.last.target).to eq(@status)
expect(alice.participations.last.count).to eq(1)
end end
it "creates the comment" do it "creates the comment" do
@ -28,7 +29,7 @@ describe User::SocialActions, :type => :model do
end end
describe 'User#like!' do describe 'User#like!' do
it "creates a partcipation" do it "creates a participation" do
expect{ alice.like!(@status) }.to change(Participation, :count).by(1) expect{ alice.like!(@status) }.to change(Participation, :count).by(1)
expect(alice.participations.last.target).to eq(@status) expect(alice.participations.last.target).to eq(@status)
end end
@ -51,7 +52,7 @@ describe User::SocialActions, :type => :model do
@status = bob.post(:status_message, :text => "hello", :to => @bobs_aspect.id) @status = bob.post(:status_message, :text => "hello", :to => @bobs_aspect.id)
end end
it "creates a partcipation" do it "creates a participation" do
expect{ alice.like!(@status) }.to change(Participation, :count).by(1) expect{ alice.like!(@status) }.to change(Participation, :count).by(1)
end end
@ -110,4 +111,18 @@ describe User::SocialActions, :type => :model do
expect(alice.participate_in_poll!(@status, @answer).poll_answer).to eq(@answer) expect(alice.participate_in_poll!(@status, @answer).poll_answer).to eq(@answer)
end end
end end
describe "many actions" do
it "two coments" do
alice.comment!(@status, "bro...")
alice.comment!(@status, "...ther")
expect(alice.participations.last.count).to eq(2)
end
it "like and comment" do
alice.comment!(@status, "bro...")
alice.like!(@status)
expect(alice.participations.last.count).to eq(2)
end
end
end end