acts-as-taggable-on 2.0.6 → 2.4.0

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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +9 -0
  5. data/CHANGELOG.md +35 -0
  6. data/Gemfile +2 -9
  7. data/Guardfile +5 -0
  8. data/{MIT-LICENSE → MIT-LICENSE.md} +1 -1
  9. data/README.md +297 -0
  10. data/Rakefile +9 -55
  11. data/UPGRADING +14 -0
  12. data/acts-as-taggable-on.gemspec +32 -0
  13. data/lib/acts-as-taggable-on/version.rb +4 -0
  14. data/lib/acts-as-taggable-on.rb +37 -4
  15. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +6 -6
  16. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +99 -45
  17. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +162 -45
  18. data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +37 -0
  19. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +40 -15
  20. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +28 -18
  21. data/lib/acts_as_taggable_on/tag.rb +41 -16
  22. data/lib/acts_as_taggable_on/tag_list.rb +19 -14
  23. data/lib/acts_as_taggable_on/taggable.rb +102 -0
  24. data/lib/acts_as_taggable_on/{acts_as_tagger.rb → tagger.rb} +3 -3
  25. data/lib/acts_as_taggable_on/tagging.rb +12 -2
  26. data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
  27. data/lib/acts_as_taggable_on/utils.rb +34 -0
  28. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +9 -2
  29. data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +3 -1
  30. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +333 -54
  31. data/spec/acts_as_taggable_on/tag_list_spec.rb +117 -61
  32. data/spec/acts_as_taggable_on/tag_spec.rb +111 -14
  33. data/spec/acts_as_taggable_on/taggable_spec.rb +330 -34
  34. data/spec/acts_as_taggable_on/tagger_spec.rb +62 -15
  35. data/spec/acts_as_taggable_on/tagging_spec.rb +2 -5
  36. data/spec/acts_as_taggable_on/tags_helper_spec.rb +16 -0
  37. data/spec/acts_as_taggable_on/utils_spec.rb +21 -0
  38. data/spec/database.yml.sample +4 -2
  39. data/spec/generators/acts_as_taggable_on/migration/migration_generator_spec.rb +22 -0
  40. data/spec/models.rb +28 -1
  41. data/spec/schema.rb +18 -0
  42. data/spec/spec_helper.rb +30 -7
  43. data/uninstall.rb +1 -0
  44. metadata +174 -57
  45. data/CHANGELOG +0 -25
  46. data/README.rdoc +0 -221
  47. data/VERSION +0 -1
  48. data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +0 -7
  49. data/generators/acts_as_taggable_on_migration/templates/migration.rb +0 -29
  50. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +0 -53
  51. data/lib/acts_as_taggable_on/compatibility/Gemfile +0 -8
  52. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +0 -17
  53. data/lib/acts_as_taggable_on/compatibility/postgresql.rb +0 -44
  54. data/spec/database.yml +0 -17
@@ -1,70 +1,126 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require File.expand_path('../../spec_helper', __FILE__)
2
3
 
3
4
  describe ActsAsTaggableOn::TagList do
4
- before(:each) do
5
- @tag_list = ActsAsTaggableOn::TagList.new("awesome","radical")
6
- end
7
-
8
- it "should be an array" do
9
- @tag_list.is_a?(Array).should be_true
10
- end
11
-
12
- it "should be able to be add a new tag word" do
13
- @tag_list.add("cool")
14
- @tag_list.include?("cool").should be_true
15
- end
16
-
17
- it "should be able to add delimited lists of words" do
18
- @tag_list.add("cool, wicked", :parse => true)
19
- @tag_list.include?("cool").should be_true
20
- @tag_list.include?("wicked").should be_true
21
- end
22
-
23
- it "should be able to add delimited list of words with quoted delimiters" do
24
- @tag_list.add("'cool, wicked', \"really cool, really wicked\"", :parse => true)
25
- @tag_list.include?("cool, wicked").should be_true
26
- @tag_list.include?("really cool, really wicked").should be_true
27
- end
28
-
29
- it "should be able to handle other uses of quotation marks correctly" do
30
- @tag_list.add("john's cool car, mary's wicked toy", :parse => true)
31
- @tag_list.include?("john's cool car").should be_true
32
- @tag_list.include?("mary's wicked toy").should be_true
33
- end
34
-
35
- it "should be able to add an array of words" do
36
- @tag_list.add(["cool", "wicked"], :parse => true)
37
- @tag_list.include?("cool").should be_true
38
- @tag_list.include?("wicked").should be_true
39
- end
40
-
41
- it "should be able to remove words" do
42
- @tag_list.remove("awesome")
43
- @tag_list.include?("awesome").should be_false
5
+ let(:tag_list) { ActsAsTaggableOn::TagList.new("awesome","radical") }
6
+
7
+ it { should be_kind_of Array }
8
+
9
+ it "#from should return empty array if empty array is passed" do
10
+ ActsAsTaggableOn::TagList.from([]).should be_empty
44
11
  end
45
-
46
- it "should be able to remove delimited lists of words" do
47
- @tag_list.remove("awesome, radical", :parse => true)
48
- @tag_list.should be_empty
12
+
13
+ describe "#add" do
14
+ it "should be able to be add a new tag word" do
15
+ tag_list.add("cool")
16
+ tag_list.include?("cool").should be_true
17
+ end
18
+
19
+ it "should be able to add delimited lists of words" do
20
+ tag_list.add("cool, wicked", :parse => true)
21
+ tag_list.should include("cool", "wicked")
22
+ end
23
+
24
+ it "should be able to add delimited list of words with quoted delimiters" do
25
+ tag_list.add("'cool, wicked', \"really cool, really wicked\"", :parse => true)
26
+ tag_list.should include("cool, wicked", "really cool, really wicked")
27
+ end
28
+
29
+ it "should be able to handle other uses of quotation marks correctly" do
30
+ tag_list.add("john's cool car, mary's wicked toy", :parse => true)
31
+ tag_list.should include("john's cool car", "mary's wicked toy")
32
+ end
33
+
34
+ it "should be able to add an array of words" do
35
+ tag_list.add(["cool", "wicked"], :parse => true)
36
+ tag_list.should include("cool", "wicked")
37
+ end
38
+
39
+ it "should quote escape tags with commas in them" do
40
+ tag_list.add("cool","rad,bodacious")
41
+ tag_list.to_s.should == "awesome, radical, cool, \"rad,bodacious\""
42
+ end
43
+
49
44
  end
50
-
51
- it "should be able to remove an array of words" do
52
- @tag_list.remove(["awesome", "radical"], :parse => true)
53
- @tag_list.should be_empty
45
+
46
+ describe "#remove" do
47
+ it "should be able to remove words" do
48
+ tag_list.remove("awesome")
49
+ tag_list.should_not include("awesome")
50
+ end
51
+
52
+ it "should be able to remove delimited lists of words" do
53
+ tag_list.remove("awesome, radical", :parse => true)
54
+ tag_list.should be_empty
55
+ end
56
+
57
+ it "should be able to remove an array of words" do
58
+ tag_list.remove(["awesome", "radical"], :parse => true)
59
+ tag_list.should be_empty
60
+ end
54
61
  end
55
-
56
- it "should give a delimited list of words when converted to string" do
57
- @tag_list.to_s.should == "awesome, radical"
62
+
63
+ describe "#to_s" do
64
+ it "should give a delimited list of words when converted to string" do
65
+ tag_list.to_s.should == "awesome, radical"
66
+ end
67
+
68
+ it "should be able to call to_s on a frozen tag list" do
69
+ tag_list.freeze
70
+ lambda { tag_list.add("cool","rad,bodacious") }.should raise_error
71
+ lambda { tag_list.to_s }.should_not raise_error
72
+ end
58
73
  end
59
-
60
- it "should quote escape tags with commas in them" do
61
- @tag_list.add("cool","rad,bodacious")
62
- @tag_list.to_s.should == "awesome, radical, cool, \"rad,bodacious\""
74
+
75
+ describe "cleaning" do
76
+ it "should parameterize if force_parameterize is set to true" do
77
+ ActsAsTaggableOn.force_parameterize = true
78
+ tag_list = ActsAsTaggableOn::TagList.new("awesome()","radical)(cc")
79
+
80
+ tag_list.to_s.should == "awesome, radical-cc"
81
+ ActsAsTaggableOn.force_parameterize = false
82
+ end
83
+
84
+ it "should lowercase if force_lowercase is set to true" do
85
+ ActsAsTaggableOn.force_lowercase = true
86
+
87
+ tag_list = ActsAsTaggableOn::TagList.new("aweSomE","RaDicaL","Entrée")
88
+ tag_list.to_s.should == "awesome, radical, entrée"
89
+
90
+ ActsAsTaggableOn.force_lowercase = false
91
+ end
92
+
63
93
  end
64
-
65
- it "should be able to call to_s on a frozen tag list" do
66
- @tag_list.freeze
67
- lambda { @tag_list.add("cool","rad,bodacious") }.should raise_error
68
- lambda { @tag_list.to_s }.should_not raise_error
94
+
95
+ describe "Multiple Delimiter" do
96
+ before do
97
+ @old_delimiter = ActsAsTaggableOn.delimiter
98
+ end
99
+
100
+ after do
101
+ ActsAsTaggableOn.delimiter = @old_delimiter
102
+ end
103
+
104
+ it "should separate tags by delimiters" do
105
+ ActsAsTaggableOn.delimiter = [',', ' ', '\|']
106
+ tag_list = ActsAsTaggableOn::TagList.from "cool, data|I have"
107
+ tag_list.to_s.should == 'cool, data, I, have'
108
+ end
109
+
110
+ it "should escape quote" do
111
+ ActsAsTaggableOn.delimiter = [',', ' ', '\|']
112
+ tag_list = ActsAsTaggableOn::TagList.from "'I have'|cool, data"
113
+ tag_list.to_s.should == '"I have", cool, data'
114
+
115
+ tag_list = ActsAsTaggableOn::TagList.from '"I, have"|cool, data'
116
+ tag_list.to_s.should == '"I, have", cool, data'
117
+ end
118
+
119
+ it "should work for utf8 delimiter and long delimiter" do
120
+ ActsAsTaggableOn.delimiter = [',', '的', '可能是']
121
+ tag_list = ActsAsTaggableOn::TagList.from "我的东西可能是不见了,还好有备份"
122
+ tag_list.to_s.should == "我, 东西, 不见了, 还好有备份"
123
+ end
69
124
  end
70
- end
125
+
126
+ end
@@ -1,3 +1,5 @@
1
+ #encoding: utf-8
2
+
1
3
  require File.expand_path('../../spec_helper', __FILE__)
2
4
 
3
5
  describe ActsAsTaggableOn::Tag do
@@ -9,12 +11,13 @@ describe ActsAsTaggableOn::Tag do
9
11
 
10
12
  describe "named like any" do
11
13
  before(:each) do
14
+ ActsAsTaggableOn::Tag.create(:name => "Awesome")
12
15
  ActsAsTaggableOn::Tag.create(:name => "awesome")
13
16
  ActsAsTaggableOn::Tag.create(:name => "epic")
14
17
  end
15
18
 
16
19
  it "should find both tags" do
17
- ActsAsTaggableOn::Tag.named_like_any(["awesome", "epic"]).should have(2).items
20
+ ActsAsTaggableOn::Tag.named_like_any(["awesome", "epic"]).should have(3).items
18
21
  end
19
22
  end
20
23
 
@@ -39,6 +42,23 @@ describe ActsAsTaggableOn::Tag do
39
42
  end
40
43
  end
41
44
 
45
+ unless ActsAsTaggableOn::Tag.using_sqlite?
46
+ describe "find or create by unicode name" do
47
+ before(:each) do
48
+ @tag.name = "привет"
49
+ @tag.save
50
+ end
51
+
52
+ it "should find by name" do
53
+ ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("привет").should == @tag
54
+ end
55
+
56
+ it "should find by name case insensitive" do
57
+ ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("ПРИВЕТ").should == @tag
58
+ end
59
+ end
60
+ end
61
+
42
62
  describe "find or create all by any name" do
43
63
  before(:each) do
44
64
  @tag.name = "awesome"
@@ -72,21 +92,23 @@ describe ActsAsTaggableOn::Tag do
72
92
 
73
93
  it "should require a name" do
74
94
  @tag.valid?
75
-
76
- if ActiveRecord::VERSION::MAJOR >= 3
77
- @tag.errors[:name].should == ["can't be blank"]
78
- else
79
- @tag.errors[:name].should == "can't be blank"
80
- end
95
+
96
+ @tag.errors[:name].should == ["can't be blank"]
81
97
 
82
98
  @tag.name = "something"
83
99
  @tag.valid?
84
-
85
- if ActiveRecord::VERSION::MAJOR >= 3
86
- @tag.errors[:name].should == []
87
- else
88
- @tag.errors[:name].should be_nil
89
- end
100
+
101
+ @tag.errors[:name].should == []
102
+ end
103
+
104
+ it "should limit the name length to 255 or less characters" do
105
+ @tag.name = "fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxranr"
106
+ @tag.valid?
107
+ @tag.errors[:name].should == ["is too long (maximum is 255 characters)"]
108
+
109
+ @tag.name = "fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxran"
110
+ @tag.valid?
111
+ @tag.errors[:name].should == []
90
112
  end
91
113
 
92
114
  it "should equal a tag with the same name" do
@@ -112,4 +134,79 @@ describe ActsAsTaggableOn::Tag do
112
134
  @another_tag = ActsAsTaggableOn::Tag.create!(:name => "coolip")
113
135
  ActsAsTaggableOn::Tag.named_like('cool').should include(@tag, @another_tag)
114
136
  end
115
- end
137
+
138
+ describe "escape wildcard symbols in like requests" do
139
+ before(:each) do
140
+ @tag.name = "cool"
141
+ @tag.save
142
+ @another_tag = ActsAsTaggableOn::Tag.create!(:name => "coo%")
143
+ @another_tag2 = ActsAsTaggableOn::Tag.create!(:name => "coolish")
144
+ end
145
+
146
+ it "return escaped result when '%' char present in tag" do
147
+ ActsAsTaggableOn::Tag.named_like('coo%').should_not include(@tag)
148
+ ActsAsTaggableOn::Tag.named_like('coo%').should include(@another_tag)
149
+ end
150
+
151
+ end
152
+
153
+ describe "when using strict_case_match" do
154
+ before do
155
+ ActsAsTaggableOn.strict_case_match = true
156
+ @tag.name = "awesome"
157
+ @tag.save!
158
+ end
159
+
160
+ after do
161
+ ActsAsTaggableOn.strict_case_match = false
162
+ end
163
+
164
+ it "should find by name" do
165
+ ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("awesome").should == @tag
166
+ end
167
+
168
+ it "should find by name case sensitively" do
169
+ expect {
170
+ ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("AWESOME")
171
+ }.to change(ActsAsTaggableOn::Tag, :count)
172
+
173
+ ActsAsTaggableOn::Tag.last.name.should == "AWESOME"
174
+ end
175
+
176
+ it "should have a named_scope named(something) that matches exactly" do
177
+ uppercase_tag = ActsAsTaggableOn::Tag.create(:name => "Cool")
178
+ @tag.name = "cool"
179
+ @tag.save!
180
+
181
+ ActsAsTaggableOn::Tag.named('cool').should include(@tag)
182
+ ActsAsTaggableOn::Tag.named('cool').should_not include(uppercase_tag)
183
+ end
184
+ end
185
+
186
+ describe "name uniqeness validation" do
187
+ let(:duplicate_tag) { ActsAsTaggableOn::Tag.new(:name => 'ror') }
188
+
189
+ before { ActsAsTaggableOn::Tag.create(:name => 'ror') }
190
+
191
+ context "when don't need unique names" do
192
+ it "should not run uniqueness validation" do
193
+ duplicate_tag.stub(:validates_name_uniqueness?).and_return(false)
194
+ duplicate_tag.save
195
+ duplicate_tag.should be_persisted
196
+ end
197
+ end
198
+
199
+ context "when do need unique names" do
200
+ it "should run uniqueness validation" do
201
+ duplicate_tag.should_not be_valid
202
+ end
203
+
204
+ it "add error to name" do
205
+ duplicate_tag.save
206
+
207
+ duplicate_tag.should have(1).errors
208
+ duplicate_tag.errors.messages[:name].should include('has already been taken')
209
+ end
210
+ end
211
+ end
212
+ end