Permalink
Browse files

add alias tags

  • Loading branch information...
1 parent 7c01189 commit 13d60ed8a836027117a37f318505e21f859bbd19 @fntz fntz committed Jul 18, 2012
@@ -20,11 +20,20 @@ def self.up
add_index :taggings, :tag_id
add_index :taggings, [:taggable_id, :taggable_type, :context]
+
+ create_table :alias_tags, :id => false do |t|
+ t.integer :tag_id
+ t.integer :alias_id
+ end
+
+ add_index :alias_tags, :tag_id
+ add_index :alias_tags, :alias_id
end
def self.down
drop_table :taggings
drop_table :tags
+ drop_table :alias_tags
end
end
View
@@ -4,7 +4,7 @@
require "rocket_tag/tagging"
require "rocket_tag/tag"
-
+require "rocket_tag/alias_tag"
require "rocket_tag/taggable"
$LOAD_PATH.shift
@@ -0,0 +1,4 @@
+module RocketTag
+ class AliasTag < ActiveRecord::Base
+ end
+end
View
@@ -6,6 +6,28 @@ class Tag < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
+ has_and_belongs_to_many :alias, :class_name => "RocketTag::Tag",
+ :join_table => "alias_tags",
+ :foreign_key => "tag_id",
+ :association_foreign_key => "alias_id",
+ :uniq => true,
+ :after_add => :add_reverse_alias,
+ :after_remove => :remove_reverse_alias
+
+ def add_reverse_alias(tag)
+ [self.alias, self].flatten.each do |t|
+ tag.alias << t if !tag.alias.include?(t) && t != tag
+ end
+ end
+
+ def remove_reverse_alias(tag)
+ tag.alias.delete(self) if tag.alias.include?(self)
+ end
+
+ def alias?(that)
+ return self.alias.include?(that)
+ end
+
def self.by_taggable_type(type)
joins{taggings}.where{taggings.taggable_type == type.to_s}
end
View
@@ -182,13 +182,26 @@ def tagged_with tags_list, options = {}
q = joins{taggings.tag}
+ alias_tag_names = lambda do |list|
+ names = RocketTag::Tag.select{:name}.where do
+ id.in(RocketTag::Tag.select{'alias_tags.alias_id'}
+ .joins(:alias).where{
+ tags.name.in(list)
+ })
+ end
+ names.map{|t| t.name}
+ end
+
case tags_list
when Hash
# A tag can only match it's context
c = tags_list.each_key.map do |context|
squeel do
- tags.name.in(tags_list[context]) & (taggings.context == context.to_s)
+ list = tags_list[context]
+ list << alias_tag_names.call(list)
+
+ tags.name.in(list.flatten!) & (taggings.context == context.to_s)
end
end.inject do |s,t|
s | t
@@ -198,8 +211,9 @@ def tagged_with tags_list, options = {}
else
# Any tag can match any context
+ tags_list << alias_tag_names.call(tags_list)
q = q.
- where{tags.name.in(tags_list)}.
+ where{tags.name.in(tags_list.flatten!)}.
where(with_tag_context(options.delete(:on)))
end
@@ -221,7 +235,7 @@ def tagged_with tags_list, options = {}
all = options.delete :all
q = q.where{tags_count==tags_list.length} if all
- # Return the relation
+ # Return the relation
q
end
@@ -0,0 +1,75 @@
+require File.expand_path('../../spec_helper', __FILE__)
+
+describe "Tag" do
+ before(:all) {
+ clean_database!
+ }
+ describe "#alias methods" do
+ before(:each) do
+ @a = RocketTag::Tag.create(:name => 'rails')
+ @b = RocketTag::Tag.create(:name => 'ror')
+ @c = RocketTag::Tag.create(:name => 'ruby-on-rails')
+ end
+
+ it "should have alias tags" do
+ @a.alias << @b
+ @b.alias << @c
+ @a.alias.should eq [@b, @c]
+ @b.alias.should eq [@a, @c]
+ @c.alias.should eq [@a, @b]
+ end
+ it "should remove alias if relations delete" do
+ @a.alias << @b
+ @b.alias << @c
+ @a.alias = []
+ @a.alias.should eq []
+ @b.alias.should eq [@c]
+ @c.alias.should eq [@b]
+ end
+ it "should check if tag is alias" do
+ @a.alias << @b
+ @a.alias?(@b).should be true
+ @b.alias?(@a).should be true
+ @c.alias?(@a).should be false
+ @c.alias?(@b).should be false
+ end
+ end
+
+ describe "TaggableModel" do
+ before(:all) do
+ @m1 = TaggableModel.new(:name => 'foo',
+ :skills => ['a', 'b', 'c'])
+ @m1.languages = [ "abc" , "cde"]
+ @m1.save
+ @m2 = TaggableModel.new(:name => 'bar',
+ :skills => ['d', 'e', 'f'])
+ @m2.languages = ["abc"]
+ @m2.save
+ @m3 = TaggableModel.new(:name => 'baz',
+ :skills => ['b', 'k', 'l'])
+ @m3.languages = ["anm"]
+ @m3.save
+ @m4 = TaggableModel.new(:name => 'zed',
+ :skills => ['l', 'z', 'o'])
+ @m4.languages = ["tex"]
+ @m4.save
+ RocketTag::Tag.find_by_name('tex').alias << RocketTag::Tag.find_by_name('abc')
+ RocketTag::Tag.find_by_name('a').alias << RocketTag::Tag.find_by_name('d') << RocketTag::Tag.find_by_name('z')
+ RocketTag::Tag.find_by_name('e').alias << RocketTag::Tag.find_by_name('k')
+ end
+
+ it "should return with alias tags" do
+ models = TaggableModel.tagged_with(["a"])
+ models.size.should eq (3)
+ models.should eq ([@m1, @m2, @m4])
+
+ mlangs = TaggableModel.tagged_with(["tex"])
+ mlangs.should eq ([@m1, @m2, @m4])
+
+ mall = TaggableModel.tagged_with(:languages => ["tex"], :skills => ["a"])
+ mall.should eq ([@m1, @m2, @m4])
+
+ TaggableModel.tagged_with(["a", "b", "tex"]).size.should eq(4)
+ end
+ end
+end
@@ -75,15 +75,13 @@
end
it "should generate the correct results" do
-
TaggableModel.tagged_with(%w[a b], :all=>true).count(:distinct => true).should == 6
TaggableModel.tagged_with(%w[a b], :all=>true).where{name.like "app%"}.count(:distinct => true).should == 3
-
TaggableModel.tagged_with(%w[a b], :all=>true).where{name.like "%1"}.count(:distinct => true).should == 2
TaggableModel.tagged_with(%w[a b], :all=>true, :on => :skills).where{name.like "%1"}.count(:distinct => true).should == 1
-
end
end
+
describe "querying tags" do
before :each do
@@ -387,5 +385,5 @@
end
end
end
- end
+ end
end
View
@@ -16,6 +16,14 @@
t.string "name"
end
+ create_table "alias_tags", :force => true, :id => false do |t|
+ t.integer "tag_id"
+ t.integer "alias_id"
+ end
+
+ add_index "alias_tags", ["tag_id"]
+ add_index "alias_tags", ["alias_id"]
+
create_table :taggable_models, :force => true do |t|
t.column :user_id, :integer
t.column :name, :string
View
@@ -70,7 +70,7 @@ def freq
end
def clean_database!
- models = [RocketTag::Tag, RocketTag::Tagging, TaggableModel]
+ models = [RocketTag::Tag, RocketTag::Tagging, TaggableModel, RocketTag::AliasTag]
models.each do |model|
ActiveRecord::Base.connection.execute "DELETE FROM #{model.table_name}"
end

0 comments on commit 13d60ed

Please sign in to comment.