From d324c061b384b35f803d2e360c4c5562c8c7d48c Mon Sep 17 00:00:00 2001 From: kikito Date: Tue, 15 Sep 2015 15:28:14 +0200 Subject: [PATCH 1/8] Refactors shared/_tags view to always take a param called taggable --- app/views/debates/_debate.html.erb | 2 +- app/views/debates/show.html.erb | 2 +- app/views/proposals/_proposal.html.erb | 2 +- app/views/proposals/show.html.erb | 2 +- app/views/shared/_tags.html.erb | 1 - 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/views/debates/_debate.html.erb b/app/views/debates/_debate.html.erb index e766f5c34..e42225a9f 100644 --- a/app/views/debates/_debate.html.erb +++ b/app/views/debates/_debate.html.erb @@ -18,7 +18,7 @@ <%= link_to debate.description, debate %>
- <%= render "shared/tags", debate: debate, limit: 5 %> + <%= render "shared/tags", taggable: debate, limit: 5 %> diff --git a/app/views/debates/show.html.erb b/app/views/debates/show.html.erb index 70f78ccf1..9d426bd5f 100644 --- a/app/views/debates/show.html.erb +++ b/app/views/debates/show.html.erb @@ -59,7 +59,7 @@ <%= safe_html_with_links @debate.description %> - <%= render 'shared/tags', debate: @debate %> + <%= render 'shared/tags', taggable: @debate %>
<%= render 'actions', debate: @debate %> diff --git a/app/views/proposals/_proposal.html.erb b/app/views/proposals/_proposal.html.erb index 21cb53f5b..840a938df 100644 --- a/app/views/proposals/_proposal.html.erb +++ b/app/views/proposals/_proposal.html.erb @@ -17,7 +17,7 @@ <%= link_to proposal.summary, proposal %>
- <%= render "shared/tags", proposal: proposal, limit: 5 %> + <%= render "shared/tags", taggable: proposal, limit: 5 %> diff --git a/app/views/proposals/show.html.erb b/app/views/proposals/show.html.erb index c85f3d2d9..152712b50 100644 --- a/app/views/proposals/show.html.erb +++ b/app/views/proposals/show.html.erb @@ -74,7 +74,7 @@

<%= @proposal.question %>

- <%= render 'shared/tags', proposal: @proposal %> + <%= render 'shared/tags', taggable: @proposal %>
<%= render 'actions', proposal: @proposal %> diff --git a/app/views/shared/_tags.html.erb b/app/views/shared/_tags.html.erb index 6c6d87096..17f158d8a 100644 --- a/app/views/shared/_tags.html.erb +++ b/app/views/shared/_tags.html.erb @@ -1,5 +1,4 @@ <%- limit ||= nil %> -<%- taggable = defined?(debate) ? debate : proposal %> <% if taggable.tags.any? %> From a00d895fe8a9a81f307e7a3281bccb51d3c270be Mon Sep 17 00:00:00 2001 From: kikito Date: Tue, 15 Sep 2015 15:28:43 +0200 Subject: [PATCH 2/8] Fixes randomly failing dev_seed errors due to long titles --- db/dev_seeds.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/dev_seeds.rb b/db/dev_seeds.rb index b9232f055..ec6731239 100644 --- a/db/dev_seeds.rb +++ b/db/dev_seeds.rb @@ -68,7 +68,7 @@ tags = Faker::Lorem.words(25) author = User.reorder("RANDOM()").first description = "

#{Faker::Lorem.paragraphs.join('

')}

" debate = Debate.create!(author: author, - title: Faker::Lorem.sentence(3), + title: Faker::Lorem.sentence(3).truncate(60), created_at: rand((Time.now - 1.week) .. Time.now), description: description, tag_list: tags.sample(3).join(','), @@ -84,7 +84,7 @@ tags = Faker::Lorem.words(25) author = User.reorder("RANDOM()").first description = "

#{Faker::Lorem.paragraphs.join('

')}

" proposal = Proposal.create!(author: author, - title: Faker::Lorem.sentence(3), + title: Faker::Lorem.sentence(3).truncate(60), question: Faker::Lorem.sentence(3), summary: Faker::Lorem.sentence(3), responsible_name: Faker::Name.name, From 7dbda5d74707f3b6c5d9ced1cb175e38a2a7959e Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 17 Sep 2015 12:28:51 +0200 Subject: [PATCH 3/8] adds custom counters to tags via a migration --- .../20150917102718_add_extra_counters_to_tags.rb | 8 ++++++++ db/schema.rb | 12 ++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20150917102718_add_extra_counters_to_tags.rb diff --git a/db/migrate/20150917102718_add_extra_counters_to_tags.rb b/db/migrate/20150917102718_add_extra_counters_to_tags.rb new file mode 100644 index 000000000..974784772 --- /dev/null +++ b/db/migrate/20150917102718_add_extra_counters_to_tags.rb @@ -0,0 +1,8 @@ +class AddExtraCountersToTags < ActiveRecord::Migration + def change + add_column :tags, :debates_count, :integer, default: 0 + add_column :tags, :proposals_count, :integer, default: 0 + add_index :tags, :debates_count + add_index :tags, :proposals_count + end +end diff --git a/db/schema.rb b/db/schema.rb index 395b773f6..741f82940 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: 20150914191003) do +ActiveRecord::Schema.define(version: 20150917102718) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -253,12 +253,16 @@ ActiveRecord::Schema.define(version: 20150914191003) do add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "tags", force: :cascade do |t| - t.string "name", limit: 40 - t.integer "taggings_count", default: 0 - t.boolean "featured", default: false + t.string "name", limit: 40 + t.integer "taggings_count", default: 0 + t.boolean "featured", default: false + t.integer "debates_count", default: 0 + t.integer "proposals_count", default: 0 end + add_index "tags", ["debates_count"], name: "index_tags_on_debates_count", using: :btree add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree + add_index "tags", ["proposals_count"], name: "index_tags_on_proposals_count", using: :btree create_table "users", force: :cascade do |t| t.string "email", default: "", null: false From fedefa215f2744b6a0f27242bbc7b7d1f2f5022c Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 17 Sep 2015 12:50:14 +0200 Subject: [PATCH 4/8] Increases/decreases custom counters when taggings are created/destroyed --- config/initializers/acts_as_taggable_on.rb | 20 ++++++++- spec/lib/acts_as_taggable_on_spec.rb | 47 ++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 spec/lib/acts_as_taggable_on_spec.rb diff --git a/config/initializers/acts_as_taggable_on.rb b/config/initializers/acts_as_taggable_on.rb index 7ad1bbb22..ddf49482f 100644 --- a/config/initializers/acts_as_taggable_on.rb +++ b/config/initializers/acts_as_taggable_on.rb @@ -1,8 +1,24 @@ ActsAsTaggableOn::Tagging.class_eval do - after_destroy :touch_taggable + after_create :increase_custom_counter + after_destroy :touch_taggable, :decrease_custom_counter def touch_taggable taggable.touch if taggable.present? end -end \ No newline at end of file + def increase_custom_counter + ActsAsTaggableOn::Tag.increment_counter(custom_counter_field_name, tag_id) + end + + def decrease_custom_counter + ActsAsTaggableOn::Tag.decrement_counter(custom_counter_field_name, tag_id) + end + + private + + def custom_counter_field_name + "#{self.taggable_type.underscore.pluralize}_count" + end + + +end diff --git a/spec/lib/acts_as_taggable_on_spec.rb b/spec/lib/acts_as_taggable_on_spec.rb new file mode 100644 index 000000000..f050da674 --- /dev/null +++ b/spec/lib/acts_as_taggable_on_spec.rb @@ -0,0 +1,47 @@ +require 'rails_helper' + +describe 'ActsAsTaggableOn::Tagging' do + + describe "when tagging debates or proposals" do + let(:proposal) { create(:proposal) } + let(:debate) { create(:debate) } + + it "increases and decreases the tag's custom counters" do + tag = ActsAsTaggableOn::Tag.create(name: "foo") + + expect(tag.debates_count).to eq(0) + expect(tag.proposals_count).to eq(0) + + proposal.tag_list.add("foo") + proposal.save + tag.reload + + expect(tag.debates_count).to eq(0) + expect(tag.proposals_count).to eq(1) + + debate.tag_list.add("foo") + debate.save + tag.reload + + expect(tag.debates_count).to eq(1) + expect(tag.proposals_count).to eq(1) + + proposal.tag_list.remove("foo") + proposal.save + tag.reload + + expect(tag.debates_count).to eq(1) + expect(tag.proposals_count).to eq(0) + + debate.tag_list.remove("foo") + debate.save + tag.reload + + expect(tag.debates_count).to eq(0) + expect(tag.proposals_count).to eq(0) + end + + end + + +end From d71a4046613598701ee651a3ed04287868ec6479 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 17 Sep 2015 16:53:21 +0200 Subject: [PATCH 5/8] Adds more methods to ActsAsTaggableOn --- config/initializers/acts_as_taggable_on.rb | 55 ++++++++++----- spec/lib/acts_as_taggable_on_spec.rb | 81 ++++++++++++++-------- 2 files changed, 90 insertions(+), 46 deletions(-) diff --git a/config/initializers/acts_as_taggable_on.rb b/config/initializers/acts_as_taggable_on.rb index ddf49482f..829b43725 100644 --- a/config/initializers/acts_as_taggable_on.rb +++ b/config/initializers/acts_as_taggable_on.rb @@ -1,24 +1,43 @@ -ActsAsTaggableOn::Tagging.class_eval do - after_create :increase_custom_counter - after_destroy :touch_taggable, :decrease_custom_counter +module ActsAsTaggableOn - def touch_taggable - taggable.touch if taggable.present? + Tagging.class_eval do + + after_create :increment_tag_custom_counter + after_destroy :touch_taggable, :decrement_tag_custom_counter + + def touch_taggable + taggable.touch if taggable.present? + end + + def increment_tag_custom_counter + tag.increment_custom_counter_for(taggable_type) + end + + def decrement_tag_custom_counter + tag.decrement_custom_counter_for(taggable_type) + end end - def increase_custom_counter - ActsAsTaggableOn::Tag.increment_counter(custom_counter_field_name, tag_id) + Tag.class_eval do + + def increment_custom_counter_for(taggable_type) + Tag.increment_counter(custom_counter_field_name_for(taggable_type), id) + end + + def decrement_custom_counter_for(taggable_type) + Tag.decrement_counter(custom_counter_field_name_for(taggable_type), id) + end + + def recalculate_custom_counter_for(taggable_type) + visible_taggables = taggable_type.constantize.includes(:taggings).where('taggings.taggable_type' => taggable_type, 'taggings.tag_id' => id) + + update(custom_counter_field_name_for(taggable_type) => visible_taggables.count) + end + + private + def custom_counter_field_name_for(taggable_type) + "#{taggable_type.underscore.pluralize}_count" + end end - def decrease_custom_counter - ActsAsTaggableOn::Tag.decrement_counter(custom_counter_field_name, tag_id) - end - - private - - def custom_counter_field_name - "#{self.taggable_type.underscore.pluralize}_count" - end - - end diff --git a/spec/lib/acts_as_taggable_on_spec.rb b/spec/lib/acts_as_taggable_on_spec.rb index f050da674..2362e8745 100644 --- a/spec/lib/acts_as_taggable_on_spec.rb +++ b/spec/lib/acts_as_taggable_on_spec.rb @@ -1,47 +1,72 @@ require 'rails_helper' -describe 'ActsAsTaggableOn::Tagging' do +describe 'ActsAsTaggableOn' do - describe "when tagging debates or proposals" do - let(:proposal) { create(:proposal) } - let(:debate) { create(:debate) } + describe 'Tagging' do + describe "when tagging debates or proposals" do + let(:proposal) { create(:proposal) } + let(:debate) { create(:debate) } - it "increases and decreases the tag's custom counters" do - tag = ActsAsTaggableOn::Tag.create(name: "foo") + it "increases and decreases the tag's custom counters" do + tag = ActsAsTaggableOn::Tag.create(name: "foo") - expect(tag.debates_count).to eq(0) - expect(tag.proposals_count).to eq(0) + expect(tag.debates_count).to eq(0) + expect(tag.proposals_count).to eq(0) - proposal.tag_list.add("foo") - proposal.save - tag.reload + proposal.tag_list.add("foo") + proposal.save + tag.reload - expect(tag.debates_count).to eq(0) - expect(tag.proposals_count).to eq(1) + expect(tag.debates_count).to eq(0) + expect(tag.proposals_count).to eq(1) - debate.tag_list.add("foo") - debate.save - tag.reload + debate.tag_list.add("foo") + debate.save + tag.reload - expect(tag.debates_count).to eq(1) - expect(tag.proposals_count).to eq(1) + expect(tag.debates_count).to eq(1) + expect(tag.proposals_count).to eq(1) - proposal.tag_list.remove("foo") - proposal.save - tag.reload + proposal.tag_list.remove("foo") + proposal.save + tag.reload - expect(tag.debates_count).to eq(1) - expect(tag.proposals_count).to eq(0) + expect(tag.debates_count).to eq(1) + expect(tag.proposals_count).to eq(0) - debate.tag_list.remove("foo") - debate.save - tag.reload + debate.tag_list.remove("foo") + debate.save + tag.reload - expect(tag.debates_count).to eq(0) - expect(tag.proposals_count).to eq(0) + expect(tag.debates_count).to eq(0) + expect(tag.proposals_count).to eq(0) + end end + end + describe 'Tag' do + describe "#recalculate_custom_counter_for" do + it "updates the counters of proposals and debates, taking into account hidden ones" do + tag = ActsAsTaggableOn::Tag.create(name: "foo") + + create(:proposal, tag_list: "foo") + create(:proposal, :hidden, tag_list: "foo") + + create(:debate, tag_list: "foo") + create(:debate, :hidden, tag_list: "foo") + + tag.update(debates_count: 0, proposals_count: 0) + + tag.recalculate_custom_counter_for('Debate') + expect(tag.debates_count).to eq(1) + + tag.recalculate_custom_counter_for('Proposal') + expect(tag.proposals_count).to eq(1) + end + end end + + end From 8ff8a1df7f0eb973f17486540fe216894aef13ab Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 17 Sep 2015 16:54:10 +0200 Subject: [PATCH 6/8] Makes tags count only visible debates and proposals --- app/models/debate.rb | 8 ++++++++ app/models/proposal.rb | 8 ++++++++ lib/acts_as_paranoid_aliases.rb | 10 ++++++++-- spec/models/debate_spec.rb | 14 ++++++++++++++ spec/models/proposal_spec.rb | 14 ++++++++++++++ 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/app/models/debate.rb b/app/models/debate.rb index d141e4330..f2723a864 100644 --- a/app/models/debate.rb +++ b/app/models/debate.rb @@ -122,6 +122,14 @@ class Debate < ActiveRecord::Base cached_votes_up/flags_count.to_f < 5 end + def after_hide + self.tags.each{ |t| t.decrement_custom_counter_for('Debate') } + end + + def after_restore + self.tags.each{ |t| t.increment_custom_counter_for('Debate') } + end + def self.title_max_length @@title_max_length ||= self.columns.find { |c| c.name == 'title' }.limit || 80 end diff --git a/app/models/proposal.rb b/app/models/proposal.rb index 24045e8f7..5d21be8d2 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -105,6 +105,14 @@ class Proposal < ActiveRecord::Base cached_votes_up) end + def after_hide + self.tags.each{ |t| t.decrement_custom_counter_for('Proposal') } + end + + def after_restore + self.tags.each{ |t| t.increment_custom_counter_for('Proposal') } + end + def self.title_max_length @@title_max_length ||= self.columns.find { |c| c.name == 'title' }.limit || 80 end diff --git a/lib/acts_as_paranoid_aliases.rb b/lib/acts_as_paranoid_aliases.rb index 6fd875ef6..7f2cfe4bd 100644 --- a/lib/acts_as_paranoid_aliases.rb +++ b/lib/acts_as_paranoid_aliases.rb @@ -4,6 +4,7 @@ module ActsAsParanoidAliases base.extend(ClassMethods) def hide + return false if hidden? update_attribute(:hidden_at, Time.now) after_hide end @@ -24,8 +25,13 @@ module ActsAsParanoidAliases end def restore(opts={}) + return false unless hidden? super(opts) update_attribute(:confirmed_hide_at, nil) + after_restore + end + + def after_restore end end @@ -48,12 +54,12 @@ module ActsAsParanoidAliases def hide_all(ids) return if ids.blank? - where(id: ids).update_all(hidden_at: Time.now) + where(id: ids).each(&:hide) end def restore_all(ids) return if ids.blank? - only_hidden.where(id: ids).update_all(hidden_at: nil) + only_hidden.where(id: ids).each(&:restore) end end end diff --git a/spec/models/debate_spec.rb b/spec/models/debate_spec.rb index 907bcef5b..1ab7b8a06 100644 --- a/spec/models/debate_spec.rb +++ b/spec/models/debate_spec.rb @@ -350,6 +350,20 @@ describe Debate do end end + describe "custom tag counters when hiding/restoring" do + it "decreases the tag counter when hiden, and increases it when restored" do + debate = create(:debate, tag_list: "foo") + tag = ActsAsTaggableOn::Tag.where(name: 'foo').first + expect(tag.debates_count).to eq(1) + + debate.hide + expect(tag.reload.debates_count).to eq(0) + + debate.restore + expect(tag.reload.debates_count).to eq(1) + end + end + describe "conflictive debates" do it "should return true when it has more than 1 flag for 5 positive votes" do diff --git a/spec/models/proposal_spec.rb b/spec/models/proposal_spec.rb index f713f3ce5..ff200fca9 100644 --- a/spec/models/proposal_spec.rb +++ b/spec/models/proposal_spec.rb @@ -193,6 +193,20 @@ describe Proposal do end end + describe "custom tag counters when hiding/restoring" do + it "decreases the tag counter when hiden, and increases it when restored" do + proposal = create(:proposal, tag_list: "foo") + tag = ActsAsTaggableOn::Tag.where(name: 'foo').first + expect(tag.proposals_count).to eq(1) + + proposal.hide + expect(tag.reload.proposals_count).to eq(0) + + proposal.restore + expect(tag.reload.proposals_count).to eq(1) + end + end + describe "#confidence_score" do it "takes into account votes" do From f9101b4970d5a7314ea6cf02e6515fe2614892b5 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 17 Sep 2015 16:55:16 +0200 Subject: [PATCH 7/8] Implements tag_cloud using the new custom counters --- app/controllers/debates_controller.rb | 2 +- app/controllers/proposals_controller.rb | 2 +- app/helpers/tags_helper.rb | 18 ++++++++++++++++-- app/views/shared/_tag_cloud.html.erb | 5 ++++- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/controllers/debates_controller.rb b/app/controllers/debates_controller.rb index 618152fab..07099b556 100644 --- a/app/controllers/debates_controller.rb +++ b/app/controllers/debates_controller.rb @@ -12,7 +12,7 @@ class DebatesController < ApplicationController @debates = @search_terms.present? ? Debate.search(@search_terms) : Debate.all @debates = @debates.tagged_with(@tag_filter) if @tag_filter @debates = @debates.page(params[:page]).for_render.send("sort_by_#{@current_order}") - @tag_cloud = Debate.tag_counts.order(taggings_count: :desc, name: :asc).limit(20) + @tag_cloud = Debate.tag_counts.order(debates_count: :desc, name: :asc).limit(20) set_debate_votes(@debates) end diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb index 1f1a66241..55d6fab0e 100644 --- a/app/controllers/proposals_controller.rb +++ b/app/controllers/proposals_controller.rb @@ -11,7 +11,7 @@ class ProposalsController < ApplicationController @proposals = @search_terms.present? ? Proposal.search(@search_terms) : Proposal.all @proposals = @proposals.tagged_with(@tag_filter) if @tag_filter @proposals = @proposals.page(params[:page]).for_render.send("sort_by_#{@current_order}") - @tag_cloud = Proposal.tag_counts.order(taggings_count: :desc, name: :asc).limit(20) + @tag_cloud = Proposal.tag_counts.order(proposals_count: :desc, name: :asc).limit(20) set_proposal_votes(@proposals) end diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb index b8476ac0a..59bc56bce 100644 --- a/app/helpers/tags_helper.rb +++ b/app/helpers/tags_helper.rb @@ -1,7 +1,7 @@ module TagsHelper - def taggable_path(taggable, tag_name) - case taggable + def taggable_path(taggable_type, tag_name) + case taggable_type when 'debate' debates_path(tag: tag_name) when 'proposal' @@ -11,4 +11,18 @@ module TagsHelper end end + def taggable_counter_field(taggable_type) + "#{taggable_type.underscore.pluralize}_count" + end + + def tag_cloud(tags, classes, counter_field = :taggings_count) + return [] if tags.empty? + + max_count = tags.sort_by(&counter_field).last.send(counter_field).to_f + + tags.each do |tag| + index = ((tag.send(counter_field) / max_count) * (classes.size - 1)) + yield tag, classes[index.nan? ? 0 : index.round] + end + end end diff --git a/app/views/shared/_tag_cloud.html.erb b/app/views/shared/_tag_cloud.html.erb index 171b85b3c..cc377f34d 100644 --- a/app/views/shared/_tag_cloud.html.erb +++ b/app/views/shared/_tag_cloud.html.erb @@ -3,6 +3,9 @@

<%= t("shared.tags_cloud.tags") %>


<% tag_cloud @tag_cloud, %w[s m l] do |tag, css_class| %> - <%= link_to sanitize("#{tag.name} #{tag.taggings_count}"), taggable_path(taggable, tag.name), class: css_class %> + <%= link_to taggable_path(taggable, tag.name), class: css_class do %> + <%= tag.name %> + <%= tag.send(taggable_counter_field(taggable)) %> + <% end %> <% end %>
From d6db2387b90702996c1407ea1afc3520831d27c5 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 17 Sep 2015 16:59:15 +0200 Subject: [PATCH 8/8] Adds rake task for recalculating tag custom counters --- lib/tasks/tags.rake | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 lib/tasks/tags.rake diff --git a/lib/tasks/tags.rake b/lib/tasks/tags.rake new file mode 100644 index 000000000..c4af963cc --- /dev/null +++ b/lib/tasks/tags.rake @@ -0,0 +1,11 @@ +namespace :tags do + desc "Recalculates the debate and proposals counters" + task custom_count: :environment do + ActsAsTaggableOn::Tag.find_in_batches do |tasks| + tasks.each do |task| + task.recalculate_custom_counter_for('Debate') + task.recalculate_custom_counter_for('Proposal') + end + end + end +end