This commit is contained in:
Toks
2013-10-06 16:56:39 -04:00
parent 5005614e97
commit c5b5ca9825
7 changed files with 76 additions and 10 deletions

View File

@@ -72,6 +72,7 @@ class TagAlias < ActiveRecord::Base
def process! def process!
update_column(:status, "processing") update_column(:status, "processing")
move_aliases_and_implications
clear_all_cache clear_all_cache
ensure_category_consistency ensure_category_consistency
update_posts update_posts
@@ -107,13 +108,28 @@ class TagAlias < ActiveRecord::Base
end end
def absence_of_transitive_relation def absence_of_transitive_relation
# We don't want a -> b && b -> c chains # We don't want a -> b && b -> c chains if the b -> c alias was created first.
if self.class.exists?(["antecedent_name = ?", consequent_name]) || self.class.exists?(["consequent_name = ?", antecedent_name]) # If the a -> b alias was created first, the new one will be allowed and the old one will be moved automatically instead.
if self.class.exists?(["antecedent_name = ?", consequent_name])
self.errors[:base] << "Tag alias can not create a transitive relation with another tag alias" self.errors[:base] << "Tag alias can not create a transitive relation with another tag alias"
false false
end end
end end
def move_aliases_and_implications
aliases = TagAlias.where(["consequent_name = ?", antecedent_name])
aliases.each do |ta|
ta.consequent_name = self.consequent_name
ta.save
end
implications = TagImplication.where(["consequent_name = ?", antecedent_name])
implications.each do |ti|
ti.consequent_name = self.consequent_name
ti.save
end
end
def ensure_tags_exist def ensure_tags_exist
Tag.find_or_create_by_name(antecedent_name) Tag.find_or_create_by_name(antecedent_name)
Tag.find_or_create_by_name(consequent_name) Tag.find_or_create_by_name(consequent_name)

View File

@@ -8,6 +8,7 @@ class TagImplication < ActiveRecord::Base
validates_presence_of :creator_id, :antecedent_name, :consequent_name validates_presence_of :creator_id, :antecedent_name, :consequent_name
validates_uniqueness_of :antecedent_name, :scope => :consequent_name validates_uniqueness_of :antecedent_name, :scope => :consequent_name
validate :absence_of_circular_relation validate :absence_of_circular_relation
validate :consequent_is_not_aliased
module DescendantMethods module DescendantMethods
extend ActiveSupport::Concern extend ActiveSupport::Concern
@@ -125,6 +126,14 @@ class TagImplication < ActiveRecord::Base
end end
end end
def consequent_is_not_aliased
# We don't want to implicate a -> b if b is already aliased to c
if TagAlias.exists?(["antecedent_name = ?", consequent_name])
self.errors[:base] << "Consequent tag must not be aliased to another tag"
false
end
end
def update_posts def update_posts
Post.without_timeout do Post.without_timeout do
Post.raw_tag_match(antecedent_name).find_each do |post| Post.raw_tag_match(antecedent_name).find_each do |post|

View File

@@ -0,0 +1,13 @@
class UpdateAliasedImplicationConsequents < ActiveRecord::Migration
def change
execute "set statement_timeout = 0"
TagImplication.find_each do |ti|
ta = TagAlias.where("antecedent_name = ? AND status != ?", ti.consequent_name, "pending").first
if ta
ti.consequent_name = ta.consequent_name
ti.save
ta.update_posts
end
end
end
end

View File

@@ -2,9 +2,12 @@ FactoryGirl.define do
factory :tag_alias do factory :tag_alias do
antecedent_name "aaa" antecedent_name "aaa"
consequent_name "bbb" consequent_name "bbb"
status "active"
after(:create) do |tag_alias| after(:create) do |tag_alias|
tag_alias.process! unless tag_alias.status == "pending"
tag_alias.process!
end
end end
end end
end end

View File

@@ -2,9 +2,12 @@ FactoryGirl.define do
factory :tag_implication do factory :tag_implication do
antecedent_name "aaa" antecedent_name "aaa"
consequent_name "bbb" consequent_name "bbb"
status "active"
after(:create) do |tag_implication| after(:create) do |tag_implication|
tag_implication.process! unless tag_implication.status == "pending"
tag_implication.process!
end
end end
end end
end end

View File

@@ -53,15 +53,29 @@ class TagAliasTest < ActiveSupport::TestCase
end end
should "not validate for transitive relations" do should "not validate for transitive relations" do
ta1 = FactoryGirl.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb") ta1 = FactoryGirl.create(:tag_alias, :antecedent_name => "bbb", :consequent_name => "ccc")
assert_difference("TagAlias.count", 0) do assert_difference("TagAlias.count", 0) do
ta3 = FactoryGirl.build(:tag_alias, :antecedent_name => "bbb", :consequent_name => "ddd") ta2 = FactoryGirl.build(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb")
ta3.save ta2.save
assert(ta3.errors.any?, "Tag alias should be invalid") assert(ta2.errors.any?, "Tag alias should be invalid")
assert_equal("Tag alias can not create a transitive relation with another tag alias", ta3.errors.full_messages.join) assert_equal("Tag alias can not create a transitive relation with another tag alias", ta2.errors.full_messages.join)
end end
end end
should "move existing aliases" do
ta1 = FactoryGirl.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb", :status => "active")
ta2 = FactoryGirl.create(:tag_alias, :antecedent_name => "bbb", :consequent_name => "ccc", :status => "active")
ta1.reload
assert_equal("ccc", ta1.consequent_name)
end
should "move existing implications" do
ti = FactoryGirl.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb", :status => "active")
ta = FactoryGirl.create(:tag_alias, :antecedent_name => "bbb", :consequent_name => "ccc", :status => "active")
ti.reload
assert_equal("ccc", ti.consequent_name)
end
should "not push the antecedent's category to the consequent if the antecedent is general" do should "not push the antecedent's category to the consequent if the antecedent is general" do
tag1 = FactoryGirl.create(:tag, :name => "aaa") tag1 = FactoryGirl.create(:tag, :name => "aaa")
tag2 = FactoryGirl.create(:tag, :name => "bbb", :category => 1) tag2 = FactoryGirl.create(:tag, :name => "bbb", :category => 1)

View File

@@ -17,7 +17,7 @@ class TagImplicationTest < ActiveSupport::TestCase
end end
should "ignore pending implications when building descendant names" do should "ignore pending implications when building descendant names" do
ti2 = FactoryGirl.build(:tag_implication, :antecedent_name => "b", :consequent_name => "c") ti2 = FactoryGirl.build(:tag_implication, :antecedent_name => "b", :consequent_name => "c", :status => "pending")
ti2.save ti2.save
ti1 = FactoryGirl.create(:tag_implication, :antecedent_name => "a", :consequent_name => "b") ti1 = FactoryGirl.create(:tag_implication, :antecedent_name => "a", :consequent_name => "b")
assert_equal("b", ti1.descendant_names) assert_equal("b", ti1.descendant_names)
@@ -44,6 +44,14 @@ class TagImplicationTest < ActiveSupport::TestCase
assert_equal("Antecedent name has already been taken", ti2.errors.full_messages.join("")) assert_equal("Antecedent name has already been taken", ti2.errors.full_messages.join(""))
end end
should "not validate if its consequent is aliased to another tag" do
ta = FactoryGirl.create(:tag_alias, :antecedent_name => "bbb", :consequent_name => "ccc")
ti = FactoryGirl.build(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti.save
assert(ti.errors.any?, "Tag implication should not have validated.")
assert_equal("Consequent tag must not be aliased to another tag", ti.errors.full_messages.join(""))
end
should "calculate all its descendants" do should "calculate all its descendants" do
ti1 = FactoryGirl.create(:tag_implication, :antecedent_name => "bbb", :consequent_name => "ccc") ti1 = FactoryGirl.create(:tag_implication, :antecedent_name => "bbb", :consequent_name => "ccc")
assert_equal("ccc", ti1.descendant_names) assert_equal("ccc", ti1.descendant_names)