acts-as-taggable-on 9.0.1 → 10.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.
- checksums.yaml +4 -4
- data/.github/workflows/spec.yml +8 -9
- data/Appraisals +6 -6
- data/CHANGELOG.md +8 -2
- data/CONTRIBUTING.md +3 -3
- data/README.md +14 -1
- data/acts-as-taggable-on.gemspec +4 -5
- data/gemfiles/activerecord_7.0.gemfile +1 -1
- data/gemfiles/activerecord_7.1.gemfile +18 -0
- data/lib/acts-as-taggable-on.rb +7 -1
- data/lib/acts_as_taggable_on/tag.rb +4 -3
- data/lib/acts_as_taggable_on/taggable/tagged_with_query/query_base.rb +11 -2
- data/lib/acts_as_taggable_on/tagger.rb +3 -9
- data/lib/acts_as_taggable_on/tagging.rb +1 -1
- data/lib/acts_as_taggable_on/utils.rb +0 -4
- data/lib/acts_as_taggable_on/version.rb +1 -1
- data/lib/tasks/example/acts_as_taggable_on.rb.example +8 -0
- data/lib/tasks/install_initializer.rake +23 -0
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +2 -2
- data/spec/acts_as_taggable_on/tag_list_spec.rb +2 -2
- data/spec/acts_as_taggable_on/tag_spec.rb +45 -2
- data/spec/acts_as_taggable_on/taggable_spec.rb +50 -43
- data/spec/acts_as_taggable_on/tagger_spec.rb +8 -8
- data/spec/acts_as_taggable_on/tagging_spec.rb +28 -2
- data/spec/support/database.rb +2 -6
- data/spec/support/database_cleaner.rb +4 -0
- metadata +11 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a72bfb5bb88aea5ec247738610ca9010077e75742abbc6ae28b0fecb441a01e
|
4
|
+
data.tar.gz: 1772385e3921e9b85bbcb0d4407b5dea817f22a62dad50a268375819885541e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72de9a02773b616bc6a55fb4b21879aa104c5100dd9f3b6538334c08bccef904a4e680796c46699e51cafab205301be0288ca831ef26e59c011cd37ab0f9210d
|
7
|
+
data.tar.gz: 74a5235a9c37a7c3793bcb8a3b4c5e2a2a6fe2a74a499a5b1fb2ef208e9c8b1f778c2b120f6921019eb7961a9020f0b05bede0aab97234612039b67171a1f7a7
|
data/.github/workflows/spec.yml
CHANGED
@@ -10,16 +10,17 @@ jobs:
|
|
10
10
|
DB: ${{ matrix.db }}
|
11
11
|
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
12
12
|
strategy:
|
13
|
+
fail-fast: false
|
13
14
|
matrix:
|
14
15
|
ruby:
|
16
|
+
- 3.2
|
17
|
+
- 3.1
|
15
18
|
- "3.0"
|
16
19
|
- 2.7
|
17
|
-
- 2.6
|
18
|
-
- 2.5
|
19
20
|
gemfile:
|
20
|
-
- gemfiles/
|
21
|
-
- gemfiles/activerecord_6.1.gemfile
|
21
|
+
- gemfiles/activerecord_7.1.gemfile
|
22
22
|
- gemfiles/activerecord_7.0.gemfile
|
23
|
+
- gemfiles/activerecord_6.1.gemfile
|
23
24
|
db:
|
24
25
|
- mysql
|
25
26
|
- postgresql
|
@@ -32,10 +33,8 @@ jobs:
|
|
32
33
|
db: postgresql
|
33
34
|
gemfile: gemfiles/activerecord_7.0.gemfile
|
34
35
|
exclude:
|
35
|
-
- ruby: 2
|
36
|
-
gemfile: gemfiles/
|
37
|
-
- ruby: 2.6
|
38
|
-
gemfile: gemfiles/activerecord_7.0.gemfile
|
36
|
+
- ruby: 3.2
|
37
|
+
gemfile: gemfiles/activerecord_6.0.gemfile
|
39
38
|
|
40
39
|
services:
|
41
40
|
postgres:
|
@@ -61,7 +60,7 @@ jobs:
|
|
61
60
|
--health-timeout 5s
|
62
61
|
--health-retries 5
|
63
62
|
steps:
|
64
|
-
- uses: actions/checkout@
|
63
|
+
- uses: actions/checkout@v4
|
65
64
|
- name: Set up Ruby
|
66
65
|
uses: ruby/setup-ruby@v1
|
67
66
|
with:
|
data/Appraisals
CHANGED
@@ -1,11 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
appraise 'activerecord-6.0' do
|
4
|
-
gem 'activerecord', '~> 6.0.0'
|
5
|
-
gem 'pg'
|
6
|
-
gem 'mysql2', '~> 0.5'
|
7
|
-
end
|
8
|
-
|
9
3
|
appraise 'activerecord-6.1' do
|
10
4
|
gem 'activerecord', '~> 6.1.0'
|
11
5
|
gem 'pg'
|
@@ -17,3 +11,9 @@ appraise 'activerecord-7.0' do
|
|
17
11
|
gem 'pg'
|
18
12
|
gem 'mysql2', '~> 0.5'
|
19
13
|
end
|
14
|
+
|
15
|
+
appraise 'activerecord-7.1' do
|
16
|
+
gem 'activerecord', '~> 7.1.0'
|
17
|
+
gem 'pg'
|
18
|
+
gem 'mysql2', '~> 0.5'
|
19
|
+
end
|
data/CHANGELOG.md
CHANGED
@@ -10,7 +10,13 @@ Each change should fall into categories that would affect whether the release is
|
|
10
10
|
|
11
11
|
As such, _Breaking Changes_ are major. _Features_ would map to either major or minor. _Fixes_, _Performance_, and _Misc_ are either minor or patch, the difference being kind of fuzzy for the purposes of history. Adding _Documentation_ (including tests) would be patch level.
|
12
12
|
|
13
|
-
### [
|
13
|
+
### [v10.0.0) / unreleased](https://github.com/mbleigh/acts-as-taggable-on/compare/v9.0.1...master)
|
14
|
+
* Features
|
15
|
+
* [@glampr Add support for prefix and suffix searches alongside previously supported containment (wildcard) searches](https://github.com/mbleigh/acts-as-taggable-on/pull/1082)
|
16
|
+
* [@donquxiote Add support for horizontally sharded databases](https://github.com/mbleigh/acts-as-taggable-on/pull/1079)
|
17
|
+
* [aovertus Remove restriction around ActiveRecord 7.x versions allowing support until next major is released](https://github.com/mbleigh/acts-as-taggable-on/pull/1110)
|
18
|
+
|
19
|
+
### [v9.0.1) / 2022-01-07](https://github.com/mbleigh/acts-as-taggable-on/compare/v9.0.0..v9.0.1)
|
14
20
|
* Fixes
|
15
21
|
* Fix migration that generate default index
|
16
22
|
|
@@ -39,7 +45,7 @@ As such, _Breaking Changes_ are major. _Features_ would map to either major or m
|
|
39
45
|
* Features
|
40
46
|
* [@kvokka Rails 6.1 support](https://github.com/mbleigh/acts-as-taggable-on/pull/1013)
|
41
47
|
* Fixes
|
42
|
-
* [@nbulaj Add support for Ruby 2.7 and it's kwargs](https://github.com/mbleigh/acts-as-taggable-on/pull/
|
48
|
+
* [@nbulaj Add support for Ruby 2.7 and it's kwargs](https://github.com/mbleigh/acts-as-taggable-on/pull/999)
|
43
49
|
* [@Andythurlow @endorfin case sensitivity fix for tagged_with](https://github.com/mbleigh/acts-as-taggable-on/pull/965)
|
44
50
|
|
45
51
|
### [6.5.0 / 2019-11-07](https://github.com/mbleigh/acts-as-taggable-on/compare/v6.0.0...v6.5.0)
|
data/CONTRIBUTING.md
CHANGED
@@ -29,7 +29,7 @@
|
|
29
29
|
* Use well-described, small (atomic) commits.
|
30
30
|
* Include links to any relevant github issues.
|
31
31
|
* *Don't* change the VERSION file.
|
32
|
-
6. Extra Credit: [Confirm it runs and tests pass on the rubies specified in the
|
32
|
+
6. Extra Credit: [Confirm it runs and tests pass on the rubies specified in the Github Actions config](.github/workflows/spec.yml). I will otherwise confirm it runs on these.
|
33
33
|
|
34
34
|
How I handle pull requests:
|
35
35
|
|
@@ -44,13 +44,13 @@ How I handle pull requests:
|
|
44
44
|
|
45
45
|
* [A Note About Git Commit Messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
46
46
|
* [http://stopwritingramblingcommitmessages.com/](http://stopwritingramblingcommitmessages.com/)
|
47
|
-
* [ThoughtBot style guide](https://github.com/thoughtbot/guides/tree/
|
47
|
+
* [ThoughtBot style guide](https://github.com/thoughtbot/guides/tree/main/git)
|
48
48
|
|
49
49
|
### About Pull Requests (PR's)
|
50
50
|
|
51
51
|
* [All Your Open Source Code Are Belong To Us](http://www.benjaminfleischer.com/2013/07/30/all-your-open-source-code-are-belong-to-us/)
|
52
52
|
* [Using Pull Requests](https://help.github.com/articles/using-pull-requests)
|
53
|
-
* [Github pull requests made easy](
|
53
|
+
* [Github pull requests made easy](https://www.element84.com/blog/github-pull-requests-made-easy)
|
54
54
|
|
55
55
|
## Documentation
|
56
56
|
|
data/README.md
CHANGED
@@ -255,7 +255,11 @@ User.tagged_with(["awesome", "cool"], :exclude => true)
|
|
255
255
|
User.tagged_with(['awesome', 'cool'], :on => :tags, :any => true).tagged_with(['smart', 'shy'], :on => :skills, :any => true)
|
256
256
|
```
|
257
257
|
|
258
|
-
|
258
|
+
#### Wildcard tag search
|
259
|
+
You now have the following options for prefix, suffix and containment search, along with `:any` or `:exclude` option.
|
260
|
+
Use `wild: :suffix` to place a wildcard at the end of the tag. It will be looking for `awesome%` and `cool%` in SQL.
|
261
|
+
Use `wild: :prefix` to place a wildcard at the beginning of the tag. It will be looking for `%awesome` and `%cool` in SQL.
|
262
|
+
Use `wild: true` to place a wildcard both at the beginning and the end of the tag. It will be looking for `%awesome%` and `%cool%` in SQL.
|
259
263
|
|
260
264
|
__Tip:__ `User.tagged_with([])` or `User.tagged_with('')` will return `[]`, an empty set of records.
|
261
265
|
|
@@ -296,6 +300,15 @@ to allow for dynamic tag contexts (this could be user generated tag contexts!)
|
|
296
300
|
User.tagged_with("same", :on => :customs) # => [@user]
|
297
301
|
```
|
298
302
|
|
303
|
+
### Finding tags based on context
|
304
|
+
|
305
|
+
You can find tags for a specific context by using the ```for_context``` scope:
|
306
|
+
|
307
|
+
```ruby
|
308
|
+
ActsAsTaggableOn::Tag.for_context(:tags)
|
309
|
+
ActsAsTaggableOn::Tag.for_context(:skills)
|
310
|
+
```
|
311
|
+
|
299
312
|
### Tag Parsers
|
300
313
|
|
301
314
|
If you want to change how tags are parsed, you can define your own implementation:
|
data/acts-as-taggable-on.gemspec
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
|
3
|
-
|
4
|
-
require 'acts_as_taggable_on/version'
|
2
|
+
|
3
|
+
require_relative 'lib/acts_as_taggable_on/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |gem|
|
7
6
|
gem.name = 'acts-as-taggable-on'
|
@@ -16,13 +15,13 @@ Gem::Specification.new do |gem|
|
|
16
15
|
gem.files = `git ls-files`.split($/)
|
17
16
|
gem.test_files = gem.files.grep(%r{^spec/})
|
18
17
|
gem.require_paths = ['lib']
|
19
|
-
gem.required_ruby_version = '>= 2.
|
18
|
+
gem.required_ruby_version = '>= 2.7.0'
|
20
19
|
|
21
20
|
if File.exist?('UPGRADING.md')
|
22
21
|
gem.post_install_message = File.read('UPGRADING.md')
|
23
22
|
end
|
24
23
|
|
25
|
-
gem.add_runtime_dependency 'activerecord', '>= 6.
|
24
|
+
gem.add_runtime_dependency 'activerecord', '>= 6.1', '< 7.2'
|
26
25
|
|
27
26
|
gem.add_development_dependency 'rspec-rails'
|
28
27
|
gem.add_development_dependency 'rspec-its'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "activerecord", "~> 7.1.0"
|
6
|
+
gem "pg"
|
7
|
+
gem "mysql2", "~> 0.5"
|
8
|
+
|
9
|
+
group :local_development do
|
10
|
+
gem "guard"
|
11
|
+
gem "guard-rspec"
|
12
|
+
gem "appraisal"
|
13
|
+
gem "rake"
|
14
|
+
gem "sqlite3"
|
15
|
+
gem "byebug", platforms: [:mri]
|
16
|
+
end
|
17
|
+
|
18
|
+
gemspec path: "../"
|
data/lib/acts-as-taggable-on.rb
CHANGED
@@ -66,7 +66,7 @@ module ActsAsTaggableOn
|
|
66
66
|
:remove_unused_tags, :default_parser,
|
67
67
|
:tags_counter, :tags_table,
|
68
68
|
:taggings_table
|
69
|
-
attr_reader :delimiter, :strict_case_match
|
69
|
+
attr_reader :delimiter, :strict_case_match, :base_class
|
70
70
|
|
71
71
|
def initialize
|
72
72
|
@delimiter = ','
|
@@ -79,6 +79,7 @@ module ActsAsTaggableOn
|
|
79
79
|
@force_binary_collation = false
|
80
80
|
@tags_table = :tags
|
81
81
|
@taggings_table = :taggings
|
82
|
+
@base_class = '::ActiveRecord::Base'
|
82
83
|
end
|
83
84
|
|
84
85
|
def strict_case_match=(force_cs)
|
@@ -119,6 +120,11 @@ WARNING
|
|
119
120
|
end
|
120
121
|
end
|
121
122
|
|
123
|
+
def base_class=(base_class)
|
124
|
+
raise "base_class must be a String" unless base_class.is_a?(String)
|
125
|
+
@base_class = base_class
|
126
|
+
end
|
127
|
+
|
122
128
|
end
|
123
129
|
|
124
130
|
setup
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActsAsTaggableOn
|
4
|
-
class Tag <
|
4
|
+
class Tag < ActsAsTaggableOn.base_class.constantize
|
5
5
|
self.table_name = ActsAsTaggableOn.tags_table
|
6
6
|
|
7
7
|
### ASSOCIATIONS:
|
@@ -84,10 +84,11 @@ module ActsAsTaggableOn
|
|
84
84
|
tries ||= 3
|
85
85
|
comparable_tag_name = comparable_name(tag_name)
|
86
86
|
existing_tag = existing_tags.find { |tag| comparable_name(tag.name) == comparable_tag_name }
|
87
|
-
existing_tag
|
87
|
+
next existing_tag if existing_tag
|
88
|
+
|
89
|
+
transaction(requires_new: true) { create(name: tag_name) }
|
88
90
|
rescue ActiveRecord::RecordNotUnique
|
89
91
|
if (tries -= 1).positive?
|
90
|
-
ActiveRecord::Base.connection.execute 'ROLLBACK'
|
91
92
|
existing_tags = named_any(list)
|
92
93
|
retry
|
93
94
|
end
|
@@ -33,7 +33,7 @@ module ActsAsTaggableOn
|
|
33
33
|
matches_attribute = matches_attribute.lower unless ActsAsTaggableOn.strict_case_match
|
34
34
|
|
35
35
|
if options[:wild].present?
|
36
|
-
matches_attribute.matches(
|
36
|
+
matches_attribute.matches(wildcard_escaped_tag(tag), '!', ActsAsTaggableOn.strict_case_match)
|
37
37
|
else
|
38
38
|
matches_attribute.matches(escaped_tag(tag), '!', ActsAsTaggableOn.strict_case_match)
|
39
39
|
end
|
@@ -45,7 +45,7 @@ module ActsAsTaggableOn
|
|
45
45
|
|
46
46
|
if options[:wild].present?
|
47
47
|
matches_attribute.matches_any(tag_list.map do |tag|
|
48
|
-
|
48
|
+
wildcard_escaped_tag(tag)
|
49
49
|
end, '!', ActsAsTaggableOn.strict_case_match)
|
50
50
|
else
|
51
51
|
matches_attribute.matches_any(tag_list.map do |tag|
|
@@ -59,6 +59,15 @@ module ActsAsTaggableOn
|
|
59
59
|
ActsAsTaggableOn::Utils.escape_like(tag)
|
60
60
|
end
|
61
61
|
|
62
|
+
def wildcard_escaped_tag(tag)
|
63
|
+
case options[:wild]
|
64
|
+
when :suffix then "#{escaped_tag(tag)}%"
|
65
|
+
when :prefix then "%#{escaped_tag(tag)}"
|
66
|
+
when true then "%#{escaped_tag(tag)}%"
|
67
|
+
else escaped_tag(tag)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
62
71
|
def adjust_taggings_alias(taggings_alias)
|
63
72
|
taggings_alias = "taggings_alias_#{Digest::SHA1.hexdigest(taggings_alias)}" if taggings_alias.size > 75
|
64
73
|
taggings_alias
|
@@ -40,9 +40,7 @@ module ActsAsTaggableOn
|
|
40
40
|
false
|
41
41
|
end
|
42
42
|
|
43
|
-
|
44
|
-
tagger?
|
45
|
-
end
|
43
|
+
alias is_tagger? tagger?
|
46
44
|
end
|
47
45
|
|
48
46
|
module InstanceMethods
|
@@ -75,9 +73,7 @@ module ActsAsTaggableOn
|
|
75
73
|
self.class.is_tagger?
|
76
74
|
end
|
77
75
|
|
78
|
-
|
79
|
-
tagger?
|
80
|
-
end
|
76
|
+
alias is_tagger? tagger?
|
81
77
|
end
|
82
78
|
|
83
79
|
module SingletonMethods
|
@@ -85,9 +81,7 @@ module ActsAsTaggableOn
|
|
85
81
|
true
|
86
82
|
end
|
87
83
|
|
88
|
-
|
89
|
-
tagger?
|
90
|
-
end
|
84
|
+
alias is_tagger? tagger?
|
91
85
|
end
|
92
86
|
end
|
93
87
|
end
|
@@ -26,10 +26,6 @@ module ActsAsTaggableOn
|
|
26
26
|
using_postgresql? ? 'ILIKE' : 'LIKE'
|
27
27
|
end
|
28
28
|
|
29
|
-
def legacy_activerecord?
|
30
|
-
ActiveRecord.version <= Gem::Version.new('5.3.0')
|
31
|
-
end
|
32
|
-
|
33
29
|
# escape _ and % characters in strings, since these are wildcards in SQL.
|
34
30
|
def escape_like(str)
|
35
31
|
str.gsub(/[!%_]/) { |x| "!#{x}" }
|
@@ -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
|
@@ -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
|
-
}
|
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
|
@@ -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
|
108
|
-
expect
|
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
|
|
@@ -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
|
-
}
|
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
|
-
}
|
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
|
-
}
|
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
|
-
}
|
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
|
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
|
-
}
|
567
|
+
}.to change(ActsAsTaggableOn::Tagging, :count).by(3)
|
564
568
|
end
|
565
569
|
it '#<<' do
|
566
|
-
expect
|
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
|
-
}
|
575
|
+
}.to change(ActsAsTaggableOn::Tagging, :count).by(2)
|
572
576
|
|
573
577
|
end
|
574
578
|
|
575
579
|
it 'unicode' do
|
576
580
|
|
577
|
-
expect
|
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
|
-
}
|
586
|
+
}.to change(ActsAsTaggableOn::Tagging, :count).by(1)
|
583
587
|
|
584
588
|
end
|
585
589
|
|
586
590
|
it '#=' do
|
587
|
-
expect
|
591
|
+
expect {
|
588
592
|
bob.tag_list = ['Happy', 'Happy']
|
589
593
|
bob.save
|
590
|
-
}
|
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
|
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
|
-
}
|
607
|
+
}.to change(ActsAsTaggableOn::Tagging, :count).by(2)
|
604
608
|
|
605
609
|
end
|
606
610
|
|
607
611
|
it '#add' do
|
608
|
-
expect
|
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
|
-
}
|
617
|
+
}.to change(ActsAsTaggableOn::Tagging, :count).by(2)
|
614
618
|
end
|
615
619
|
|
616
620
|
it 'unicode' do
|
617
621
|
|
618
|
-
expect
|
622
|
+
expect {
|
619
623
|
bob.tag_list.add 'ПРИВЕТ'
|
620
624
|
bob.tag_list.add 'привет', 'Привет'
|
621
625
|
bob.save
|
622
|
-
}
|
626
|
+
}.to change(ActsAsTaggableOn::Tagging, :count).by(1)
|
623
627
|
|
624
628
|
end
|
625
629
|
|
626
630
|
it '#=' do
|
627
|
-
expect
|
631
|
+
expect {
|
628
632
|
bob.tag_list = ['Happy', 'HAPPY']
|
629
633
|
bob.save
|
630
|
-
}
|
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
|
-
|
640
|
-
|
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
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
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
|
-
}
|
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)
|
@@ -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
|
-
}
|
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
|
-
}
|
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
|
-
}
|
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
|
-
}
|
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
|
-
}
|
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
|
data/spec/support/database.rb
CHANGED
@@ -18,18 +18,14 @@ if ActiveRecord.version >= Gem::Version.new('7.0.0.alpha2')
|
|
18
18
|
else
|
19
19
|
ActiveRecord::Base.default_timezone = :utc
|
20
20
|
end
|
21
|
-
config =
|
22
|
-
ActiveRecord::Base.configurations.configs_for(env_name: db_name)
|
23
|
-
else
|
24
|
-
ActiveSupport::HashWithIndifferentAccess.new(ActiveRecord::Base.configurations[db_name])
|
25
|
-
end
|
21
|
+
config = ActiveRecord::Base.configurations.configs_for(env_name: db_name)
|
26
22
|
|
27
23
|
begin
|
28
24
|
ActiveRecord::Base.establish_connection(db_name.to_sym)
|
29
25
|
ActiveRecord::Base.connection
|
30
26
|
rescue StandardError
|
31
27
|
case db_name
|
32
|
-
when /mysql/
|
28
|
+
when /(mysql)/
|
33
29
|
ActiveRecord::Base.establish_connection(config.merge('database' => nil))
|
34
30
|
ActiveRecord::Base.connection.create_database(config['database'],
|
35
31
|
{ charset: 'utf8', collation: 'utf8_unicode_ci' })
|
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:
|
4
|
+
version: 10.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Bleigh
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2023-10-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -17,20 +17,20 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '6.
|
20
|
+
version: '6.1'
|
21
21
|
- - "<"
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: '7.
|
23
|
+
version: '7.2'
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
27
27
|
requirements:
|
28
28
|
- - ">="
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
version: '6.
|
30
|
+
version: '6.1'
|
31
31
|
- - "<"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '7.
|
33
|
+
version: '7.2'
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: rspec-rails
|
36
36
|
requirement: !ruby/object:Gem::Requirement
|
@@ -133,6 +133,7 @@ files:
|
|
133
133
|
- gemfiles/activerecord_6.0.gemfile
|
134
134
|
- gemfiles/activerecord_6.1.gemfile
|
135
135
|
- gemfiles/activerecord_7.0.gemfile
|
136
|
+
- gemfiles/activerecord_7.1.gemfile
|
136
137
|
- lib/acts-as-taggable-on.rb
|
137
138
|
- lib/acts_as_taggable_on.rb
|
138
139
|
- lib/acts_as_taggable_on/default_parser.rb
|
@@ -157,6 +158,8 @@ files:
|
|
157
158
|
- lib/acts_as_taggable_on/tags_helper.rb
|
158
159
|
- lib/acts_as_taggable_on/utils.rb
|
159
160
|
- lib/acts_as_taggable_on/version.rb
|
161
|
+
- lib/tasks/example/acts_as_taggable_on.rb.example
|
162
|
+
- lib/tasks/install_initializer.rake
|
160
163
|
- lib/tasks/tags_collate_utf8.rake
|
161
164
|
- spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb
|
162
165
|
- spec/acts_as_taggable_on/acts_as_tagger_spec.rb
|
@@ -207,14 +210,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
207
210
|
requirements:
|
208
211
|
- - ">="
|
209
212
|
- !ruby/object:Gem::Version
|
210
|
-
version: 2.
|
213
|
+
version: 2.7.0
|
211
214
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
212
215
|
requirements:
|
213
216
|
- - ">="
|
214
217
|
- !ruby/object:Gem::Version
|
215
218
|
version: '0'
|
216
219
|
requirements: []
|
217
|
-
rubygems_version: 3.
|
220
|
+
rubygems_version: 3.4.12
|
218
221
|
signing_key:
|
219
222
|
specification_version: 4
|
220
223
|
summary: Advanced tagging for Rails.
|