acts-as-taggable-on 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.1.2
@@ -390,21 +390,38 @@ 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
+ taggings = Tagging.find(:all, :conditions => { :taggable_id => self.id, :taggable_type => self.class.to_s })
402
+
403
+ # Destroy old taggings:
404
+ if owner
405
+ old_tags = tags_on(context, owner) - new_tags
406
+ old_taggings = Tagging.find(:all, :conditions => { :taggable_id => self.id, :taggable_type => self.class.to_s, :tag_id => old_tags, :tagger_id => owner, :tagger_type => owner.class.to_s, :context => context })
407
+
408
+ old_taggings.each(&:destroy)
409
+ else
410
+ old_tags = tags_on(context) - new_tags
411
+ base_tags.delete(*old_tags)
412
+ end
413
+
414
+ new_tags.reject! { |tag| taggings.any? { |tagging| tagging.tag == tag &&
415
+ tagging.tagger == owner &&
416
+ tagging.context == context } }
417
+
418
+ # create new taggings:
419
+ new_tags.each do |tag|
420
+ Tagging.create!(:tag_id => tag.id, :context => context, :tagger => owner, :taggable => self)
404
421
  end
405
422
  end
406
423
  end
407
- end
424
+ end
408
425
 
409
426
  true
410
427
  end
@@ -13,17 +13,31 @@ 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
+ return [] if list.empty?
31
+
32
+ existing_tags = Tag.named_any(list).all
33
+ new_tag_names = list.reject { |name| existing_tags.any? { |tag| tag.name.downcase == name.downcase } }
34
+ created_tags = new_tag_names.map { |name| Tag.create(:name => name) }
35
+
36
+ existing_tags + created_tags
37
+ end
38
+
39
+ ### INSTANCE METHODS:
40
+
27
41
  def ==(object)
28
42
  super || (object.is_a?(Tag) && name == object.name)
29
43
  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,32 @@ 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
84
+ end
85
+
86
+ context "when called by multiple tagger's" do
87
+ before(:each) do
88
+ @user_x = TaggableUser.new(:name => "User X")
89
+ @user_y = TaggableUser.new(:name => "User Y")
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)
81
94
 
95
+ @user_y.tag(@taggable, :with => '', :on => :tags)
96
+ end
97
+
98
+ it "should delete owned tags" do
99
+ @user_y.owned_tags.should be_empty
100
+ end
101
+
102
+ it "should not delete other taggers tags" do
103
+ @user_x.owned_tags.should have(2).items
104
+ end
105
+
106
+ it "should not delete original tags" do
107
+ @taggable.all_tags_list_on(:tags).should include('plugin')
108
+ end
82
109
  end
83
-
84
110
  end
85
111
 
86
112
  end
@@ -38,6 +38,37 @@ 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
+
68
+ it "should return an empty array if no tags are specified" do
69
+ Tag.find_or_create_all_with_like_by_name([]).should == []
70
+ end
71
+ end
41
72
 
42
73
  it "should require a name" do
43
74
  @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,12 @@ 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).sort
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
+
30
+ @taggable.tags_from(@user).sort.should == %w(ruby scheme).sort
31
+ @taggable.tags_from(@user2).sort.should == %w(java lisp python ruby).sort
32
+
31
33
  @taggable.all_tags_list_on(:tags).sort.should == %w(ruby scheme java python lisp).sort
32
34
  @taggable.all_tags_on(:tags).size.should == 6
33
35
  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.2
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-07 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15