diff --git a/app/controllers/debates_controller.rb b/app/controllers/debates_controller.rb
index 30a683404..ded2d2aa0 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/models/debate.rb b/app/models/debate.rb
index a7b84f0ce..4785ab04f 100644
--- a/app/models/debate.rb
+++ b/app/models/debate.rb
@@ -127,6 +127,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/app/views/debates/_debate.html.erb b/app/views/debates/_debate.html.erb
index 936de966e..3be675bb9 100644
--- a/app/views/debates/_debate.html.erb
+++ b/app/views/debates/_debate.html.erb
@@ -42,7 +42,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 23134d424..c15b5b709 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 865365493..6d8116b1c 100644
--- a/app/views/proposals/_proposal.html.erb
+++ b/app/views/proposals/_proposal.html.erb
@@ -42,7 +42,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 72c8d2918..68b599f66 100644
--- a/app/views/proposals/show.html.erb
+++ b/app/views/proposals/show.html.erb
@@ -78,7 +78,7 @@
<%= @proposal.question %>
- <%= render 'shared/tags', proposal: @proposal %>
+ <%= render 'shared/tags', taggable: @proposal %>
<%= render 'actions', proposal: @proposal %>
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 %>
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? %>
diff --git a/config/initializers/acts_as_taggable_on.rb b/config/initializers/acts_as_taggable_on.rb
index 7ad1bbb22..829b43725 100644
--- a/config/initializers/acts_as_taggable_on.rb
+++ b/config/initializers/acts_as_taggable_on.rb
@@ -1,8 +1,43 @@
-ActsAsTaggableOn::Tagging.class_eval do
- after_destroy :touch_taggable
+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
-end
\ No newline at end of file
+ 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
+
+end
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,
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
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/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
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..2362e8745
--- /dev/null
+++ b/spec/lib/acts_as_taggable_on_spec.rb
@@ -0,0 +1,72 @@
+require 'rails_helper'
+
+describe 'ActsAsTaggableOn' do
+
+ 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")
+
+ 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
+
+ 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
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 2a5d1f9e5..20a461f15 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