acts-as-taggable-on 9.0.0 → 11.0.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/spec.yml +12 -14
  3. data/Appraisals +10 -7
  4. data/CHANGELOG.md +22 -3
  5. data/CONTRIBUTING.md +3 -3
  6. data/Gemfile +0 -1
  7. data/README.md +16 -4
  8. data/acts-as-taggable-on.gemspec +5 -5
  9. data/db/migrate/1_acts_as_taggable_on_migration.rb +0 -1
  10. data/gemfiles/activerecord_7.0.gemfile +2 -2
  11. data/gemfiles/{activerecord_6.0.gemfile → activerecord_7.1.gemfile} +2 -2
  12. data/gemfiles/{activerecord_6.1.gemfile → activerecord_7.2.gemfile} +2 -2
  13. data/lib/{acts_as_taggable_on → acts-as-taggable-on}/tag.rb +4 -3
  14. data/lib/acts-as-taggable-on/taggable/caching.rb +46 -0
  15. data/lib/{acts_as_taggable_on → acts-as-taggable-on}/taggable/collection.rb +5 -4
  16. data/lib/{acts_as_taggable_on → acts-as-taggable-on}/taggable/core.rb +4 -6
  17. data/lib/{acts_as_taggable_on → acts-as-taggable-on}/taggable/ownership.rb +5 -7
  18. data/lib/{acts_as_taggable_on → acts-as-taggable-on}/taggable/tagged_with_query/query_base.rb +11 -2
  19. data/lib/{acts_as_taggable_on → acts-as-taggable-on}/taggable.rb +8 -8
  20. data/lib/{acts_as_taggable_on → acts-as-taggable-on}/tagger.rb +5 -13
  21. data/lib/{acts_as_taggable_on → acts-as-taggable-on}/tagging.rb +1 -1
  22. data/lib/{acts_as_taggable_on → acts-as-taggable-on}/utils.rb +0 -4
  23. data/lib/{acts_as_taggable_on → acts-as-taggable-on}/version.rb +1 -1
  24. data/lib/acts-as-taggable-on.rb +14 -29
  25. data/lib/tasks/example/acts-as-taggable-on.rb.example +8 -0
  26. data/lib/tasks/install_initializer.rake +23 -0
  27. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +5 -10
  28. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +2 -2
  29. data/spec/acts_as_taggable_on/caching_spec.rb +0 -4
  30. data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -3
  31. data/spec/acts_as_taggable_on/tag_spec.rb +45 -2
  32. data/spec/acts_as_taggable_on/taggable_spec.rb +51 -44
  33. data/spec/acts_as_taggable_on/tagger_spec.rb +8 -8
  34. data/spec/acts_as_taggable_on/tagging_spec.rb +28 -2
  35. data/spec/support/database.rb +3 -11
  36. data/spec/support/database_cleaner.rb +4 -0
  37. metadata +57 -36
  38. data/lib/acts_as_taggable_on/taggable/cache.rb +0 -92
  39. data/lib/acts_as_taggable_on.rb +0 -6
  40. /data/lib/{acts_as_taggable_on → acts-as-taggable-on}/default_parser.rb +0 -0
  41. /data/lib/{acts_as_taggable_on → acts-as-taggable-on}/engine.rb +0 -0
  42. /data/lib/{acts_as_taggable_on → acts-as-taggable-on}/generic_parser.rb +0 -0
  43. /data/lib/{acts_as_taggable_on → acts-as-taggable-on}/tag_list.rb +0 -0
  44. /data/lib/{acts_as_taggable_on → acts-as-taggable-on}/taggable/related.rb +0 -0
  45. /data/lib/{acts_as_taggable_on → acts-as-taggable-on}/taggable/tag_list_type.rb +0 -0
  46. /data/lib/{acts_as_taggable_on → acts-as-taggable-on}/taggable/tagged_with_query/all_tags_query.rb +0 -0
  47. /data/lib/{acts_as_taggable_on → acts-as-taggable-on}/taggable/tagged_with_query/any_tags_query.rb +0 -0
  48. /data/lib/{acts_as_taggable_on → acts-as-taggable-on}/taggable/tagged_with_query/exclude_tags_query.rb +0 -0
  49. /data/lib/{acts_as_taggable_on → acts-as-taggable-on}/taggable/tagged_with_query.rb +0 -0
  50. /data/lib/{acts_as_taggable_on → acts-as-taggable-on}/tags_helper.rb +0 -0
@@ -1,43 +1,21 @@
1
1
  require 'active_record'
2
2
  require 'active_record/version'
3
3
  require 'active_support/core_ext/module'
4
+ require 'zeitwerk'
5
+
6
+ loader = Zeitwerk::Loader.for_gem
7
+ loader.inflector.inflect "acts-as-taggable-on" => "ActsAsTaggableOn"
8
+ loader.setup
4
9
 
5
10
  begin
6
11
  require 'rails/engine'
7
- require 'acts_as_taggable_on/engine'
12
+ require 'acts-as-taggable-on/engine'
8
13
  rescue LoadError
9
-
10
14
  end
11
15
 
12
16
  require 'digest/sha1'
13
17
 
14
18
  module ActsAsTaggableOn
15
- extend ActiveSupport::Autoload
16
-
17
- autoload :Tag
18
- autoload :TagList
19
- autoload :GenericParser
20
- autoload :DefaultParser
21
- autoload :Taggable
22
- autoload :Tagger
23
- autoload :Tagging
24
- autoload :TagsHelper
25
- autoload :VERSION
26
-
27
- autoload_under 'taggable' do
28
- autoload :Cache
29
- autoload :Collection
30
- autoload :Core
31
- autoload :Dirty
32
- autoload :Ownership
33
- autoload :Related
34
- autoload :TagListType
35
- end
36
-
37
- autoload :Utils
38
- autoload :Compatibility
39
-
40
-
41
19
  class DuplicateTagError < StandardError
42
20
  end
43
21
 
@@ -66,7 +44,7 @@ module ActsAsTaggableOn
66
44
  :remove_unused_tags, :default_parser,
67
45
  :tags_counter, :tags_table,
68
46
  :taggings_table
69
- attr_reader :delimiter, :strict_case_match
47
+ attr_reader :delimiter, :strict_case_match, :base_class
70
48
 
71
49
  def initialize
72
50
  @delimiter = ','
@@ -79,6 +57,7 @@ module ActsAsTaggableOn
79
57
  @force_binary_collation = false
80
58
  @tags_table = :tags
81
59
  @taggings_table = :taggings
60
+ @base_class = '::ActiveRecord::Base'
82
61
  end
83
62
 
84
63
  def strict_case_match=(force_cs)
@@ -119,6 +98,11 @@ WARNING
119
98
  end
120
99
  end
121
100
 
101
+ def base_class=(base_class)
102
+ raise "base_class must be a String" unless base_class.is_a?(String)
103
+ @base_class = base_class
104
+ end
105
+
122
106
  end
123
107
 
124
108
  setup
@@ -128,6 +112,7 @@ ActiveSupport.on_load(:active_record) do
128
112
  extend ActsAsTaggableOn::Taggable
129
113
  include ActsAsTaggableOn::Tagger
130
114
  end
115
+
131
116
  ActiveSupport.on_load(:action_view) do
132
117
  include ActsAsTaggableOn::TagsHelper
133
118
  end
@@ -0,0 +1,8 @@
1
+ ActsAsTaggableOn.setup do |config|
2
+ # This works because the classes where the base class is a concern, Tag and Tagging
3
+ # are autoloaded, and won't be started until after the initializers run. The value
4
+ # must be a String, as the Rails Zeitwerk autoloader will not allow models to be
5
+ # referenced at initialization time.
6
+ #
7
+ # config.base_class = 'ApplicationRecord'
8
+ end
@@ -0,0 +1,23 @@
1
+ namespace :acts_as_taggable_on do
2
+
3
+ namespace :sharded_db do
4
+
5
+ desc "Install initializer setting custom base class"
6
+ task :install_initializer => [:environment, "config/initializers/foo"] do
7
+ source = File.join(
8
+ Gem.loaded_specs["acts-as-taggable-on"].full_gem_path,
9
+ "lib",
10
+ "tasks",
11
+ "examples",
12
+ "acts-as-taggable-on.rb.example"
13
+ )
14
+
15
+ destination = "config/initializers/acts-as-taggable-on.rb"
16
+
17
+ cp source, destination
18
+ end
19
+
20
+ directory "config/initializers"
21
+ end
22
+
23
+ end
@@ -197,7 +197,7 @@ describe 'Acts As Taggable On' do
197
197
 
198
198
  its(:language_list) { should == ['ruby', '.net']}
199
199
  its(:cached_language_list) { should == 'ruby, .net' } # passes
200
- its(:instance_variables) { should include((RUBY_VERSION < '1.9' ? '@language_list' : :@language_list)) }
200
+ its(:instance_variables) { should include(:@language_list) }
201
201
  end
202
202
 
203
203
  context 'status taggings cache after update' do
@@ -207,8 +207,8 @@ describe 'Acts As Taggable On' do
207
207
  its(:status_list) { should == ['happy', 'married'] }
208
208
  its(:cached_status_list) { should == 'happy, married' } # fails
209
209
  its(:cached_status_list) { should_not == '' } # fails, is blank
210
- its(:instance_variables) { should include((RUBY_VERSION < '1.9' ? '@status_list' : :@status_list)) }
211
- its(:instance_variables) { should_not include((RUBY_VERSION < '1.9' ? '@statu_list' : :@statu_list)) } # fails, note: one "s"
210
+ its(:instance_variables) { should include(:@status_list) }
211
+ its(:instance_variables) { should_not include(:@statu_list) } # fails, note: one "s"
212
212
 
213
213
  end
214
214
 
@@ -221,13 +221,8 @@ describe 'Acts As Taggable On' do
221
221
  its(:glass_list) { should == ['rectangle', 'aviator'] }
222
222
  its(:cached_glass_list) { should == 'rectangle, aviator' } # fails
223
223
  its(:cached_glass_list) { should_not == '' } # fails, is blank
224
- if RUBY_VERSION < '1.9'
225
- its(:instance_variables) { should include('@glass_list') }
226
- its(:instance_variables) { should_not include('@glas_list') } # fails, note: one "s"
227
- else
228
- its(:instance_variables) { should include(:@glass_list) }
229
- its(:instance_variables) { should_not include(:@glas_list) } # fails, note: one "s"
230
- end
224
+ its(:instance_variables) { should include(:@glass_list) }
225
+ its(:instance_variables) { should_not include(:@glas_list) } # fails, note: one "s"
231
226
 
232
227
  end
233
228
  end
@@ -70,9 +70,9 @@ describe 'acts_as_tagger' do
70
70
 
71
71
  it 'should throw an exception when the default is over-ridden' do
72
72
  expect(@taggable.tag_list_on(:foo_boo)).to be_empty
73
- expect(-> {
73
+ expect {
74
74
  @tagger.tag(@taggable, with: 'this, and, that', on: :foo_boo, force: false)
75
- }).to raise_error(RuntimeError)
75
+ }.to raise_error(RuntimeError)
76
76
  end
77
77
 
78
78
  it 'should not create the tag context on-the-fly when the default is over-ridden' do
@@ -16,10 +16,6 @@ describe 'Acts As Taggable On' do
16
16
  expect(@taggable).to respond_to(:save_tags)
17
17
  end
18
18
 
19
- it 'should add cached tag lists to the instance if cached column is not present' do
20
- expect(TaggableModel.new(name: 'Art Kram')).to_not respond_to(:save_cached_tag_list)
21
- end
22
-
23
19
  it 'should generate a cached column checker for each tag type' do
24
20
  expect(CachedModel).to respond_to(:caching_tag_list?)
25
21
  expect(OtherCachedModel).to respond_to(:caching_language_list?)
@@ -104,8 +104,8 @@ describe ActsAsTaggableOn::TagList do
104
104
 
105
105
  it 'should be able to call to_s on a frozen tag list' do
106
106
  tag_list.freeze
107
- expect(-> { tag_list.add('cool', 'rad,bodacious') }).to raise_error(RuntimeError)
108
- expect(-> { tag_list.to_s }).to_not raise_error
107
+ expect { tag_list.add('cool', 'rad,bodacious') }.to raise_error(RuntimeError)
108
+ expect { tag_list.to_s }.to_not raise_error
109
109
  end
110
110
  end
111
111
 
@@ -161,7 +161,7 @@ describe ActsAsTaggableOn::TagList do
161
161
  expect(parser).to have_received(:parse)
162
162
  end
163
163
 
164
- it 'should use the parser setted as attribute' do
164
+ it 'should use the parser set as attribute' do
165
165
  allow(parser_class).to receive(:new).with('new, tag').and_return(parser)
166
166
 
167
167
  tag_list = ActsAsTaggableOn::TagList.new('example')
@@ -100,9 +100,9 @@ describe ActsAsTaggableOn::Tag do
100
100
  end
101
101
 
102
102
  it 'should create by name' do
103
- expect(-> {
103
+ expect {
104
104
  ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('epic')
105
- }).to change(ActsAsTaggableOn::Tag, :count).by(1)
105
+ }.to change(ActsAsTaggableOn::Tag, :count).by(1)
106
106
  end
107
107
  end
108
108
 
@@ -182,6 +182,25 @@ describe ActsAsTaggableOn::Tag do
182
182
  it 'should return an empty array if no tags are specified' do
183
183
  expect(ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name([])).to be_empty
184
184
  end
185
+
186
+ context 'another tag is created concurrently', :database_cleaner_delete, if: supports_concurrency? do
187
+ it 'retries and finds tag if tag with same name created concurrently' do
188
+ tag_name = 'super'
189
+
190
+ expect(ActsAsTaggableOn::Tag).to receive(:create).with(name: tag_name) do
191
+ # Simulate concurrent tag creation
192
+ Thread.new do
193
+ ActsAsTaggableOn::Tag.new(name: tag_name).save!
194
+ end.join
195
+
196
+ raise ActiveRecord::RecordNotUnique
197
+ end
198
+
199
+ expect {
200
+ ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name(tag_name)
201
+ }.to change(ActsAsTaggableOn::Tag, :count).by(1)
202
+ end
203
+ end
185
204
  end
186
205
 
187
206
  it 'should require a name' do
@@ -352,4 +371,28 @@ describe ActsAsTaggableOn::Tag do
352
371
  end
353
372
  end
354
373
 
374
+ describe 'base_class' do
375
+ before do
376
+ class Foo < ActiveRecord::Base; end
377
+ end
378
+
379
+ context "default" do
380
+ it "inherits from ActiveRecord::Base" do
381
+
382
+ expect(ActsAsTaggableOn::Tag.ancestors).to include(ActiveRecord::Base)
383
+ expect(ActsAsTaggableOn::Tag.ancestors).to_not include(Foo)
384
+ end
385
+ end
386
+
387
+ context "custom" do
388
+ it "inherits from custom class" do
389
+
390
+ ActsAsTaggableOn.base_class = 'Foo'
391
+ hide_const("ActsAsTaggableOn::Tag")
392
+ load("lib/acts-as-taggable-on/tag.rb")
393
+
394
+ expect(ActsAsTaggableOn::Tag.ancestors).to include(Foo)
395
+ end
396
+ end
397
+ end
355
398
  end
@@ -27,9 +27,9 @@ describe 'Taggable To Preserve Order' do
27
27
  @taggable.tag_list = 'rails, ruby, css'
28
28
  expect(@taggable.instance_variable_get('@tag_list').instance_of?(ActsAsTaggableOn::TagList)).to be_truthy
29
29
 
30
- expect(-> {
30
+ expect{
31
31
  @taggable.save
32
- }).to change(ActsAsTaggableOn::Tag, :count).by(3)
32
+ }.to change(ActsAsTaggableOn::Tag, :count).by(3)
33
33
 
34
34
  @taggable.reload
35
35
  expect(@taggable.tag_list).to eq(%w(rails ruby css))
@@ -61,9 +61,9 @@ describe 'Taggable To Preserve Order' do
61
61
  @taggable.tag_list = 'pow, ruby, rails'
62
62
  expect(@taggable.instance_variable_get('@tag_list').instance_of?(ActsAsTaggableOn::TagList)).to be_truthy
63
63
 
64
- expect(-> {
64
+ expect {
65
65
  @taggable.save
66
- }).to change(ActsAsTaggableOn::Tag, :count).by(3)
66
+ }.to change(ActsAsTaggableOn::Tag, :count).by(3)
67
67
 
68
68
  @taggable.reload
69
69
  expect(@taggable.tags.map { |t| t.name }).to eq(%w(pow ruby rails))
@@ -157,9 +157,9 @@ describe 'Taggable' do
157
157
  @taggable.skill_list = 'ruby, rails, css'
158
158
  expect(@taggable.instance_variable_get('@skill_list').instance_of?(ActsAsTaggableOn::TagList)).to be_truthy
159
159
 
160
- expect(-> {
160
+ expect{
161
161
  @taggable.save
162
- }).to change(ActsAsTaggableOn::Tag, :count).by(3)
162
+ }.to change(ActsAsTaggableOn::Tag, :count).by(3)
163
163
 
164
164
  @taggable.reload
165
165
  expect(@taggable.skill_list.sort).to eq(%w(ruby rails css).sort)
@@ -480,6 +480,10 @@ describe 'Taggable' do
480
480
  jim = TaggableModel.create(name: 'Jim', tag_list: 'jim, steve')
481
481
 
482
482
  expect(TaggableModel.tagged_with(%w(bob tricia), wild: true, any: true).to_a.sort_by { |o| o.id }).to eq([bob, frank, steve])
483
+ expect(TaggableModel.tagged_with(%w(bob tricia), wild: :prefix, any: true).to_a.sort_by { |o| o.id }).to eq([bob, steve])
484
+ expect(TaggableModel.tagged_with(%w(bob tricia), wild: :suffix, any: true).to_a.sort_by { |o| o.id }).to eq([bob, frank])
485
+ expect(TaggableModel.tagged_with(%w(cia), wild: :prefix, any: true).to_a.sort_by { |o| o.id }).to eq([bob, steve])
486
+ expect(TaggableModel.tagged_with(%w(j), wild: :suffix, any: true).to_a.sort_by { |o| o.id }).to eq([frank, steve, jim])
483
487
  expect(TaggableModel.tagged_with(%w(bob tricia), wild: true, exclude: true).to_a).to eq([jim])
484
488
  expect(TaggableModel.tagged_with('ji', wild: true, any: true).to_a).to match_array([frank, jim])
485
489
  end
@@ -555,39 +559,39 @@ describe 'Taggable' do
555
559
  let(:bob) { TaggableModel.create(name: 'Bob') }
556
560
  context 'case sensitive' do
557
561
  it '#add' do
558
- expect(lambda {
562
+ expect {
559
563
  bob.tag_list.add 'happier'
560
564
  bob.tag_list.add 'happier'
561
565
  bob.tag_list.add 'happier', 'rich', 'funny'
562
566
  bob.save
563
- }).to change(ActsAsTaggableOn::Tagging, :count).by(3)
567
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(3)
564
568
  end
565
569
  it '#<<' do
566
- expect(lambda {
570
+ expect {
567
571
  bob.tag_list << 'social'
568
572
  bob.tag_list << 'social'
569
573
  bob.tag_list << 'social' << 'wow'
570
574
  bob.save
571
- }).to change(ActsAsTaggableOn::Tagging, :count).by(2)
575
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(2)
572
576
 
573
577
  end
574
578
 
575
579
  it 'unicode' do
576
580
 
577
- expect(lambda {
581
+ expect {
578
582
  bob.tag_list.add 'ПРИВЕТ'
579
583
  bob.tag_list.add 'ПРИВЕТ'
580
584
  bob.tag_list.add 'ПРИВЕТ', 'ПРИВЕТ'
581
585
  bob.save
582
- }).to change(ActsAsTaggableOn::Tagging, :count).by(1)
586
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(1)
583
587
 
584
588
  end
585
589
 
586
590
  it '#=' do
587
- expect(lambda {
591
+ expect {
588
592
  bob.tag_list = ['Happy', 'Happy']
589
593
  bob.save
590
- }).to change(ActsAsTaggableOn::Tagging, :count).by(1)
594
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(1)
591
595
  end
592
596
  end
593
597
  context 'case insensitive' do
@@ -595,39 +599,39 @@ describe 'Taggable' do
595
599
  after(:all) { ActsAsTaggableOn.force_lowercase = false }
596
600
 
597
601
  it '#<<' do
598
- expect(lambda {
602
+ expect {
599
603
  bob.tag_list << 'Alone'
600
604
  bob.tag_list << 'AloNe'
601
605
  bob.tag_list << 'ALONE' << 'In The dark'
602
606
  bob.save
603
- }).to change(ActsAsTaggableOn::Tagging, :count).by(2)
607
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(2)
604
608
 
605
609
  end
606
610
 
607
611
  it '#add' do
608
- expect(lambda {
612
+ expect {
609
613
  bob.tag_list.add 'forever'
610
614
  bob.tag_list.add 'ForEver'
611
615
  bob.tag_list.add 'FOREVER', 'ALONE'
612
616
  bob.save
613
- }).to change(ActsAsTaggableOn::Tagging, :count).by(2)
617
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(2)
614
618
  end
615
619
 
616
620
  it 'unicode' do
617
621
 
618
- expect(lambda {
622
+ expect {
619
623
  bob.tag_list.add 'ПРИВЕТ'
620
624
  bob.tag_list.add 'привет', 'Привет'
621
625
  bob.save
622
- }).to change(ActsAsTaggableOn::Tagging, :count).by(1)
626
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(1)
623
627
 
624
628
  end
625
629
 
626
630
  it '#=' do
627
- expect(lambda {
631
+ expect {
628
632
  bob.tag_list = ['Happy', 'HAPPY']
629
633
  bob.save
630
- }).to change(ActsAsTaggableOn::Tagging, :count).by(1)
634
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(1)
631
635
  end
632
636
 
633
637
 
@@ -636,26 +640,29 @@ describe 'Taggable' do
636
640
 
637
641
  end
638
642
 
639
- xit 'should not duplicate tags added on different threads', if: supports_concurrency?, skip: 'FIXME, Deadlocks in travis' do
640
- #TODO, try with more threads and fix deadlock
641
- thread_count = 4
642
- barrier = Barrier.new thread_count
643
+ it 'should not duplicate tags' do
644
+ connor = TaggableModel.new(name: 'Connor', tag_list: 'There, can, be, only, one')
643
645
 
644
- expect {
645
- thread_count.times.map do |idx|
646
- Thread.start do
647
- connor = TaggableModel.first_or_create(name: 'Connor')
648
- connor.tag_list = 'There, can, be, only, one'
649
- barrier.wait
650
- begin
651
- connor.save
652
- rescue ActsAsTaggableOn::DuplicateTagError
653
- # second save should succeed
654
- connor.save
655
- end
656
- end
657
- end.map(&:join)
658
- }.to change(ActsAsTaggableOn::Tag, :count).by(5)
646
+ allow(ActsAsTaggableOn::Tag).to receive(:create).and_call_original
647
+ expect(ActsAsTaggableOn::Tag).to receive(:create).with(name: 'can') do
648
+ # Simulate concurrent tag creation
649
+ ActsAsTaggableOn::Tag.new(name: 'can').save!
650
+
651
+ raise ActiveRecord::RecordNotUnique
652
+ end
653
+
654
+ expect(ActsAsTaggableOn::Tag).to receive(:create).with(name: 'be') do
655
+ # Simulate concurrent tag creation
656
+ ActsAsTaggableOn::Tag.new(name: 'be').save!
657
+
658
+ raise ActiveRecord::RecordNotUnique
659
+ end
660
+
661
+ expect { connor.save! }.to change(ActsAsTaggableOn::Tag, :count).by(5)
662
+
663
+ %w[There can only be one].each do |tag|
664
+ expect(TaggableModel.tagged_with(tag).count).to eq(1)
665
+ end
659
666
  end
660
667
  end
661
668
 
@@ -726,9 +733,9 @@ describe 'Taggable' do
726
733
  @taggable.skill_list = 'ruby, rails, css'
727
734
  expect(@taggable.instance_variable_get('@skill_list').instance_of?(ActsAsTaggableOn::TagList)).to be_truthy
728
735
 
729
- expect(-> {
736
+ expect {
730
737
  @taggable.save
731
- }).to change(ActsAsTaggableOn::Tag, :count).by(3)
738
+ }.to change(ActsAsTaggableOn::Tag, :count).by(3)
732
739
 
733
740
  @taggable.reload
734
741
  expect(@taggable.skill_list.sort).to eq(%w(ruby rails css).sort)
@@ -754,7 +761,7 @@ describe 'Taggable' do
754
761
  end
755
762
 
756
763
  # See https://github.com/mbleigh/acts-as-taggable-on/pull/457 for details
757
- context 'tag_counts and aggreating scopes, compatability with MySQL ' do
764
+ context 'tag_counts and aggreating scopes, compatibility with MySQL ' do
758
765
  before(:each) do
759
766
  TaggableModel.new(:name => 'Barb Jones').tap { |t| t.tag_list = %w(awesome fun) }.save
760
767
  TaggableModel.new(:name => 'John Doe').tap { |t| t.tag_list = %w(cool fun hella) }.save
@@ -61,10 +61,10 @@ describe 'Tagger' do
61
61
 
62
62
  it 'should not overlap tags from different taggers' do
63
63
  @user2 = User.new
64
- expect(-> {
64
+ expect {
65
65
  @user.tag(@taggable, with: 'ruby, scheme', on: :tags)
66
66
  @user2.tag(@taggable, with: 'java, python, lisp, ruby', on: :tags)
67
- }).to change(ActsAsTaggableOn::Tagging, :count).by(6)
67
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(6)
68
68
 
69
69
  [@user, @user2, @taggable].each(&:reload)
70
70
 
@@ -83,9 +83,9 @@ describe 'Tagger' do
83
83
  @user2.tag(@taggable, with: 'java, python, lisp, ruby', on: :tags)
84
84
  @user.tag(@taggable, with: 'ruby, scheme', on: :tags)
85
85
 
86
- expect(-> {
86
+ expect {
87
87
  @user2.tag(@taggable, with: 'java, python, lisp', on: :tags)
88
- }).to change(ActsAsTaggableOn::Tagging, :count).by(-1)
88
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(-1)
89
89
 
90
90
  [@user, @user2, @taggable].each(&:reload)
91
91
 
@@ -102,9 +102,9 @@ describe 'Tagger' do
102
102
  @user.tag(@taggable, with: 'awesome', on: :tags)
103
103
  @user2.tag(@taggable, with: 'awesome, epic', on: :tags)
104
104
 
105
- expect(-> {
105
+ expect {
106
106
  @user2.tag(@taggable, with: 'epic', on: :tags)
107
- }).to change(ActsAsTaggableOn::Tagging, :count).by(-1)
107
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(-1)
108
108
 
109
109
  @taggable.reload
110
110
  expect(@taggable.all_tags_list).to include('awesome')
@@ -119,9 +119,9 @@ describe 'Tagger' do
119
119
  expect(@taggable.tag_list).to eq(%w(ruby))
120
120
  expect(@taggable.all_tags_list.sort).to eq(%w(ruby scheme).sort)
121
121
 
122
- expect(-> {
122
+ expect {
123
123
  @taggable.update(tag_list: '')
124
- }).to change(ActsAsTaggableOn::Tagging, :count).by(-1)
124
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(-1)
125
125
 
126
126
  expect(@taggable.tag_list).to be_empty
127
127
  expect(@taggable.all_tags_list.sort).to eq(%w(ruby scheme).sort)
@@ -20,9 +20,9 @@ describe ActsAsTaggableOn::Tagging do
20
20
  @taggable = TaggableModel.create(name: 'Bob Jones')
21
21
  @tag = ActsAsTaggableOn::Tag.create(name: 'awesome')
22
22
 
23
- expect(-> {
23
+ expect {
24
24
  2.times { ActsAsTaggableOn::Tagging.create(taggable: @taggable, tag: @tag, context: 'tags') }
25
- }).to change(ActsAsTaggableOn::Tagging, :count).by(1)
25
+ }.to change(ActsAsTaggableOn::Tagging, :count).by(1)
26
26
  end
27
27
 
28
28
  it 'should not delete tags of other records' do
@@ -140,4 +140,30 @@ describe ActsAsTaggableOn::Tagging do
140
140
  end
141
141
  end
142
142
  end
143
+
144
+ describe 'base_class' do
145
+ before do
146
+ class Foo < ActiveRecord::Base; end
147
+ end
148
+
149
+ context "default" do
150
+ it "inherits from ActiveRecord::Base" do
151
+
152
+ expect(ActsAsTaggableOn::Tagging.ancestors).to include(ActiveRecord::Base)
153
+ expect(ActsAsTaggableOn::Tagging.ancestors).to_not include(Foo)
154
+ end
155
+ end
156
+
157
+ context "custom" do
158
+ it "inherits from custom class" do
159
+
160
+ ActsAsTaggableOn.base_class = 'Foo'
161
+ hide_const("ActsAsTaggableOn::Tagging")
162
+ load("lib/acts-as-taggable-on/tagging.rb")
163
+
164
+ expect(ActsAsTaggableOn::Tagging.ancestors).to include(Foo)
165
+ end
166
+ end
167
+ end
168
+
143
169
  end
@@ -13,23 +13,15 @@ ActiveRecord::Base.configurations = YAML.load_file(database_yml)
13
13
  ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), '../debug.log'))
14
14
  ActiveRecord::Base.logger.level = ENV['CI'] ? ::Logger::ERROR : ::Logger::DEBUG
15
15
  ActiveRecord::Migration.verbose = false
16
- if ActiveRecord.version >= Gem::Version.new('7.0.0.alpha2')
17
- ActiveRecord.default_timezone = :utc
18
- else
19
- ActiveRecord::Base.default_timezone = :utc
20
- end
21
- config = if ActiveRecord.version >= Gem::Version.new('6.1.0')
22
- ActiveRecord::Base.configurations.configs_for(env_name: db_name)
23
- else
24
- ActiveSupport::HashWithIndifferentAccess.new(ActiveRecord::Base.configurations[db_name])
25
- end
16
+ ActiveRecord.default_timezone = :utc
17
+ config = ActiveRecord::Base.configurations.configs_for(env_name: db_name)
26
18
 
27
19
  begin
28
20
  ActiveRecord::Base.establish_connection(db_name.to_sym)
29
21
  ActiveRecord::Base.connection
30
22
  rescue StandardError
31
23
  case db_name
32
- when /mysql/
24
+ when /(mysql)/
33
25
  ActiveRecord::Base.establish_connection(config.merge('database' => nil))
34
26
  ActiveRecord::Base.connection.create_database(config['database'],
35
27
  { charset: 'utf8', collation: 'utf8_unicode_ci' })
@@ -6,6 +6,10 @@ RSpec.configure do |config|
6
6
  DatabaseCleaner.clean
7
7
  end
8
8
 
9
+ config.before(:each, :database_cleaner_delete) do
10
+ DatabaseCleaner.strategy = :truncation
11
+ end
12
+
9
13
  config.after(:suite) do
10
14
  DatabaseCleaner.clean
11
15
  end