make_taggable 1.7.0 → 1.7.1
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/CHANGELOG.md +35 -14
- data/CONTRIBUTING.md +23 -1
- data/UPGRADING.md +104 -23
- data/lib/make_taggable/tag.rb +5 -1
- data/lib/make_taggable/taggable/tagged_with_query/exclude_tags_query.rb +0 -38
- data/lib/make_taggable/taggable/tagged_with_query.rb +7 -0
- data/lib/make_taggable/version.rb +1 -1
- metadata +105 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d9c3e4304ca2823d876d4ca13908a567b0d5be1521a2c413b39a98d8adbe976f
|
|
4
|
+
data.tar.gz: df708319ca01f12577082ada78b8a9093508aabd8013aeb5f02354c92ba29be7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f5e8acad4d9852e5ce2c7d44135126d2a1b70cff524c69d3d93531f271a4eb454608f2ca172945393cc4779e824201500b05dba24979854826725e146691bd4f
|
|
7
|
+
data.tar.gz: 044af9b759de0558e7adf4617ecfac8264421f7effc04db64556233215aa93357aaac951754378fdab996f1e3522a43eb3d363c9a66e978132a82886140fed84
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ All notable changes to this project are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
6
6
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.7.1] - 2026-08-23
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- `tagged_with(..., exclude: true, match_all: true)` raised `HAVING clause on a non-aggregate query`.
|
|
13
|
+
The combination is now refused with an `ArgumentError`: `:match_all` selects records carrying only
|
|
14
|
+
the given tags, `:exclude` selects records carrying none of them, and no set of records satisfies
|
|
15
|
+
both.
|
|
16
|
+
|
|
17
|
+
- Tag creation retries on a deadlock as well as on a duplicate key. Under concurrent writes MySQL
|
|
18
|
+
reports one or the other depending on how two inserts of the same name interleave on the unique
|
|
19
|
+
index, and only the second was being retried -- so a busy application could see
|
|
20
|
+
`ActiveRecord::Deadlocked` escape from what is meant to be a transparent retry.
|
|
21
|
+
|
|
22
|
+
### Internal
|
|
23
|
+
|
|
24
|
+
- Coverage is measured (`COVERAGE=1 bundle exec rspec`) with a floor enforced in CI. It found the
|
|
25
|
+
`:exclude` combination above on its first run.
|
|
26
|
+
- The concurrency example runs. It had been skipped since the fork and referenced a `Barrier` class
|
|
27
|
+
that did not exist, so concurrent tag creation was untested outright.
|
|
28
|
+
- `UPGRADING.md`, which ships as the gem's post-install message, covers the whole 1.x line rather
|
|
29
|
+
than stopping at 1.0.
|
|
30
|
+
- The 1.7.0 changelog entry was split in two by a stray `Unreleased` heading, so the per-context
|
|
31
|
+
vocabulary changes that shipped in it were filed as unreleased. Folded back into 1.7.0.
|
|
32
|
+
|
|
8
33
|
## [1.7.0] - 2026-08-23
|
|
9
34
|
|
|
10
35
|
### Added
|
|
@@ -23,20 +48,6 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
23
48
|
This is a global setting. Giving different contexts different vocabularies is a separate thing and
|
|
24
49
|
needs a `type` column -- see [docs/contexts.md](docs/contexts.md).
|
|
25
50
|
|
|
26
|
-
### Fixed
|
|
27
|
-
|
|
28
|
-
- A tag list came back in whatever order the database chose. An `ORDER BY` was applied only under
|
|
29
|
-
`make_ordered_taggable`, so `record.tag_list` could return the same tags in different orders on
|
|
30
|
-
different calls or adapters, and `tag_list_was` could report an original the list had never been
|
|
31
|
-
in. Lists are now always ordered by when each tag was applied; `preserve_tag_order` keeps its
|
|
32
|
-
existing meaning for writes and for change detection.
|
|
33
|
-
|
|
34
|
-
- The documentation for `find_or_create_tags_from_list_with_context` claimed overriding it keeps a
|
|
35
|
-
separate vocabulary for one context. It does not: it routes creation only, and without a `type`
|
|
36
|
-
column on the tags table nothing distinguishes a subclass's rows.
|
|
37
|
-
|
|
38
|
-
## [Unreleased]
|
|
39
|
-
|
|
40
51
|
### Changed
|
|
41
52
|
|
|
42
53
|
- The tag name uniqueness validation scopes itself by `type` when the tags table has that column, so
|
|
@@ -50,6 +61,16 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
50
61
|
|
|
51
62
|
### Fixed
|
|
52
63
|
|
|
64
|
+
- A tag list came back in whatever order the database chose. An `ORDER BY` was applied only under
|
|
65
|
+
`make_ordered_taggable`, so `record.tag_list` could return the same tags in different orders on
|
|
66
|
+
different calls or adapters, and `tag_list_was` could report an original the list had never been
|
|
67
|
+
in. Lists are now always ordered by when each tag was applied; `preserve_tag_order` keeps its
|
|
68
|
+
existing meaning for writes and for change detection.
|
|
69
|
+
|
|
70
|
+
- The documentation for `find_or_create_tags_from_list_with_context` claimed overriding it keeps a
|
|
71
|
+
separate vocabulary for one context. It does not: it routes creation only, and without a `type`
|
|
72
|
+
column on the tags table nothing distinguishes a subclass's rows.
|
|
73
|
+
|
|
53
74
|
- The recipe in `docs/contexts.md` for giving a context its own vocabulary did not work. It had you
|
|
54
75
|
add a `type` column and widen the unique index, then failed on the model validation with `Name has
|
|
55
76
|
already been taken` -- after a column had been added and a unique index rewritten on a production
|
data/CONTRIBUTING.md
CHANGED
|
@@ -17,7 +17,9 @@ smallest model and code that reproduce the problem.
|
|
|
17
17
|
5. Format: `bundle exec standardrb --fix`.
|
|
18
18
|
6. Document any public API you added: `bundle exec yard stats --list-undoc` should report 100%.
|
|
19
19
|
7. Add an entry to [CHANGELOG.md](CHANGELOG.md) under "unreleased".
|
|
20
|
-
8.
|
|
20
|
+
8. If it changes something an application would have to react to, add it to
|
|
21
|
+
[UPGRADING.md](UPGRADING.md) — that file is the gem's post-install message.
|
|
22
|
+
9. Open a pull request explaining what changed and why.
|
|
21
23
|
|
|
22
24
|
Keep commits small and well described, and link any relevant issues. Don't bump the version — that
|
|
23
25
|
happens at release.
|
|
@@ -38,6 +40,26 @@ DATABASE_ADAPTER=postgresql DATABASE_URL=postgres://localhost/make_taggable_test
|
|
|
38
40
|
DATABASE_ADAPTER=mysql2 DATABASE_URL=mysql2://root@127.0.0.1/make_taggable_test bundle exec rake
|
|
39
41
|
```
|
|
40
42
|
|
|
43
|
+
Worth doing before opening a pull request if you touched anything that generates SQL. Several bugs
|
|
44
|
+
in this gem only appeared on one adapter — SQLite in particular is forgiving enough to hide them,
|
|
45
|
+
and one of them passed on SQLite only because it treats an unresolvable double-quoted identifier as
|
|
46
|
+
a string literal.
|
|
47
|
+
|
|
48
|
+
The concurrency example does not run on SQLite. It needs an adapter that supports real concurrent
|
|
49
|
+
writes, so it is skipped there and runs on MySQL and PostgreSQL.
|
|
50
|
+
|
|
51
|
+
## Coverage
|
|
52
|
+
|
|
53
|
+
```shell
|
|
54
|
+
COVERAGE=1 bundle exec rspec
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The run fails if coverage drops below the floor set in `spec/spec_helper.rb`, and CI runs it on
|
|
58
|
+
every pull request. Raise the floor when coverage improves; never lower it to make a run pass.
|
|
59
|
+
|
|
60
|
+
Coverage is a way of finding untested paths, not a target. The last time it was measured it pointed
|
|
61
|
+
straight at an untested option combination that turned out to be broken.
|
|
62
|
+
|
|
41
63
|
Across every supported Rails version:
|
|
42
64
|
|
|
43
65
|
```shell
|
data/UPGRADING.md
CHANGED
|
@@ -1,35 +1,116 @@
|
|
|
1
|
-
Upgrading
|
|
1
|
+
Upgrading MakeTaggable
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Only the changes that can ask something of you are listed. Everything else is
|
|
4
|
+
in CHANGELOG.md.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
--------------------------------------------------------------------------
|
|
7
|
+
1.7.0
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
acts_as_taggable_on :skills -> make_taggable :skills
|
|
9
|
-
acts_as_ordered_taggable -> make_ordered_taggable
|
|
10
|
-
acts_as_ordered_taggable_on :skills -> make_ordered_taggable :skills
|
|
11
|
-
acts_as_tagger -> make_tagger
|
|
9
|
+
Tag lists come back in a stable order
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
A list is now ordered by when each tag was applied. Before, an order was
|
|
12
|
+
only applied under make_ordered_taggable, so the database chose -- and on
|
|
13
|
+
PostgreSQL that could differ between calls.
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
Nothing to change. If you were sorting a list defensively, you no longer
|
|
16
|
+
need to.
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
New: a Tag class of your own
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
note that it now tags on :tags.
|
|
20
|
+
MakeTaggable.setup { |config| config.tag_class = "MyApp::Tag" }
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
Name the class as a String, in an initializer. See docs/configuration.md.
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
already holds duplicates -- docs/database.md has a snippet to clear them.
|
|
24
|
+
--------------------------------------------------------------------------
|
|
25
|
+
1.6.0
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
Eager loading tag lists actually works
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
rails db:migrate
|
|
29
|
+
Book.includes(:tags).each { |book| book.tag_list }
|
|
34
30
|
|
|
35
|
-
|
|
31
|
+
used to cost a query per record on top of the preload. It no longer does.
|
|
32
|
+
Nothing to change.
|
|
33
|
+
|
|
34
|
+
--------------------------------------------------------------------------
|
|
35
|
+
1.4.0 ACTION NEEDED
|
|
36
|
+
|
|
37
|
+
Tag lists are no longer in as_json
|
|
38
|
+
|
|
39
|
+
A tag list is not an Active Record attribute any more, so it is not in
|
|
40
|
+
attributes and not in as_json. This is what stops serialising a
|
|
41
|
+
collection querying once per record.
|
|
42
|
+
|
|
43
|
+
If an API response included a tag list, it will stop:
|
|
44
|
+
|
|
45
|
+
book.as_json # no "tag_list" key
|
|
46
|
+
book.as_json(methods: :tag_list) # ask for it
|
|
47
|
+
|
|
48
|
+
attributes["tag_list"] is gone too -- it always returned nil, while
|
|
49
|
+
tag_list returned the tags. Use tag_list.
|
|
50
|
+
|
|
51
|
+
Dirty tracking is unchanged: tag_list_changed?, _was, _change,
|
|
52
|
+
saved_change_to_tag_list? and changes all still work.
|
|
53
|
+
|
|
54
|
+
MakeTaggable::Taggable::TagListType is removed. It backed the attribute.
|
|
55
|
+
|
|
56
|
+
--------------------------------------------------------------------------
|
|
57
|
+
1.3.0 ACTION NEEDED
|
|
58
|
+
|
|
59
|
+
tagged_with no longer joins the taggings table
|
|
60
|
+
|
|
61
|
+
It tests for each tag with an EXISTS subquery instead. That is what stops
|
|
62
|
+
a record being returned once per matching tagging -- a tag applied in two
|
|
63
|
+
contexts used to return the record twice, and .count disagreed with the
|
|
64
|
+
number of records.
|
|
65
|
+
|
|
66
|
+
A taggings column is no longer in scope on the relation:
|
|
67
|
+
|
|
68
|
+
Book.tagged_with("sci-fi").order("taggings.created_at") # was fine
|
|
69
|
+
Book.tagged_with("sci-fi").joins(:taggings).order("taggings.created_at")
|
|
70
|
+
|
|
71
|
+
Joining brings back one row per tagging, so add .distinct if you want
|
|
72
|
+
records rather than matches.
|
|
73
|
+
|
|
74
|
+
Five indexes dropped from taggings
|
|
75
|
+
|
|
76
|
+
Migration 7 removes single-column indexes on tag_id, taggable_id,
|
|
77
|
+
taggable_type and tagger_id, plus a duplicate of the tagger pair. Each is
|
|
78
|
+
a leading column of an index that remains, so no query plan changes --
|
|
79
|
+
but every index was maintained on insert.
|
|
80
|
+
|
|
81
|
+
It is not applied automatically:
|
|
82
|
+
|
|
83
|
+
rails make_taggable_engine:install:migrations
|
|
84
|
+
|
|
85
|
+
--------------------------------------------------------------------------
|
|
86
|
+
1.2.0
|
|
87
|
+
|
|
88
|
+
A migration you may want
|
|
89
|
+
|
|
90
|
+
Migration 6 adds a partial unique index stopping duplicate unowned
|
|
91
|
+
taggings, which the existing index could not do because it spans the
|
|
92
|
+
nullable tagger columns. Install it the same way. MySQL has no partial
|
|
93
|
+
indexes, so it is a no-op there.
|
|
94
|
+
|
|
95
|
+
--------------------------------------------------------------------------
|
|
96
|
+
1.0.0 ACTION NEEDED
|
|
97
|
+
|
|
98
|
+
The declaration methods were renamed
|
|
99
|
+
|
|
100
|
+
acts_as_taggable -> make_taggable
|
|
101
|
+
acts_as_taggable_on :skills -> make_taggable :skills
|
|
102
|
+
acts_as_ordered_taggable -> make_ordered_taggable
|
|
103
|
+
acts_as_ordered_taggable_on :skills -> make_ordered_taggable :skills
|
|
104
|
+
|
|
105
|
+
Delimiters are literal strings
|
|
106
|
+
|
|
107
|
+
A delimiter is no longer interpolated into a pattern unescaped, so it no
|
|
108
|
+
longer needs escaping by hand:
|
|
109
|
+
|
|
110
|
+
MakeTaggable.delimiter = '\|' -> MakeTaggable.delimiter = "|"
|
|
111
|
+
|
|
112
|
+
make_taggable with no arguments tags on :tags
|
|
113
|
+
|
|
114
|
+
It previously added no contexts at all, despite the documentation.
|
|
115
|
+
|
|
116
|
+
Minimum versions are Ruby 3.2 and Active Record 7.2
|
data/lib/make_taggable/tag.rb
CHANGED
|
@@ -197,7 +197,11 @@ module MakeTaggable
|
|
|
197
197
|
# unwinds only the failed insert. Without one the caller's transaction
|
|
198
198
|
# is left in an aborted state and everything it had done is lost.
|
|
199
199
|
transaction(requires_new: true) { create(name: tag_name) }.tap { |tag| existing_tags << tag }
|
|
200
|
-
|
|
200
|
+
# A deadlock counts as losing the race, the same as a duplicate key.
|
|
201
|
+
# MySQL reports one or the other depending on how two inserts of the
|
|
202
|
+
# same name interleave on the unique index, and both mean the work
|
|
203
|
+
# should be re-read and retried rather than abandoned.
|
|
204
|
+
rescue ActiveRecord::RecordNotUnique, ActiveRecord::Deadlocked
|
|
201
205
|
if (tries -= 1).positive?
|
|
202
206
|
existing_tags = named_any(list).to_a
|
|
203
207
|
retry
|
|
@@ -13,7 +13,6 @@ module MakeTaggable::Taggable::TaggedWithQuery
|
|
|
13
13
|
def build
|
|
14
14
|
taggable_model.joins(owning_to_tagger)
|
|
15
15
|
.where(tags_not_in_list)
|
|
16
|
-
.having(tags_that_matches_count)
|
|
17
16
|
.readonly(false)
|
|
18
17
|
end
|
|
19
18
|
|
|
@@ -61,44 +60,7 @@ module MakeTaggable::Taggable::TaggedWithQuery
|
|
|
61
60
|
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
|
62
61
|
)
|
|
63
62
|
|
|
64
|
-
if options[:match_all].present?
|
|
65
|
-
arel_join = arel_join
|
|
66
|
-
.join(tagging_arel_table, Arel::Nodes::OuterJoin)
|
|
67
|
-
.on(
|
|
68
|
-
match_all_on_conditions
|
|
69
|
-
)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
63
|
arel_join.join_sources
|
|
73
64
|
end
|
|
74
|
-
|
|
75
|
-
def match_all_on_conditions
|
|
76
|
-
on_condition = tagging_arel_table[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
|
|
77
|
-
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
|
78
|
-
|
|
79
|
-
if options[:start_at].present?
|
|
80
|
-
on_condition = on_condition.and(tagging_arel_table[:created_at].gteq(options[:start_at]))
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
if options[:end_at].present?
|
|
84
|
-
on_condition = on_condition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
if options[:on].present?
|
|
88
|
-
on_condition = on_condition.and(context_predicate)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
on_condition
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def tags_that_matches_count
|
|
95
|
-
return [] unless options[:match_all].present?
|
|
96
|
-
|
|
97
|
-
taggable_model.find_by_sql(tag_arel_table.project(Arel.star.count).where(tags_match_type).to_sql)
|
|
98
|
-
|
|
99
|
-
tagging_arel_table[:taggable_id].count.eq(
|
|
100
|
-
tag_arel_table.project(Arel.star.count).where(tags_match_type)
|
|
101
|
-
)
|
|
102
|
-
end
|
|
103
65
|
end
|
|
104
66
|
end
|
|
@@ -22,6 +22,13 @@ module MakeTaggable::Taggable::TaggedWithQuery
|
|
|
22
22
|
# @return [ActiveRecord::Relation]
|
|
23
23
|
#
|
|
24
24
|
def self.build(taggable_model, tag_model, tagging_model, tag_list, options)
|
|
25
|
+
if options[:exclude].present? && options[:match_all].present?
|
|
26
|
+
raise ArgumentError,
|
|
27
|
+
":match_all and :exclude cannot be combined. :match_all selects records carrying only the " \
|
|
28
|
+
"given tags and nothing else, :exclude selects records carrying none of them, and there is " \
|
|
29
|
+
"no set of records that satisfies both."
|
|
30
|
+
end
|
|
31
|
+
|
|
25
32
|
if options[:exclude].present?
|
|
26
33
|
ExcludeTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
|
|
27
34
|
elsif options[:any].present?
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: make_taggable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.7.
|
|
4
|
+
version: 1.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthew Kennedy
|
|
@@ -92,41 +92,122 @@ metadata:
|
|
|
92
92
|
documentation_uri: https://github.com/MatthewKennedy/make_taggable/blob/master/docs
|
|
93
93
|
rubygems_mfa_required: 'true'
|
|
94
94
|
post_install_message: |
|
|
95
|
-
Upgrading
|
|
95
|
+
Upgrading MakeTaggable
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
Only the changes that can ask something of you are listed. Everything else is
|
|
98
|
+
in CHANGELOG.md.
|
|
98
99
|
|
|
99
|
-
|
|
100
|
+
--------------------------------------------------------------------------
|
|
101
|
+
1.7.0
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
acts_as_taggable_on :skills -> make_taggable :skills
|
|
103
|
-
acts_as_ordered_taggable -> make_ordered_taggable
|
|
104
|
-
acts_as_ordered_taggable_on :skills -> make_ordered_taggable :skills
|
|
105
|
-
acts_as_tagger -> make_tagger
|
|
103
|
+
Tag lists come back in a stable order
|
|
106
104
|
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
A list is now ordered by when each tag was applied. Before, an order was
|
|
106
|
+
only applied under make_ordered_taggable, so the database chose -- and on
|
|
107
|
+
PostgreSQL that could differ between calls.
|
|
109
108
|
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
Nothing to change. If you were sorting a list defensively, you no longer
|
|
110
|
+
need to.
|
|
112
111
|
|
|
113
|
-
|
|
112
|
+
New: a Tag class of your own
|
|
114
113
|
|
|
115
|
-
|
|
116
|
-
note that it now tags on :tags.
|
|
114
|
+
MakeTaggable.setup { |config| config.tag_class = "MyApp::Tag" }
|
|
117
115
|
|
|
118
|
-
|
|
116
|
+
Name the class as a String, in an initializer. See docs/configuration.md.
|
|
119
117
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
already holds duplicates -- docs/database.md has a snippet to clear them.
|
|
118
|
+
--------------------------------------------------------------------------
|
|
119
|
+
1.6.0
|
|
123
120
|
|
|
124
|
-
|
|
121
|
+
Eager loading tag lists actually works
|
|
125
122
|
|
|
126
|
-
|
|
127
|
-
rails db:migrate
|
|
123
|
+
Book.includes(:tags).each { |book| book.tag_list }
|
|
128
124
|
|
|
129
|
-
|
|
125
|
+
used to cost a query per record on top of the preload. It no longer does.
|
|
126
|
+
Nothing to change.
|
|
127
|
+
|
|
128
|
+
--------------------------------------------------------------------------
|
|
129
|
+
1.4.0 ACTION NEEDED
|
|
130
|
+
|
|
131
|
+
Tag lists are no longer in as_json
|
|
132
|
+
|
|
133
|
+
A tag list is not an Active Record attribute any more, so it is not in
|
|
134
|
+
attributes and not in as_json. This is what stops serialising a
|
|
135
|
+
collection querying once per record.
|
|
136
|
+
|
|
137
|
+
If an API response included a tag list, it will stop:
|
|
138
|
+
|
|
139
|
+
book.as_json # no "tag_list" key
|
|
140
|
+
book.as_json(methods: :tag_list) # ask for it
|
|
141
|
+
|
|
142
|
+
attributes["tag_list"] is gone too -- it always returned nil, while
|
|
143
|
+
tag_list returned the tags. Use tag_list.
|
|
144
|
+
|
|
145
|
+
Dirty tracking is unchanged: tag_list_changed?, _was, _change,
|
|
146
|
+
saved_change_to_tag_list? and changes all still work.
|
|
147
|
+
|
|
148
|
+
MakeTaggable::Taggable::TagListType is removed. It backed the attribute.
|
|
149
|
+
|
|
150
|
+
--------------------------------------------------------------------------
|
|
151
|
+
1.3.0 ACTION NEEDED
|
|
152
|
+
|
|
153
|
+
tagged_with no longer joins the taggings table
|
|
154
|
+
|
|
155
|
+
It tests for each tag with an EXISTS subquery instead. That is what stops
|
|
156
|
+
a record being returned once per matching tagging -- a tag applied in two
|
|
157
|
+
contexts used to return the record twice, and .count disagreed with the
|
|
158
|
+
number of records.
|
|
159
|
+
|
|
160
|
+
A taggings column is no longer in scope on the relation:
|
|
161
|
+
|
|
162
|
+
Book.tagged_with("sci-fi").order("taggings.created_at") # was fine
|
|
163
|
+
Book.tagged_with("sci-fi").joins(:taggings).order("taggings.created_at")
|
|
164
|
+
|
|
165
|
+
Joining brings back one row per tagging, so add .distinct if you want
|
|
166
|
+
records rather than matches.
|
|
167
|
+
|
|
168
|
+
Five indexes dropped from taggings
|
|
169
|
+
|
|
170
|
+
Migration 7 removes single-column indexes on tag_id, taggable_id,
|
|
171
|
+
taggable_type and tagger_id, plus a duplicate of the tagger pair. Each is
|
|
172
|
+
a leading column of an index that remains, so no query plan changes --
|
|
173
|
+
but every index was maintained on insert.
|
|
174
|
+
|
|
175
|
+
It is not applied automatically:
|
|
176
|
+
|
|
177
|
+
rails make_taggable_engine:install:migrations
|
|
178
|
+
|
|
179
|
+
--------------------------------------------------------------------------
|
|
180
|
+
1.2.0
|
|
181
|
+
|
|
182
|
+
A migration you may want
|
|
183
|
+
|
|
184
|
+
Migration 6 adds a partial unique index stopping duplicate unowned
|
|
185
|
+
taggings, which the existing index could not do because it spans the
|
|
186
|
+
nullable tagger columns. Install it the same way. MySQL has no partial
|
|
187
|
+
indexes, so it is a no-op there.
|
|
188
|
+
|
|
189
|
+
--------------------------------------------------------------------------
|
|
190
|
+
1.0.0 ACTION NEEDED
|
|
191
|
+
|
|
192
|
+
The declaration methods were renamed
|
|
193
|
+
|
|
194
|
+
acts_as_taggable -> make_taggable
|
|
195
|
+
acts_as_taggable_on :skills -> make_taggable :skills
|
|
196
|
+
acts_as_ordered_taggable -> make_ordered_taggable
|
|
197
|
+
acts_as_ordered_taggable_on :skills -> make_ordered_taggable :skills
|
|
198
|
+
|
|
199
|
+
Delimiters are literal strings
|
|
200
|
+
|
|
201
|
+
A delimiter is no longer interpolated into a pattern unescaped, so it no
|
|
202
|
+
longer needs escaping by hand:
|
|
203
|
+
|
|
204
|
+
MakeTaggable.delimiter = '\|' -> MakeTaggable.delimiter = "|"
|
|
205
|
+
|
|
206
|
+
make_taggable with no arguments tags on :tags
|
|
207
|
+
|
|
208
|
+
It previously added no contexts at all, despite the documentation.
|
|
209
|
+
|
|
210
|
+
Minimum versions are Ruby 3.2 and Active Record 7.2
|
|
130
211
|
rdoc_options: []
|
|
131
212
|
require_paths:
|
|
132
213
|
- lib
|