From 6636a8911859c8f1c20cd34810ea9a26efe2ef34 Mon Sep 17 00:00:00 2001 From: Marcelo Briones Date: Mon, 6 Apr 2015 02:42:27 -0300 Subject: [PATCH] Add participation counter --- app/models/comment.rb | 3 +- app/models/like.rb | 3 +- app/models/participation.rb | 8 ++++ app/models/user.rb | 8 ++-- app/models/user/social_actions.rb | 17 ++++--- .../20150404193023_participation_counter.rb | 29 ++++++++++++ db/schema.rb | 9 ++-- .../{ => desktop}/activity_stream.feature | 44 +++++++++---------- features/step_definitions/stream_steps.rb | 4 -- features/support/publishing_cuke_helpers.rb | 6 --- .../status_messages_controller_spec.rb | 7 +++ spec/models/comment_spec.rb | 23 ++++++++++ spec/models/like_spec.rb | 17 +++++++ spec/models/participation_spec.rb | 28 ++++++++++++ spec/models/user/social_actions_spec.rb | 23 ++++++++-- 15 files changed, 175 insertions(+), 54 deletions(-) create mode 100644 db/migrate/20150404193023_participation_counter.rb rename features/{ => desktop}/activity_stream.feature (75%) diff --git a/app/models/comment.rb b/app/models/comment.rb index 3eca8287d..f6cd6f353 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -22,7 +22,6 @@ class Comment < ActiveRecord::Base belongs_to :commentable, :touch => true, :polymorphic => true alias_attribute :post, :commentable 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 :comment_email_subject, to: :parent @@ -48,6 +47,8 @@ class Comment < ActiveRecord::Base after_destroy do self.parent.update_comments_counter + participation = author.participations.where(target_id: post.id).first + participation.unparticipate! if participation.present? end def diaspora_handle diff --git a/app/models/like.rb b/app/models/like.rb index 99061ba3f..946c1d2ba 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -12,7 +12,6 @@ class Like < Federated::Relayable {:target => @target, :positive => true} end end - has_one :participation, :dependent => :destroy, :foreign_key => :target_id, :primary_key => :target_id after_commit :on => :create do self.parent.update_likes_counter @@ -20,6 +19,8 @@ class Like < Federated::Relayable after_destroy do self.parent.update_likes_counter + participation = author.participations.where(target_id: target.id).first + participation.unparticipate! if participation.present? end xml_attr :positive diff --git a/app/models/participation.rb b/app/models/participation.rb index b36083428..7a1f0d180 100644 --- a/app/models/participation.rb +++ b/app/models/participation.rb @@ -9,6 +9,14 @@ class Participation < Federated::Relayable end end + def unparticipate! + if count == 1 + destroy + else + update!(count: count.pred) + end + end + # NOTE API V1 to be extracted acts_as_api api_accessible :backbone do |t| diff --git a/app/models/user.rb b/app/models/user.rb index de6cc79df..b318465a2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -377,10 +377,10 @@ class User < ActiveRecord::Base retraction = Retraction.for(target) end - if target.is_a?(Post) - opts[:additional_subscribers] = target.resharers - opts[:services] = self.services - end + if target.is_a?(Post) + opts[:additional_subscribers] = target.resharers + opts[:services] = services + end mailman = Postzord::Dispatcher.build(self, retraction, opts) mailman.post diff --git a/app/models/user/social_actions.rb b/app/models/user/social_actions.rb index ccc30b484..2b3ff4a4a 100644 --- a/app/models/user/social_actions.rb +++ b/app/models/user/social_actions.rb @@ -1,6 +1,6 @@ module User::SocialActions def comment!(target, text, opts={}) - find_or_create_participation!(target) + update_or_create_participation!(target) Comment::Generator.new(self, target, text).create!(opts) end @@ -9,17 +9,17 @@ module User::SocialActions end def like!(target, opts={}) - find_or_create_participation!(target) + update_or_create_participation!(target) Like::Generator.new(self, target).create!(opts) end 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) end def reshare!(target, opts={}) - find_or_create_participation!(target) + update_or_create_participation!(target) reshare = build_post(:reshare, :root_guid => target.guid) reshare.save! Postzord::Dispatcher.defer_build_and_post(self, reshare) @@ -48,7 +48,12 @@ module User::SocialActions ) end - def find_or_create_participation!(target) - participations.where(:target_id => target).first || participate!(target) + def update_or_create_participation!(target) + participation = participations.where(target_id: target).first + if participation.present? + participation.update!(count: participation.count.next) + else + participate!(target) + end end end diff --git a/db/migrate/20150404193023_participation_counter.rb b/db/migrate/20150404193023_participation_counter.rb new file mode 100644 index 000000000..a13d075b8 --- /dev/null +++ b/db/migrate/20150404193023_participation_counter.rb @@ -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 diff --git a/db/schema.rb b/db/schema.rb index 07f4db16c..40ffa940f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # 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| t.string "diaspora_handle", limit: 255 @@ -254,12 +254,13 @@ ActiveRecord::Schema.define(version: 20150403212139) do create_table "participations", force: :cascade do |t| t.string "guid", limit: 255 t.integer "target_id", limit: 4 - t.string "target_type", limit: 60, null: false + t.string "target_type", limit: 60, null: false t.integer "author_id", limit: 4 t.text "author_signature", limit: 65535 t.text "parent_author_signature", limit: 65535 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "count", limit: 4, default: 1, null: false end add_index "participations", ["guid"], name: "index_participations_on_guid", length: {"guid"=>191}, using: :btree diff --git a/features/activity_stream.feature b/features/desktop/activity_stream.feature similarity index 75% rename from features/activity_stream.feature rename to features/desktop/activity_stream.feature index 345cee9e7..81d0d72f8 100644 --- a/features/activity_stream.feature +++ b/features/desktop/activity_stream.feature @@ -11,25 +11,27 @@ Feature: The activity stream Scenario: Sorting When I sign in as "bob@bob.bob" - And I post "A- I like turtles" - And I wait for 1 second - And I post "B- barack obama is your new bicycle" - And I wait for 1 second - And I post "C- barack obama is a square" - And I wait for 1 second + Given I expand the publisher + When I write the status message "A- I like turtles" + And I submit the publisher + + Given I expand the publisher + 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 Then "C- barack obama is a square" should be post 1 And "B- barack obama is your new bicycle" should be post 2 And "A- I like turtles" should be post 3 - When I like the post "A- I like turtles" - And I wait for 1 second + When I like the post "A- I like turtles" in the stream 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" - And I wait for 1 second - + And I like the post "B- barack obama is your new bicycle" in the stream + When I go to the activity stream page Then "B- barack obama is your new bicycle" should be post 1 And "C- barack obama is a square" should be post 2 @@ -43,17 +45,14 @@ Feature: The activity stream And I fill in the following: | text | is that a poodle? | And I press "Comment" - And I wait for the ajax to finish When I go to the activity stream page Then I should see "Look at this dog" And I should see "is that a poodle?" 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 wait for the ajax to finish + And I confirm the alert And I go to the activity stream page 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 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 Then I should see "Look at this dog" 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 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 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 Then I should see "Look at this dog" @@ -88,21 +87,18 @@ Feature: The activity stream And I fill in the following: | text | is that a poodle? | And I press "Comment" - And I wait for the ajax to finish And I go to the activity stream page Then I should see "Look at this dog" 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 Then I should see "Look at this dog" 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 wait for the ajax to finish + And I confirm the alert And I go to the activity stream page Then I should not see "Look at this dog" diff --git a/features/step_definitions/stream_steps.rb b/features/step_definitions/stream_steps.rb index f93fc4f6a..9e38191e5 100644 --- a/features/step_definitions/stream_steps.rb +++ b/features/step_definitions/stream_steps.rb @@ -6,10 +6,6 @@ Then /^I should see an image in the publisher$/ do photo_in_publisher.should be_present 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| stream_element_numbers_content(position).should have_content(post_text) end diff --git a/features/support/publishing_cuke_helpers.rb b/features/support/publishing_cuke_helpers.rb index 6d7ea8d25..c76c46f68 100644 --- a/features/support/publishing_cuke_helpers.rb +++ b/features/support/publishing_cuke_helpers.rb @@ -106,12 +106,6 @@ module PublishingCukeHelpers end end - def unlike_post(post_text) - within_post(post_text) do - find(:css, 'a.unlike').click - end - end - def stream_posts all('.stream_element') end diff --git a/spec/controllers/status_messages_controller_spec.rb b/spec/controllers/status_messages_controller_spec.rb index 24b8787b8..5fc8ea7ea 100644 --- a/spec/controllers/status_messages_controller_spec.rb +++ b/spec/controllers/status_messages_controller_spec.rb @@ -158,6 +158,13 @@ describe StatusMessagesController, :type => :controller do # response.body.should include('Status message requires a message or at least one photo') # 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 before do @photo1 = alice.build_post(:photo, :pending => true, :user_file=> File.open(photo_fixture_name), :to => @aspect1.id) diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index 2e2a91205..33dc00e6f 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -11,6 +11,23 @@ describe Comment, :type => :model do @status = bob.post(:status_message, :text => "hello", :to => bob.aspects.first.id) 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 let (:comment) { alice.comment!(@status, "why so formal?") } @@ -60,6 +77,12 @@ describe Comment, :type => :model do alice.comment!(@status, 'hello') }.to change { Comment.count }.by(1) 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 describe 'counter cache' do diff --git a/spec/models/like_spec.rb b/spec/models/like_spec.rb index 11532bff6..9015d09d3 100644 --- a/spec/models/like_spec.rb +++ b/spec/models/like_spec.rb @@ -14,6 +14,23 @@ describe Like, :type => :model do expect(FactoryGirl.build(:like)).to be_valid 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 before do @like = alice.like!(@status) diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb index 658c0cc8e..5c681185d 100644 --- a/spec/models/participation_spec.rb +++ b/spec/models/participation_spec.rb @@ -20,4 +20,32 @@ describe Participation, :type => :model do it_should_behave_like 'it is relayable' 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 diff --git a/spec/models/user/social_actions_spec.rb b/spec/models/user/social_actions_spec.rb index 74a67608b..ed3041b0f 100644 --- a/spec/models/user/social_actions_spec.rb +++ b/spec/models/user/social_actions_spec.rb @@ -11,9 +11,10 @@ describe User::SocialActions, :type => :model do expect(alice.comment!(@status, "unicorn_mountain").text).to eq("unicorn_mountain") end - it "creates a partcipation" do + it "creates a participation" do expect{ alice.comment!(@status, "bro") }.to change(Participation, :count).by(1) expect(alice.participations.last.target).to eq(@status) + expect(alice.participations.last.count).to eq(1) end it "creates the comment" do @@ -28,7 +29,7 @@ describe User::SocialActions, :type => :model do end 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.participations.last.target).to eq(@status) end @@ -51,7 +52,7 @@ describe User::SocialActions, :type => :model do @status = bob.post(:status_message, :text => "hello", :to => @bobs_aspect.id) end - it "creates a partcipation" do + it "creates a participation" do expect{ alice.like!(@status) }.to change(Participation, :count).by(1) end @@ -110,4 +111,18 @@ describe User::SocialActions, :type => :model do expect(alice.participate_in_poll!(@status, @answer).poll_answer).to eq(@answer) end end -end \ No newline at end of file + + 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