acts-as-taggable-on 4.0.0 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/spec.yml +95 -0
- data/.gitignore +1 -1
- data/Appraisals +12 -10
- data/CHANGELOG.md +206 -71
- data/CONTRIBUTING.md +13 -0
- data/Gemfile +1 -1
- data/README.md +79 -13
- data/acts-as-taggable-on.gemspec +2 -6
- data/db/migrate/1_acts_as_taggable_on_migration.rb +14 -8
- data/db/migrate/2_add_missing_unique_indices.rb +14 -9
- data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +9 -4
- data/db/migrate/4_add_missing_taggable_index.rb +8 -3
- data/db/migrate/5_change_collation_for_tag_names.rb +7 -2
- data/db/migrate/6_add_missing_indexes_on_taggings.rb +22 -0
- data/db/migrate/7_add_tenant_to_taggings.rb +16 -0
- data/gemfiles/activerecord_5.0.gemfile +11 -5
- data/gemfiles/activerecord_5.1.gemfile +21 -0
- data/gemfiles/activerecord_5.2.gemfile +21 -0
- data/gemfiles/activerecord_6.0.gemfile +21 -0
- data/gemfiles/activerecord_6.1.gemfile +23 -0
- data/lib/acts-as-taggable-on.rb +6 -2
- data/lib/acts_as_taggable_on/tag.rb +23 -23
- data/lib/acts_as_taggable_on/tag_list.rb +1 -0
- data/lib/acts_as_taggable_on/taggable.rb +18 -1
- data/lib/acts_as_taggable_on/taggable/cache.rb +38 -34
- data/lib/acts_as_taggable_on/taggable/collection.rb +9 -7
- data/lib/acts_as_taggable_on/taggable/core.rb +49 -179
- data/lib/acts_as_taggable_on/taggable/ownership.rb +16 -5
- data/lib/acts_as_taggable_on/taggable/related.rb +1 -1
- data/lib/acts_as_taggable_on/taggable/tag_list_type.rb +4 -0
- data/lib/acts_as_taggable_on/taggable/tagged_with_query.rb +16 -0
- data/lib/acts_as_taggable_on/taggable/tagged_with_query/all_tags_query.rb +111 -0
- data/lib/acts_as_taggable_on/taggable/tagged_with_query/any_tags_query.rb +70 -0
- data/lib/acts_as_taggable_on/taggable/tagged_with_query/exclude_tags_query.rb +82 -0
- data/lib/acts_as_taggable_on/taggable/tagged_with_query/query_base.rb +61 -0
- data/lib/acts_as_taggable_on/tagger.rb +3 -3
- data/lib/acts_as_taggable_on/tagging.rb +9 -4
- data/lib/acts_as_taggable_on/utils.rb +4 -4
- data/lib/acts_as_taggable_on/version.rb +1 -2
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +4 -12
- data/spec/acts_as_taggable_on/caching_spec.rb +34 -10
- data/spec/acts_as_taggable_on/{taggable/dirty_spec.rb → dirty_spec.rb} +28 -13
- data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +28 -8
- data/spec/acts_as_taggable_on/tag_spec.rb +16 -1
- data/spec/acts_as_taggable_on/taggable_spec.rb +22 -15
- data/spec/acts_as_taggable_on/tagger_spec.rb +2 -2
- data/spec/acts_as_taggable_on/tagging_spec.rb +26 -0
- data/spec/internal/app/models/altered_inheriting_taggable_model.rb +2 -0
- data/spec/internal/app/models/cached_model_with_array.rb +6 -0
- data/spec/internal/app/models/columns_override_model.rb +5 -0
- data/spec/internal/app/models/company.rb +1 -1
- data/spec/internal/app/models/inheriting_taggable_model.rb +2 -0
- data/spec/internal/app/models/market.rb +1 -1
- data/spec/internal/app/models/non_standard_id_taggable_model.rb +1 -1
- data/spec/internal/app/models/student.rb +2 -0
- data/spec/internal/app/models/taggable_model.rb +3 -0
- data/spec/internal/app/models/user.rb +1 -1
- data/spec/internal/config/database.yml.sample +4 -8
- data/spec/internal/db/schema.rb +17 -5
- data/spec/spec_helper.rb +0 -1
- data/spec/support/database.rb +4 -4
- metadata +29 -68
- data/.travis.yml +0 -36
- data/UPGRADING.md +0 -8
- data/db/migrate/6_add_missing_indexes.rb +0 -12
- data/gemfiles/activerecord_4.0.gemfile +0 -16
- data/gemfiles/activerecord_4.1.gemfile +0 -16
- data/gemfiles/activerecord_4.2.gemfile +0 -15
- data/lib/acts_as_taggable_on/taggable/dirty.rb +0 -36
- data/spec/internal/app/models/models.rb +0 -90
@@ -27,13 +27,16 @@ module ActsAsTaggableOn::Taggable
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
30
|
+
def owner_tags(owner)
|
31
31
|
if owner.nil?
|
32
|
-
scope = base_tags
|
32
|
+
scope = base_tags
|
33
33
|
else
|
34
|
-
scope = base_tags.where(
|
35
|
-
|
36
|
-
|
34
|
+
scope = base_tags.where(
|
35
|
+
"#{ActsAsTaggableOn::Tagging.table_name}" => {
|
36
|
+
tagger_id: owner.id,
|
37
|
+
tagger_type: owner.class.base_class.to_s
|
38
|
+
}
|
39
|
+
)
|
37
40
|
end
|
38
41
|
|
39
42
|
# when preserving tag order, return tags in created order
|
@@ -45,6 +48,14 @@ module ActsAsTaggableOn::Taggable
|
|
45
48
|
end
|
46
49
|
end
|
47
50
|
|
51
|
+
def owner_tags_on(owner, context)
|
52
|
+
owner_tags(owner).where(
|
53
|
+
"#{ActsAsTaggableOn::Tagging.table_name}" => {
|
54
|
+
context: context
|
55
|
+
}
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
48
59
|
def cached_owned_tag_list_on(context)
|
49
60
|
variable_name = "@owned_#{context}_list"
|
50
61
|
(instance_variable_defined?(variable_name) && instance_variable_get(variable_name)) || instance_variable_set(variable_name, {})
|
@@ -49,7 +49,7 @@ module ActsAsTaggableOn::Taggable
|
|
49
49
|
private
|
50
50
|
|
51
51
|
def exclude_self(klass, id)
|
52
|
-
"#{klass.
|
52
|
+
"#{klass.arel_table[klass.primary_key].not_eq(id).to_sql} AND" if [self.class.base_class, self.class].include? klass
|
53
53
|
end
|
54
54
|
|
55
55
|
def group_columns(klass)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'tagged_with_query/query_base'
|
2
|
+
require_relative 'tagged_with_query/exclude_tags_query'
|
3
|
+
require_relative 'tagged_with_query/any_tags_query'
|
4
|
+
require_relative 'tagged_with_query/all_tags_query'
|
5
|
+
|
6
|
+
module ActsAsTaggableOn::Taggable::TaggedWithQuery
|
7
|
+
def self.build(taggable_model, tag_model, tagging_model, tag_list, options)
|
8
|
+
if options[:exclude].present?
|
9
|
+
ExcludeTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
|
10
|
+
elsif options[:any].present?
|
11
|
+
AnyTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
|
12
|
+
else
|
13
|
+
AllTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module ActsAsTaggableOn::Taggable::TaggedWithQuery
|
2
|
+
class AllTagsQuery < QueryBase
|
3
|
+
def build
|
4
|
+
taggable_model.joins(each_tag_in_list)
|
5
|
+
.group(by_taggable)
|
6
|
+
.having(tags_that_matches_count)
|
7
|
+
.order(order_conditions)
|
8
|
+
.readonly(false)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def each_tag_in_list
|
14
|
+
arel_join = taggable_arel_table
|
15
|
+
|
16
|
+
tag_list.each do |tag|
|
17
|
+
tagging_alias = tagging_arel_table.alias(tagging_alias(tag))
|
18
|
+
arel_join = arel_join
|
19
|
+
.join(tagging_alias)
|
20
|
+
.on(on_conditions(tag, tagging_alias))
|
21
|
+
end
|
22
|
+
|
23
|
+
if options[:match_all].present?
|
24
|
+
arel_join = arel_join
|
25
|
+
.join(tagging_arel_table, Arel::Nodes::OuterJoin)
|
26
|
+
.on(
|
27
|
+
match_all_on_conditions
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
return arel_join.join_sources
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_conditions(tag, tagging_alias)
|
35
|
+
on_condition = tagging_alias[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
|
36
|
+
.and(tagging_alias[:taggable_type].eq(taggable_model.base_class.name))
|
37
|
+
.and(
|
38
|
+
tagging_alias[:tag_id].in(
|
39
|
+
tag_arel_table.project(tag_arel_table[:id]).where(tag_match_type(tag))
|
40
|
+
)
|
41
|
+
)
|
42
|
+
|
43
|
+
if options[:start_at].present?
|
44
|
+
on_condition = on_condition.and(tagging_alias[:created_at].gteq(options[:start_at]))
|
45
|
+
end
|
46
|
+
|
47
|
+
if options[:end_at].present?
|
48
|
+
on_condition = on_condition.and(tagging_alias[:created_at].lteq(options[:end_at]))
|
49
|
+
end
|
50
|
+
|
51
|
+
if options[:on].present?
|
52
|
+
on_condition = on_condition.and(tagging_alias[:context].eq(options[:on]))
|
53
|
+
end
|
54
|
+
|
55
|
+
if (owner = options[:owned_by]).present?
|
56
|
+
on_condition = on_condition.and(tagging_alias[:tagger_id].eq(owner.id))
|
57
|
+
.and(tagging_alias[:tagger_type].eq(owner.class.base_class.to_s))
|
58
|
+
end
|
59
|
+
|
60
|
+
on_condition
|
61
|
+
end
|
62
|
+
|
63
|
+
def match_all_on_conditions
|
64
|
+
on_condition = tagging_arel_table[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
|
65
|
+
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
66
|
+
|
67
|
+
if options[:start_at].present?
|
68
|
+
on_condition = on_condition.and(tagging_arel_table[:created_at].gteq(options[:start_at]))
|
69
|
+
end
|
70
|
+
|
71
|
+
if options[:end_at].present?
|
72
|
+
on_condition = on_condition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
|
73
|
+
end
|
74
|
+
|
75
|
+
if options[:on].present?
|
76
|
+
on_condition = on_condition.and(tagging_arel_table[:context].eq(options[:on]))
|
77
|
+
end
|
78
|
+
|
79
|
+
on_condition
|
80
|
+
end
|
81
|
+
|
82
|
+
def by_taggable
|
83
|
+
return [] unless options[:match_all].present?
|
84
|
+
|
85
|
+
taggable_arel_table[taggable_model.primary_key]
|
86
|
+
end
|
87
|
+
|
88
|
+
def tags_that_matches_count
|
89
|
+
return [] unless options[:match_all].present?
|
90
|
+
|
91
|
+
taggable_model.find_by_sql(tag_arel_table.project(Arel.star.count).where(tags_match_type).to_sql)
|
92
|
+
|
93
|
+
tagging_arel_table[:taggable_id].count.eq(
|
94
|
+
tag_arel_table.project(Arel.star.count).where(tags_match_type)
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
def order_conditions
|
99
|
+
order_by = []
|
100
|
+
order_by << tagging_arel_table.project(tagging_arel_table[Arel.star].count.as('taggings_count')).order('taggings_count DESC').to_sql if options[:order_by_matching_tag_count].present? && options[:match_all].blank?
|
101
|
+
|
102
|
+
order_by << options[:order] if options[:order].present?
|
103
|
+
order_by.join(', ')
|
104
|
+
end
|
105
|
+
|
106
|
+
def tagging_alias(tag)
|
107
|
+
alias_base_name = taggable_model.base_class.name.downcase
|
108
|
+
adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tag)}")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ActsAsTaggableOn::Taggable::TaggedWithQuery
|
2
|
+
class AnyTagsQuery < QueryBase
|
3
|
+
def build
|
4
|
+
taggable_model.select(all_fields)
|
5
|
+
.where(model_has_at_least_one_tag)
|
6
|
+
.order(Arel.sql(order_conditions))
|
7
|
+
.readonly(false)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def all_fields
|
13
|
+
taggable_arel_table[Arel.star]
|
14
|
+
end
|
15
|
+
|
16
|
+
def model_has_at_least_one_tag
|
17
|
+
tagging_arel_table.project(Arel.star).where(at_least_one_tag).exists
|
18
|
+
end
|
19
|
+
|
20
|
+
def at_least_one_tag
|
21
|
+
exists_contition = tagging_arel_table[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
|
22
|
+
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
23
|
+
.and(
|
24
|
+
tagging_arel_table[:tag_id].in(
|
25
|
+
tag_arel_table.project(tag_arel_table[:id]).where(tags_match_type)
|
26
|
+
)
|
27
|
+
)
|
28
|
+
|
29
|
+
if options[:start_at].present?
|
30
|
+
exists_contition = exists_contition.and(tagging_arel_table[:created_at].gteq(options[:start_at]))
|
31
|
+
end
|
32
|
+
|
33
|
+
if options[:end_at].present?
|
34
|
+
exists_contition = exists_contition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
|
35
|
+
end
|
36
|
+
|
37
|
+
if options[:on].present?
|
38
|
+
exists_contition = exists_contition.and(tagging_arel_table[:context].eq(options[:on]))
|
39
|
+
end
|
40
|
+
|
41
|
+
if (owner = options[:owned_by]).present?
|
42
|
+
exists_contition = exists_contition.and(tagging_arel_table[:tagger_id].eq(owner.id))
|
43
|
+
.and(tagging_arel_table[:tagger_type].eq(owner.class.base_class.to_s))
|
44
|
+
end
|
45
|
+
|
46
|
+
exists_contition
|
47
|
+
end
|
48
|
+
|
49
|
+
def order_conditions
|
50
|
+
order_by = []
|
51
|
+
if options[:order_by_matching_tag_count].present?
|
52
|
+
order_by << "(SELECT count(*) FROM #{tagging_model.table_name} WHERE #{at_least_one_tag.to_sql}) desc"
|
53
|
+
end
|
54
|
+
|
55
|
+
order_by << options[:order] if options[:order].present?
|
56
|
+
order_by.join(', ')
|
57
|
+
end
|
58
|
+
|
59
|
+
def alias_name(tag_list)
|
60
|
+
alias_base_name = taggable_model.base_class.name.downcase
|
61
|
+
taggings_context = options[:on] ? "_#{options[:on]}" : ''
|
62
|
+
|
63
|
+
taggings_alias = adjust_taggings_alias(
|
64
|
+
"#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tag_list.join('_'))}"
|
65
|
+
)
|
66
|
+
|
67
|
+
taggings_alias
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module ActsAsTaggableOn::Taggable::TaggedWithQuery
|
2
|
+
class ExcludeTagsQuery < QueryBase
|
3
|
+
def build
|
4
|
+
taggable_model.joins(owning_to_tagger)
|
5
|
+
.where(tags_not_in_list)
|
6
|
+
.having(tags_that_matches_count)
|
7
|
+
.readonly(false)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def tags_not_in_list
|
13
|
+
return taggable_arel_table[:id].not_in(
|
14
|
+
tagging_arel_table
|
15
|
+
.project(tagging_arel_table[:taggable_id])
|
16
|
+
.join(tag_arel_table)
|
17
|
+
.on(
|
18
|
+
tagging_arel_table[:tag_id].eq(tag_arel_table[:id])
|
19
|
+
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
20
|
+
.and(tags_match_type)
|
21
|
+
)
|
22
|
+
)
|
23
|
+
|
24
|
+
# FIXME: missing time scope, this is also missing in the original implementation
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def owning_to_tagger
|
29
|
+
return [] unless options[:owned_by].present?
|
30
|
+
|
31
|
+
owner = options[:owned_by]
|
32
|
+
|
33
|
+
arel_join = taggable_arel_table
|
34
|
+
.join(tagging_arel_table)
|
35
|
+
.on(
|
36
|
+
tagging_arel_table[:tagger_id].eq(owner.id)
|
37
|
+
.and(tagging_arel_table[:tagger_type].eq(owner.class.base_class.to_s))
|
38
|
+
.and(tagging_arel_table[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key]))
|
39
|
+
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
40
|
+
)
|
41
|
+
|
42
|
+
if options[:match_all].present?
|
43
|
+
arel_join = arel_join
|
44
|
+
.join(tagging_arel_table, Arel::Nodes::OuterJoin)
|
45
|
+
.on(
|
46
|
+
match_all_on_conditions
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
return arel_join.join_sources
|
51
|
+
end
|
52
|
+
|
53
|
+
def match_all_on_conditions
|
54
|
+
on_condition = tagging_arel_table[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
|
55
|
+
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
56
|
+
|
57
|
+
if options[:start_at].present?
|
58
|
+
on_condition = on_condition.and(tagging_arel_table[:created_at].gteq(options[:start_at]))
|
59
|
+
end
|
60
|
+
|
61
|
+
if options[:end_at].present?
|
62
|
+
on_condition = on_condition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
|
63
|
+
end
|
64
|
+
|
65
|
+
if options[:on].present?
|
66
|
+
on_condition = on_condition.and(tagging_arel_table[:context].eq(options[:on]))
|
67
|
+
end
|
68
|
+
|
69
|
+
on_condition
|
70
|
+
end
|
71
|
+
|
72
|
+
def tags_that_matches_count
|
73
|
+
return [] unless options[:match_all].present?
|
74
|
+
|
75
|
+
taggable_model.find_by_sql(tag_arel_table.project(Arel.star.count).where(tags_match_type).to_sql)
|
76
|
+
|
77
|
+
tagging_arel_table[:taggable_id].count.eq(
|
78
|
+
tag_arel_table.project(Arel.star.count).where(tags_match_type)
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ActsAsTaggableOn::Taggable::TaggedWithQuery
|
2
|
+
class QueryBase
|
3
|
+
def initialize(taggable_model, tag_model, tagging_model, tag_list, options)
|
4
|
+
@taggable_model = taggable_model
|
5
|
+
@tag_model = tag_model
|
6
|
+
@tagging_model = tagging_model
|
7
|
+
@tag_list = tag_list
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
attr_reader :taggable_model, :tag_model, :tagging_model, :tag_list, :options
|
14
|
+
|
15
|
+
def taggable_arel_table
|
16
|
+
@taggable_arel_table ||= taggable_model.arel_table
|
17
|
+
end
|
18
|
+
|
19
|
+
def tag_arel_table
|
20
|
+
@tag_arel_table ||= tag_model.arel_table
|
21
|
+
end
|
22
|
+
|
23
|
+
def tagging_arel_table
|
24
|
+
@tagging_arel_table ||=tagging_model.arel_table
|
25
|
+
end
|
26
|
+
|
27
|
+
def tag_match_type(tag)
|
28
|
+
matches_attribute = tag_arel_table[:name]
|
29
|
+
matches_attribute = matches_attribute.lower unless ActsAsTaggableOn.strict_case_match
|
30
|
+
|
31
|
+
if options[:wild].present?
|
32
|
+
matches_attribute.matches("%#{escaped_tag(tag)}%", "!", ActsAsTaggableOn.strict_case_match)
|
33
|
+
else
|
34
|
+
matches_attribute.matches(escaped_tag(tag), "!", ActsAsTaggableOn.strict_case_match)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def tags_match_type
|
39
|
+
matches_attribute = tag_arel_table[:name]
|
40
|
+
matches_attribute = matches_attribute.lower unless ActsAsTaggableOn.strict_case_match
|
41
|
+
|
42
|
+
if options[:wild].present?
|
43
|
+
matches_attribute.matches_any(tag_list.map{|tag| "%#{escaped_tag(tag)}%"}, "!", ActsAsTaggableOn.strict_case_match)
|
44
|
+
else
|
45
|
+
matches_attribute.matches_any(tag_list.map{|tag| "#{escaped_tag(tag)}"}, "!", ActsAsTaggableOn.strict_case_match)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def escaped_tag(tag)
|
50
|
+
tag = tag.downcase unless ActsAsTaggableOn.strict_case_match
|
51
|
+
ActsAsTaggableOn::Utils.escape_like(tag)
|
52
|
+
end
|
53
|
+
|
54
|
+
def adjust_taggings_alias(taggings_alias)
|
55
|
+
if taggings_alias.size > 75
|
56
|
+
taggings_alias = 'taggings_alias_' + Digest::SHA1.hexdigest(taggings_alias)
|
57
|
+
end
|
58
|
+
taggings_alias
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -18,14 +18,14 @@ module ActsAsTaggableOn
|
|
18
18
|
owned_taggings_scope = opts.delete(:scope)
|
19
19
|
|
20
20
|
has_many :owned_taggings, owned_taggings_scope,
|
21
|
-
opts.merge(
|
21
|
+
**opts.merge(
|
22
22
|
as: :tagger,
|
23
|
-
class_name: ::ActsAsTaggableOn::Tagging,
|
23
|
+
class_name: '::ActsAsTaggableOn::Tagging',
|
24
24
|
dependent: :destroy
|
25
25
|
)
|
26
26
|
|
27
27
|
has_many :owned_tags, -> { distinct },
|
28
|
-
class_name: ::ActsAsTaggableOn::Tag,
|
28
|
+
class_name: '::ActsAsTaggableOn::Tag',
|
29
29
|
source: :tag,
|
30
30
|
through: :owned_taggings
|
31
31
|
end
|
@@ -1,15 +1,20 @@
|
|
1
1
|
module ActsAsTaggableOn
|
2
2
|
class Tagging < ::ActiveRecord::Base #:nodoc:
|
3
|
+
self.table_name = ActsAsTaggableOn.taggings_table
|
4
|
+
|
5
|
+
DEFAULT_CONTEXT = 'tags'
|
3
6
|
belongs_to :tag, class_name: '::ActsAsTaggableOn::Tag', counter_cache: ActsAsTaggableOn.tags_counter
|
4
7
|
belongs_to :taggable, polymorphic: true
|
5
8
|
|
6
|
-
belongs_to :tagger,
|
9
|
+
belongs_to :tagger, polymorphic: true, optional: true
|
7
10
|
|
8
11
|
scope :owned_by, ->(owner) { where(tagger: owner) }
|
9
12
|
scope :not_owned, -> { where(tagger_id: nil, tagger_type: nil) }
|
10
13
|
|
11
|
-
scope :by_contexts, ->(contexts) { where(context: (contexts ||
|
12
|
-
scope :by_context, ->(context =
|
14
|
+
scope :by_contexts, ->(contexts) { where(context: (contexts || DEFAULT_CONTEXT)) }
|
15
|
+
scope :by_context, ->(context = DEFAULT_CONTEXT) { by_contexts(context.to_s) }
|
16
|
+
|
17
|
+
scope :by_tenant, ->(tenant) { where(tenant: tenant) }
|
13
18
|
|
14
19
|
validates_presence_of :context
|
15
20
|
validates_presence_of :tag_id
|
@@ -25,7 +30,7 @@ module ActsAsTaggableOn
|
|
25
30
|
if ActsAsTaggableOn.tags_counter
|
26
31
|
tag.destroy if tag.reload.taggings_count.zero?
|
27
32
|
else
|
28
|
-
tag.destroy if tag.reload.taggings.
|
33
|
+
tag.destroy if tag.reload.taggings.none?
|
29
34
|
end
|
30
35
|
end
|
31
36
|
end
|