acts-as-taggable-on 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +1 -2
  3. data/README.rdoc +27 -50
  4. data/acts-as-taggable-on.gemspec +4 -3
  5. data/lib/acts-as-taggable-on.rb +0 -4
  6. data/lib/acts-as-taggable-on/version.rb +1 -1
  7. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +4 -13
  8. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +1 -1
  9. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +1 -1
  10. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +29 -17
  11. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +15 -15
  12. data/lib/acts_as_taggable_on/tag.rb +12 -13
  13. data/lib/acts_as_taggable_on/tagging.rb +0 -2
  14. data/lib/acts_as_taggable_on/utils.rb +7 -8
  15. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +228 -61
  16. data/spec/acts_as_taggable_on/tag_list_spec.rb +4 -0
  17. data/spec/acts_as_taggable_on/tag_spec.rb +11 -15
  18. data/spec/acts_as_taggable_on/taggable_spec.rb +89 -27
  19. data/spec/acts_as_taggable_on/tagger_spec.rb +14 -0
  20. data/spec/acts_as_taggable_on/utils_spec.rb +2 -3
  21. data/spec/database.yml.sample +1 -1
  22. data/spec/generators/acts_as_taggable_on/migration/migration_generator_spec.rb +22 -0
  23. data/spec/models.rb +11 -2
  24. data/spec/schema.rb +7 -0
  25. data/spec/spec_helper.rb +1 -0
  26. metadata +95 -122
  27. data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +0 -7
  28. data/generators/acts_as_taggable_on_migration/templates/migration.rb +0 -29
  29. data/lib/acts_as_taggable_on/compatibility/Gemfile +0 -8
  30. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +0 -21
@@ -62,6 +62,10 @@ describe ActsAsTaggableOn::TagList do
62
62
  @tag_list.to_s.should == "awesome, radical, cool, \"rad,bodacious\""
63
63
  end
64
64
 
65
+ it "#from should return empty array if empty array is passed" do
66
+ ActsAsTaggableOn::TagList.from([]).should == []
67
+ end
68
+
65
69
  it "should be able to call to_s on a frozen tag list" do
66
70
  @tag_list.freeze
67
71
  lambda { @tag_list.add("cool","rad,bodacious") }.should raise_error
@@ -9,12 +9,13 @@ describe ActsAsTaggableOn::Tag do
9
9
 
10
10
  describe "named like any" do
11
11
  before(:each) do
12
+ ActsAsTaggableOn::Tag.create(:name => "Awesome")
12
13
  ActsAsTaggableOn::Tag.create(:name => "awesome")
13
14
  ActsAsTaggableOn::Tag.create(:name => "epic")
14
15
  end
15
16
 
16
17
  it "should find both tags" do
17
- ActsAsTaggableOn::Tag.named_like_any(["awesome", "epic"]).should have(2).items
18
+ ActsAsTaggableOn::Tag.named_like_any(["awesome", "epic"]).should have(3).items
18
19
  end
19
20
  end
20
21
 
@@ -72,7 +73,7 @@ describe ActsAsTaggableOn::Tag do
72
73
 
73
74
  it "should require a name" do
74
75
  @tag.valid?
75
-
76
+
76
77
  if ActiveRecord::VERSION::MAJOR >= 3
77
78
  @tag.errors[:name].should == ["can't be blank"]
78
79
  else
@@ -81,8 +82,8 @@ describe ActsAsTaggableOn::Tag do
81
82
 
82
83
  @tag.name = "something"
83
84
  @tag.valid?
84
-
85
- if ActiveRecord::VERSION::MAJOR >= 3
85
+
86
+ if ActiveRecord::VERSION::MAJOR >= 3
86
87
  @tag.errors[:name].should == []
87
88
  else
88
89
  @tag.errors[:name].should be_nil
@@ -112,24 +113,19 @@ describe ActsAsTaggableOn::Tag do
112
113
  @another_tag = ActsAsTaggableOn::Tag.create!(:name => "coolip")
113
114
  ActsAsTaggableOn::Tag.named_like('cool').should include(@tag, @another_tag)
114
115
  end
115
-
116
+
116
117
  describe "escape wildcard symbols in like requests" do
117
118
  before(:each) do
118
119
  @tag.name = "cool"
119
120
  @tag.save
120
- @another_tag = ActsAsTaggableOn::Tag.create!(:name => "coo%")
121
- @another_tag2 = ActsAsTaggableOn::Tag.create!(:name => "coolish")
121
+ @another_tag = ActsAsTaggableOn::Tag.create!(:name => "coo%")
122
+ @another_tag2 = ActsAsTaggableOn::Tag.create!(:name => "coolish")
122
123
  end
123
-
124
+
124
125
  it "return escaped result when '%' char present in tag" do
125
- if @tag.using_sqlite?
126
- ActsAsTaggableOn::Tag.named_like('coo%').should include(@tag)
127
- ActsAsTaggableOn::Tag.named_like('coo%').should include(@another_tag)
128
- else
129
- ActsAsTaggableOn::Tag.named_like('coo%').should_not include(@tag)
126
+ ActsAsTaggableOn::Tag.named_like('coo%').should_not include(@tag)
130
127
  ActsAsTaggableOn::Tag.named_like('coo%').should include(@another_tag)
131
- end
132
128
  end
133
-
129
+
134
130
  end
135
131
  end
@@ -25,14 +25,19 @@ describe "Taggable" do
25
25
  @taggable.tag_counts_on(:tags).length.should == 2
26
26
  end
27
27
 
28
+ it "should return [] right after create" do
29
+ blank_taggable = TaggableModel.new(:name => "Bob Jones")
30
+ blank_taggable.tag_list.should == []
31
+ end
32
+
28
33
  it "should be able to create tags" do
29
34
  @taggable.skill_list = "ruby, rails, css"
30
35
  @taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
31
-
36
+
32
37
  lambda {
33
38
  @taggable.save
34
39
  }.should change(ActsAsTaggableOn::Tag, :count).by(3)
35
-
40
+
36
41
  @taggable.reload
37
42
  @taggable.skill_list.sort.should == %w(ruby rails css).sort
38
43
  end
@@ -41,10 +46,10 @@ describe "Taggable" do
41
46
  @taggable.tag_list_on(:test).add("hello")
42
47
  @taggable.tag_list_cache_on(:test).should_not be_empty
43
48
  @taggable.tag_list_on(:test).should == ["hello"]
44
-
49
+
45
50
  @taggable.save
46
51
  @taggable.save_tags
47
-
52
+
48
53
  @taggable.reload
49
54
  @taggable.tag_list_on(:test).should == ["hello"]
50
55
  end
@@ -75,7 +80,7 @@ describe "Taggable" do
75
80
  @taggables[0].skill_list = "ruby"
76
81
  @taggables[1].skill_list = "css"
77
82
  @taggables.each{|taggable| taggable.save}
78
-
83
+
79
84
  @found_taggables_by_tag = TaggableModel.joins(:tags).where(:tags => {:name => ["bob"]})
80
85
  @found_taggables_by_skill = TaggableModel.joins(:skills).where(:tags => {:name => ["ruby"]})
81
86
 
@@ -84,7 +89,7 @@ describe "Taggable" do
84
89
  @found_taggables_by_skill.should include @taggables[0]
85
90
  @found_taggables_by_skill.should_not include @taggables[1]
86
91
  end
87
-
92
+
88
93
  it "should be able to find by tag" do
89
94
  @taggable.skill_list = "ruby, rails, css"
90
95
  @taggable.save
@@ -136,7 +141,7 @@ describe "Taggable" do
136
141
  # Let's only find those who need rails or css and are offering c++ or java
137
142
  TaggableModel.tagged_with(['rails, css'], :on => :needs, :any => true).tagged_with(['c++', 'java'], :on => :offerings, :any => true).to_a.should == [bob]
138
143
  end
139
-
144
+
140
145
  if ActiveRecord::VERSION::MAJOR >= 3
141
146
  it "should not return read-only records" do
142
147
  TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
@@ -146,10 +151,10 @@ describe "Taggable" do
146
151
  xit "should not return read-only records" do
147
152
  # apparantly, there is no way to set readonly to false in a scope if joins are made
148
153
  end
149
-
154
+
150
155
  it "should be possible to return writable records" do
151
156
  TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
152
- TaggableModel.tagged_with("ruby").first(:readonly => false).should_not be_readonly
157
+ TaggableModel.tagged_with("ruby").first(:readonly => false).should_not be_readonly
153
158
  end
154
159
  end
155
160
 
@@ -174,10 +179,10 @@ describe "Taggable" do
174
179
  bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
175
180
  frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
176
181
  charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby, java")
177
-
182
+
178
183
  TaggableModel.tagged_with('rails').all_tag_counts.should have(3).items
179
184
  TaggableModel.tagged_with('rails').all_tag_counts.any? { |tag| tag.name == 'java' }.should be_false
180
-
185
+
181
186
  # Test specific join syntaxes:
182
187
  frank.untaggable_models.create!
183
188
  TaggableModel.tagged_with('rails').scoped(:joins => :untaggable_models).all_tag_counts.should have(2).items
@@ -203,15 +208,15 @@ describe "Taggable" do
203
208
  TaggableModel.tagged_with("ruby, rails", :order => 'taggable_models.name').to_a.should == [bob, frank]
204
209
  TaggableModel.tagged_with(["ruby", "rails"], :order => 'taggable_models.name').to_a.should == [bob, frank]
205
210
  end
206
-
211
+
207
212
  it "should be able to find tagged with quotation marks" do
208
213
  bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive, 'I love the ,comma,'")
209
214
  TaggableModel.tagged_with("'I love the ,comma,'").should include(bob)
210
215
  end
211
-
216
+
212
217
  it "should be able to find tagged with invalid tags" do
213
- bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive")
214
- TaggableModel.tagged_with("sad, happier").should_not include(bob)
218
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive")
219
+ TaggableModel.tagged_with("sad, happier").should_not include(bob)
215
220
  end
216
221
 
217
222
  it "should be able to find tagged with any tag" do
@@ -260,11 +265,12 @@ describe "Taggable" do
260
265
 
261
266
  TaggableModel.tagged_with("lazy", :exclude => true).to_a.should == [frank, steve]
262
267
  end
263
-
268
+
264
269
  it "should return an empty scope for empty tags" do
265
270
  TaggableModel.tagged_with('').should == []
266
271
  TaggableModel.tagged_with(' ').should == []
267
- TaggableModel.tagged_with(nil).should == []
272
+ TaggableModel.tagged_with(nil).should == []
273
+ TaggableModel.tagged_with([]).should == []
268
274
  end
269
275
 
270
276
  it "should not create duplicate taggings" do
@@ -275,12 +281,12 @@ describe "Taggable" do
275
281
  bob.save
276
282
  }.should change(ActsAsTaggableOn::Tagging, :count).by(1)
277
283
  end
278
-
284
+
279
285
  describe "Associations" do
280
286
  before(:each) do
281
287
  @taggable = TaggableModel.create(:tag_list => "awesome, epic")
282
288
  end
283
-
289
+
284
290
  it "should not remove tags when creating associated objects" do
285
291
  @taggable.untaggable_models.create!
286
292
  @taggable.reload
@@ -296,6 +302,10 @@ describe "Taggable" do
296
302
  it "should return all column names joined for TaggableModel GROUP clause" do
297
303
  @taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.id, taggable_models.name, taggable_models.type"
298
304
  end
305
+
306
+ it "should return all column names joined for NonStandardIdTaggableModel GROUP clause" do
307
+ @taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.#{TaggableModel.primary_key}, taggable_models.name, taggable_models.type"
308
+ end
299
309
  end
300
310
 
301
311
  describe "Single Table Inheritance" do
@@ -304,45 +314,97 @@ describe "Taggable" do
304
314
  @inherited_same = InheritingTaggableModel.new(:name => "inherited same")
305
315
  @inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
306
316
  end
307
-
317
+
308
318
  it "should be able to save tags for inherited models" do
309
319
  @inherited_same.tag_list = "bob, kelso"
310
320
  @inherited_same.save
311
321
  InheritingTaggableModel.tagged_with("bob").first.should == @inherited_same
312
322
  end
313
-
323
+
314
324
  it "should find STI tagged models on the superclass" do
315
325
  @inherited_same.tag_list = "bob, kelso"
316
326
  @inherited_same.save
317
327
  TaggableModel.tagged_with("bob").first.should == @inherited_same
318
328
  end
319
-
329
+
320
330
  it "should be able to add on contexts only to some subclasses" do
321
331
  @inherited_different.part_list = "fork, spoon"
322
332
  @inherited_different.save
323
333
  InheritingTaggableModel.tagged_with("fork", :on => :parts).should be_empty
324
334
  AlteredInheritingTaggableModel.tagged_with("fork", :on => :parts).first.should == @inherited_different
325
335
  end
326
-
336
+
327
337
  it "should have different tag_counts_on for inherited models" do
328
338
  @inherited_same.tag_list = "bob, kelso"
329
339
  @inherited_same.save!
330
340
  @inherited_different.tag_list = "fork, spoon"
331
341
  @inherited_different.save!
332
-
342
+
333
343
  InheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso)
334
344
  AlteredInheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(fork spoon)
335
345
  TaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso fork spoon)
336
346
  end
337
-
347
+
338
348
  it 'should store same tag without validation conflict' do
339
349
  @taggable.tag_list = 'one'
340
350
  @taggable.save!
341
-
351
+
342
352
  @inherited_same.tag_list = 'one'
343
353
  @inherited_same.save!
344
-
354
+
345
355
  @inherited_same.update_attributes! :name => 'foo'
346
356
  end
347
357
  end
358
+
359
+ describe "NonStandardIdTaggable" do
360
+ before(:each) do
361
+ clean_database!
362
+ @taggable = NonStandardIdTaggableModel.new(:name => "Bob Jones")
363
+ @taggables = [@taggable, NonStandardIdTaggableModel.new(:name => "John Doe")]
364
+ end
365
+
366
+ it "should have tag types" do
367
+ [:tags, :languages, :skills, :needs, :offerings].each do |type|
368
+ NonStandardIdTaggableModel.tag_types.should include type
369
+ end
370
+
371
+ @taggable.tag_types.should == NonStandardIdTaggableModel.tag_types
372
+ end
373
+
374
+ it "should have tag_counts_on" do
375
+ NonStandardIdTaggableModel.tag_counts_on(:tags).all.should be_empty
376
+
377
+ @taggable.tag_list = ["awesome", "epic"]
378
+ @taggable.save
379
+
380
+ NonStandardIdTaggableModel.tag_counts_on(:tags).length.should == 2
381
+ @taggable.tag_counts_on(:tags).length.should == 2
382
+ end
383
+
384
+ it "should be able to create tags" do
385
+ @taggable.skill_list = "ruby, rails, css"
386
+ @taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
387
+
388
+ lambda {
389
+ @taggable.save
390
+ }.should change(ActsAsTaggableOn::Tag, :count).by(3)
391
+
392
+ @taggable.reload
393
+ @taggable.skill_list.sort.should == %w(ruby rails css).sort
394
+ end
395
+
396
+ it "should be able to create tags through the tag list directly" do
397
+ @taggable.tag_list_on(:test).add("hello")
398
+ @taggable.tag_list_cache_on(:test).should_not be_empty
399
+ @taggable.tag_list_on(:test).should == ["hello"]
400
+
401
+ @taggable.save
402
+ @taggable.save_tags
403
+
404
+ @taggable.reload
405
+ @taggable.tag_list_on(:test).should == ["hello"]
406
+ end
407
+ end
348
408
  end
409
+
410
+
@@ -17,6 +17,20 @@ describe "Tagger" do
17
17
  @user.owned_tags.size == 2
18
18
  end
19
19
 
20
+ it "should scope objects returned by tagged_with by owners" do
21
+ @taggable2 = TaggableModel.create(:name => "Jim Jones")
22
+ @taggable3 = TaggableModel.create(:name => "Jane Doe")
23
+
24
+ @user2 = TaggableUser.new
25
+ @user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
26
+ @user2.tag(@taggable2, :with => 'ruby, scheme', :on => :tags)
27
+ @user2.tag(@taggable3, :with => 'ruby, scheme', :on => :tags)
28
+
29
+ TaggableModel.tagged_with(%w(ruby scheme), :owned_by => @user).count.should == 1
30
+ TaggableModel.tagged_with(%w(ruby scheme), :owned_by => @user2).count.should == 2
31
+
32
+ end
33
+
20
34
  it "should not overlap tags from different taggers" do
21
35
  @user2 = TaggableUser.new
22
36
  lambda{
@@ -4,16 +4,15 @@ describe ActsAsTaggableOn::Utils do
4
4
  describe "like_operator" do
5
5
  before(:each) do
6
6
  clean_database!
7
- TaggableModel.write_inheritable_attribute(:tag_types, [])
8
7
  TaggableModel.acts_as_taggable_on(:tags, :languages, :skills, :needs, :offerings)
9
8
  @taggable = TaggableModel.new(:name => "Bob Jones")
10
9
  end
11
-
10
+
12
11
  it "should return 'ILIKE' when the adapter is PostgreSQL" do
13
12
  TaggableModel.connection.stub(:adapter_name).and_return("PostgreSQL")
14
13
  TaggableModel.send(:like_operator).should == "ILIKE"
15
14
  end
16
-
15
+
17
16
  it "should return 'LIKE' when the adapter is not PostgreSQL" do
18
17
  TaggableModel.connection.stub(:adapter_name).and_return("MySQL")
19
18
  TaggableModel.send(:like_operator).should == "LIKE"
@@ -16,4 +16,4 @@ postgresql:
16
16
  username: postgres
17
17
  password:
18
18
  database: acts_as_taggable_on
19
- encoding: utf8
19
+ encoding: utf8
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ # Generators are not automatically loaded by Rails
4
+ require 'generators/acts_as_taggable_on/migration/migration_generator'
5
+
6
+ describe ActsAsTaggableOn::MigrationGenerator do
7
+ # Tell the generator where to put its output (what it thinks of as Rails.root)
8
+ destination File.expand_path("../../../../../tmp", __FILE__)
9
+
10
+ before do
11
+ prepare_destination
12
+ Rails::Generators.options[:rails][:orm] = :active_record
13
+ end
14
+ describe 'no arguments' do
15
+ before { run_generator }
16
+
17
+ describe 'db/migrate/acts_as_taggable_on_migration.rb' do
18
+ subject { file('db/migrate/acts_as_taggable_on_migration.rb') }
19
+ it { should be_a_migration }
20
+ end
21
+ end
22
+ end
@@ -11,7 +11,7 @@ class CachedModel < ActiveRecord::Base
11
11
  end
12
12
 
13
13
  class OtherCachedModel < ActiveRecord::Base
14
- acts_as_taggable_on :languages
14
+ acts_as_taggable_on :languages, :statuses, :glasses
15
15
  end
16
16
 
17
17
  class OtherTaggableModel < ActiveRecord::Base
@@ -32,4 +32,13 @@ end
32
32
 
33
33
  class UntaggableModel < ActiveRecord::Base
34
34
  belongs_to :taggable_model
35
- end
35
+ end
36
+
37
+ class NonStandardIdTaggableModel < ActiveRecord::Base
38
+ set_primary_key "an_id"
39
+ acts_as_taggable
40
+ acts_as_taggable_on :languages
41
+ acts_as_taggable_on :skills
42
+ acts_as_taggable_on :needs, :offerings
43
+ has_many :untaggable_models
44
+ end
@@ -21,6 +21,11 @@ ActiveRecord::Schema.define :version => 0 do
21
21
  t.column :type, :string
22
22
  end
23
23
 
24
+ create_table :non_standard_id_taggable_models, :primary_key => "an_id", :force => true do |t|
25
+ t.column :name, :string
26
+ t.column :type, :string
27
+ end
28
+
24
29
  create_table :untaggable_models, :force => true do |t|
25
30
  t.column :taggable_model_id, :integer
26
31
  t.column :name, :string
@@ -36,6 +41,8 @@ ActiveRecord::Schema.define :version => 0 do
36
41
  t.column :name, :string
37
42
  t.column :type, :string
38
43
  t.column :cached_language_list, :string
44
+ t.column :cached_status_list, :string
45
+ t.column :cached_glass_list, :string
39
46
  end
40
47
 
41
48
  create_table :taggable_users, :force => true do |t|
@@ -19,6 +19,7 @@ end
19
19
 
20
20
  Bundler.require
21
21
  require File.expand_path('../../lib/acts-as-taggable-on', __FILE__)
22
+ require 'ammeter/init'
22
23
 
23
24
  unless [].respond_to?(:freq)
24
25
  class Array