acts-as-taggable-on 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.1.1
@@ -390,21 +390,36 @@ module ActiveRecord
390
390
  end
391
391
 
392
392
  def save_tags
393
- (custom_contexts + self.class.tag_types.map(&:to_s)).each do |tag_type|
394
- tag_list_cache = tag_list_cache_on(tag_type)
395
- for owner, tag_list in tag_list_cache
396
- new_tag_names = tag_list - tags_on(tag_type, owner).map(&:name)
397
- old_tags = tags_on(tag_type, owner).reject { |tag| tag_list.include?(tag.name) }
398
- transaction do
399
- base_tags.delete(*old_tags) if old_tags.any?
400
- new_tag_names.each do |new_tag_name|
401
- new_tag = Tag.find_or_create_with_like_by_name(new_tag_name)
402
- Tagging.create(:tag_id => new_tag.id, :context => tag_type,
403
- :taggable => self, :tagger => owner)
393
+ contexts = custom_contexts + self.class.tag_types.map(&:to_s)
394
+
395
+ transaction do
396
+ contexts.each do |context|
397
+ cache = tag_list_cache_on(context)
398
+
399
+ cache.each do |owner, list|
400
+ new_tags = Tag.find_or_create_all_with_like_by_name(list.uniq)
401
+
402
+ # Destroy old taggings:
403
+ if owner
404
+ old_tags = tags_on(context, owner) - new_tags
405
+ old_taggings = taggings.find(:all, :conditions => { :tag_id => old_tags, :tagger_id => owner, :tagger_type => owner.class.to_s, :context => context })
406
+ old_taggings.each(&:destroy)
407
+ else
408
+ old_tags = tags_on(context) - new_tags
409
+ base_tags.delete(*old_tags)
410
+ end
411
+
412
+ new_tags.reject! { |tag| taggings.any? { |tagging| tagging.tag == tag &&
413
+ tagging.tagger == owner &&
414
+ tagging.context == context } }
415
+
416
+ # create new taggings:
417
+ new_tags.each do |tag|
418
+ taggings.create!(:tag_id => tag.id, :context => context, :tagger => owner)
404
419
  end
405
420
  end
406
421
  end
407
- end
422
+ end
408
423
 
409
424
  true
410
425
  end
@@ -13,17 +13,29 @@ class Tag < ActiveRecord::Base
13
13
 
14
14
  ### NAMED SCOPES:
15
15
 
16
- named_scope :named, lambda { |name| { :conditions => ["name = ?", name] } }
16
+ named_scope :named, lambda { |name| { :conditions => ["name LIKE ?", name] } }
17
+ named_scope :named_any, lambda { |list| { :conditions => list.map { |tag| sanitize_sql(["name LIKE ?", tag.to_s]) }.join(" OR ") } }
17
18
  named_scope :named_like, lambda { |name| { :conditions => ["name LIKE ?", "%#{name}%"] } }
18
- named_scope :named_like_any, lambda { |list| { :conditions => list.map { |tag| sanitize_sql(["name LIKE ?", tag.to_s]) }.join(" OR ") } }
19
+ named_scope :named_like_any, lambda { |list| { :conditions => list.map { |tag| sanitize_sql(["name LIKE ?", "%#{tag.to_s}%"]) }.join(" OR ") } }
19
20
 
20
- ### METHODS:
21
+ ### CLASS METHODS:
21
22
 
22
- # LIKE is used for cross-database case-insensitivity
23
23
  def self.find_or_create_with_like_by_name(name)
24
- find(:first, :conditions => ["name LIKE ?", name]) || create(:name => name)
24
+ named_like(name).first || create(:name => name)
25
25
  end
26
26
 
27
+ def self.find_or_create_all_with_like_by_name(*list)
28
+ list = [list].flatten
29
+
30
+ existing_tags = Tag.named_any(list).all
31
+ new_tag_names = list.reject { |name| existing_tags.any? { |tag| tag.name.downcase == name.downcase } }
32
+ created_tags = new_tag_names.map { |name| Tag.create(:name => name) }
33
+
34
+ existing_tags + created_tags
35
+ end
36
+
37
+ ### INSTANCE METHODS:
38
+
27
39
  def ==(object)
28
40
  super || (object.is_a?(Tag) && name == object.name)
29
41
  end
@@ -1,8 +1,11 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
2
 
3
3
  describe "acts_as_tagger" do
4
+ before(:each) do
5
+ [TaggableUser, TaggableModel, Tagging, Tag].each(&:destroy_all)
6
+ end
7
+
4
8
  context "Tagger Method Generation" do
5
-
6
9
  before(:each) do
7
10
  @tagger = TaggableUser.new()
8
11
  end
@@ -78,9 +81,28 @@ describe "acts_as_tagger" do
78
81
  @tagger.tag(@taggable, :with=>'this, and, that', :on=>:foo_boo, :force=>false) rescue
79
82
  @taggable.tag_list_on(:foo_boo).should be_empty
80
83
  end
81
-
82
84
  end
83
-
85
+
86
+ context "when called by multiple tagger's" do
87
+ before(:each) do
88
+ @user_x = TaggableUser.new
89
+ @user_y = TaggableUser.new
90
+ @taggable = TaggableModel.new(:name => 'acts_as_taggable_on', :tag_list => 'plugin')
91
+
92
+ @user_x.tag(@taggable, :with => 'ruby, rails', :on => :tags)
93
+ @user_y.tag(@taggable, :with => 'ruby, plugin', :on => :tags)
94
+ end
95
+
96
+ it "should not delete other taggers tags" do
97
+ @user_y.tag(@taggable, :with => '', :on => :tags)
98
+ @taggable.all_tags_list_on(:tags).should include('ruby')
99
+ end
100
+
101
+ it "should not delete original tags" do
102
+ @user_y.tag(@taggable, :with => '', :on => :tags)
103
+ @taggable.all_tags_list_on(:tags).should include('plugin')
104
+ end
105
+ end
84
106
  end
85
107
 
86
108
  end
@@ -38,6 +38,33 @@ describe Tag do
38
38
  }.should change(Tag, :count).by(1)
39
39
  end
40
40
  end
41
+
42
+ describe "find or create all by any name" do
43
+ before(:each) do
44
+ @tag.name = "awesome"
45
+ @tag.save
46
+ end
47
+
48
+ it "should find by name" do
49
+ Tag.find_or_create_all_with_like_by_name("awesome").should == [@tag]
50
+ end
51
+
52
+ it "should find by name case insensitive" do
53
+ Tag.find_or_create_all_with_like_by_name("AWESOME").should == [@tag]
54
+ end
55
+
56
+ it "should create by name" do
57
+ lambda {
58
+ Tag.find_or_create_all_with_like_by_name("epic")
59
+ }.should change(Tag, :count).by(1)
60
+ end
61
+
62
+ it "should find or create by name" do
63
+ lambda {
64
+ Tag.find_or_create_all_with_like_by_name("awesome", "epic").map(&:name).should == ["awesome", "epic"]
65
+ }.should change(Tag, :count).by(1)
66
+ end
67
+ end
41
68
 
42
69
  it "should require a name" do
43
70
  @tag.valid?
@@ -46,8 +46,8 @@ describe "Taggable" do
46
46
  @taggable.tag_list = "ruby, bob, charlie"
47
47
  @taggable.save
48
48
  @taggable.reload
49
- @taggable.skill_list.include?("ruby").should be_true
50
- @taggable.skill_list.include?("bob").should be_false
49
+ @taggable.skill_list.should include("ruby")
50
+ @taggable.skill_list.should_not include("bob")
51
51
  end
52
52
 
53
53
  it "should be able to remove tags through list alone" do
@@ -24,10 +24,10 @@ describe "Tagger" do
24
24
  @user2.tag(@taggable, :with => 'java, python, lisp, ruby', :on => :tags)
25
25
  }.should change(Tagging, :count).by(6)
26
26
 
27
- @user.owned_tags.map(&:name).should == %w(ruby scheme)
27
+ @user.owned_tags.map(&:name).sort.should == %w(ruby scheme)
28
28
  @user2.owned_tags.map(&:name).sort.should == %w(java python lisp ruby).sort
29
- @taggable.tags_from(@user).should == %w(ruby scheme)
30
- @taggable.tags_from(@user2).should == %w(java python lisp ruby)
29
+ @taggable.tags_from(@user).sort.should == %w(ruby scheme)
30
+ @taggable.tags_from(@user2).sort.should == %w(java lisp python ruby)
31
31
  @taggable.all_tags_list_on(:tags).sort.should == %w(ruby scheme java python lisp).sort
32
32
  @taggable.all_tags_on(:tags).size.should == 6
33
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts-as-taggable-on
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-03 00:00:00 +01:00
12
+ date: 2010-02-06 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15