acts-as-taggable-on 9.0.0 → 11.0.0

Sign up to get free protection for your applications and to get access to all the features.
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