acts-as-taggable-on 8.1.0 → 9.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/spec.yml +15 -34
- data/Appraisals +13 -13
- data/CHANGELOG.md +13 -3
- data/README.md +6 -6
- data/acts-as-taggable-on.gemspec +2 -2
- data/db/migrate/1_acts_as_taggable_on_migration.rb +5 -7
- data/db/migrate/2_add_missing_unique_indices.rb +6 -8
- data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +3 -6
- data/db/migrate/4_add_missing_taggable_index.rb +5 -7
- data/db/migrate/5_change_collation_for_tag_names.rb +4 -6
- data/db/migrate/6_add_missing_indexes_on_taggings.rb +15 -13
- data/db/migrate/7_add_tenant_to_taggings.rb +7 -10
- data/docker-compose.yml +15 -0
- data/gemfiles/activerecord_6.0.gemfile +5 -8
- data/gemfiles/activerecord_6.1.gemfile +3 -8
- data/gemfiles/{activerecord_5.0.gemfile → activerecord_7.0.gemfile} +6 -9
- data/lib/acts_as_taggable_on/default_parser.rb +8 -10
- data/lib/acts_as_taggable_on/engine.rb +2 -0
- data/lib/acts_as_taggable_on/generic_parser.rb +2 -0
- data/lib/acts_as_taggable_on/tag.rb +30 -30
- data/lib/acts_as_taggable_on/tag_list.rb +8 -11
- data/lib/acts_as_taggable_on/taggable/cache.rb +64 -62
- data/lib/acts_as_taggable_on/taggable/collection.rb +178 -142
- data/lib/acts_as_taggable_on/taggable/core.rb +248 -244
- data/lib/acts_as_taggable_on/taggable/ownership.rb +110 -98
- data/lib/acts_as_taggable_on/taggable/related.rb +60 -47
- data/lib/acts_as_taggable_on/taggable/tag_list_type.rb +6 -2
- data/lib/acts_as_taggable_on/taggable/tagged_with_query/all_tags_query.rb +110 -106
- data/lib/acts_as_taggable_on/taggable/tagged_with_query/any_tags_query.rb +57 -53
- data/lib/acts_as_taggable_on/taggable/tagged_with_query/exclude_tags_query.rb +63 -60
- data/lib/acts_as_taggable_on/taggable/tagged_with_query/query_base.rb +54 -46
- data/lib/acts_as_taggable_on/taggable/tagged_with_query.rb +14 -8
- data/lib/acts_as_taggable_on/taggable.rb +14 -14
- data/lib/acts_as_taggable_on/tagger.rb +9 -5
- data/lib/acts_as_taggable_on/tagging.rb +6 -4
- data/lib/acts_as_taggable_on/tags_helper.rb +3 -1
- data/lib/acts_as_taggable_on/utils.rb +4 -2
- data/lib/acts_as_taggable_on/version.rb +3 -1
- data/spec/support/database.rb +36 -26
- metadata +13 -14
- data/gemfiles/activerecord_5.1.gemfile +0 -21
- data/gemfiles/activerecord_5.2.gemfile +0 -21
@@ -1,70 +1,74 @@
|
|
1
|
-
|
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
|
1
|
+
# frozen_string_literal: true
|
9
2
|
|
10
|
-
|
3
|
+
module ActsAsTaggableOn
|
4
|
+
module Taggable
|
5
|
+
module TaggedWithQuery
|
6
|
+
class AnyTagsQuery < QueryBase
|
7
|
+
def build
|
8
|
+
taggable_model.select(all_fields)
|
9
|
+
.where(model_has_at_least_one_tag)
|
10
|
+
.order(Arel.sql(order_conditions))
|
11
|
+
.readonly(false)
|
12
|
+
end
|
11
13
|
|
12
|
-
|
13
|
-
taggable_arel_table[Arel.star]
|
14
|
-
end
|
14
|
+
private
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
def all_fields
|
17
|
+
taggable_arel_table[Arel.star]
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
.and(
|
24
|
-
tagging_arel_table[:tag_id].in(
|
25
|
-
tag_arel_table.project(tag_arel_table[:id]).where(tags_match_type)
|
26
|
-
)
|
27
|
-
)
|
20
|
+
def model_has_at_least_one_tag
|
21
|
+
tagging_arel_table.project(Arel.star).where(at_least_one_tag).exists
|
22
|
+
end
|
28
23
|
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
def at_least_one_tag
|
25
|
+
exists_contition = tagging_arel_table[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
|
26
|
+
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
27
|
+
.and(
|
28
|
+
tagging_arel_table[:tag_id].in(
|
29
|
+
tag_arel_table.project(tag_arel_table[:id]).where(tags_match_type)
|
30
|
+
)
|
31
|
+
)
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
if options[:start_at].present?
|
34
|
+
exists_contition = exists_contition.and(tagging_arel_table[:created_at].gteq(options[:start_at]))
|
35
|
+
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
if options[:end_at].present?
|
38
|
+
exists_contition = exists_contition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
|
39
|
+
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
41
|
+
if options[:on].present?
|
42
|
+
exists_contition = exists_contition.and(tagging_arel_table[:context].eq(options[:on]))
|
43
|
+
end
|
45
44
|
|
46
|
-
|
47
|
-
|
45
|
+
if (owner = options[:owned_by]).present?
|
46
|
+
exists_contition = exists_contition.and(tagging_arel_table[:tagger_id].eq(owner.id))
|
47
|
+
.and(tagging_arel_table[:tagger_type].eq(owner.class.base_class.to_s))
|
48
|
+
end
|
48
49
|
|
49
|
-
|
50
|
-
|
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
|
50
|
+
exists_contition
|
51
|
+
end
|
54
52
|
|
55
|
-
|
56
|
-
|
57
|
-
|
53
|
+
def order_conditions
|
54
|
+
order_by = []
|
55
|
+
if options[:order_by_matching_tag_count].present?
|
56
|
+
order_by << "(SELECT count(*) FROM #{tagging_model.table_name} WHERE #{at_least_one_tag.to_sql}) desc"
|
57
|
+
end
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
order_by << options[:order] if options[:order].present?
|
60
|
+
order_by.join(', ')
|
61
|
+
end
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
def alias_name(tag_list)
|
64
|
+
alias_base_name = taggable_model.base_class.name.downcase
|
65
|
+
taggings_context = options[:on] ? "_#{options[:on]}" : ''
|
66
66
|
|
67
|
-
|
67
|
+
adjust_taggings_alias(
|
68
|
+
"#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tag_list.join('_'))}"
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
68
72
|
end
|
69
73
|
end
|
70
74
|
end
|
@@ -1,82 +1,85 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOn
|
4
|
+
module Taggable
|
5
|
+
module TaggedWithQuery
|
6
|
+
class ExcludeTagsQuery < QueryBase
|
7
|
+
def build
|
8
|
+
taggable_model.joins(owning_to_tagger)
|
9
|
+
.where(tags_not_in_list)
|
10
|
+
.having(tags_that_matches_count)
|
11
|
+
.readonly(false)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def tags_not_in_list
|
17
|
+
taggable_arel_table[:id].not_in(
|
14
18
|
tagging_arel_table
|
15
19
|
.project(tagging_arel_table[:taggable_id])
|
16
20
|
.join(tag_arel_table)
|
17
21
|
.on(
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
tagging_arel_table[:tag_id].eq(tag_arel_table[:id])
|
23
|
+
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
24
|
+
.and(tags_match_type)
|
25
|
+
)
|
22
26
|
)
|
23
27
|
|
24
|
-
|
25
|
-
|
28
|
+
# FIXME: missing time scope, this is also missing in the original implementation
|
29
|
+
end
|
26
30
|
|
31
|
+
def owning_to_tagger
|
32
|
+
return [] if options[:owned_by].blank?
|
27
33
|
|
28
|
-
|
29
|
-
return [] unless options[:owned_by].present?
|
34
|
+
owner = options[:owned_by]
|
30
35
|
|
31
|
-
|
36
|
+
arel_join = taggable_arel_table
|
37
|
+
.join(tagging_arel_table)
|
38
|
+
.on(
|
39
|
+
tagging_arel_table[:tagger_id].eq(owner.id)
|
40
|
+
.and(tagging_arel_table[:tagger_type].eq(owner.class.base_class.to_s))
|
41
|
+
.and(tagging_arel_table[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key]))
|
42
|
+
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
43
|
+
)
|
32
44
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
)
|
45
|
+
if options[:match_all].present?
|
46
|
+
arel_join = arel_join
|
47
|
+
.join(tagging_arel_table, Arel::Nodes::OuterJoin)
|
48
|
+
.on(
|
49
|
+
match_all_on_conditions
|
50
|
+
)
|
51
|
+
end
|
41
52
|
|
42
|
-
|
43
|
-
|
44
|
-
.join(tagging_arel_table, Arel::Nodes::OuterJoin)
|
45
|
-
.on(
|
46
|
-
match_all_on_conditions
|
47
|
-
)
|
48
|
-
end
|
53
|
+
arel_join.join_sources
|
54
|
+
end
|
49
55
|
|
50
|
-
|
51
|
-
|
56
|
+
def match_all_on_conditions
|
57
|
+
on_condition = tagging_arel_table[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
|
58
|
+
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
52
59
|
|
53
|
-
|
54
|
-
|
55
|
-
|
60
|
+
if options[:start_at].present?
|
61
|
+
on_condition = on_condition.and(tagging_arel_table[:created_at].gteq(options[:start_at]))
|
62
|
+
end
|
56
63
|
|
57
|
-
|
58
|
-
|
59
|
-
|
64
|
+
if options[:end_at].present?
|
65
|
+
on_condition = on_condition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
|
66
|
+
end
|
60
67
|
|
61
|
-
|
62
|
-
on_condition = on_condition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
|
63
|
-
end
|
68
|
+
on_condition = on_condition.and(tagging_arel_table[:context].eq(options[:on])) if options[:on].present?
|
64
69
|
|
65
|
-
|
66
|
-
|
67
|
-
end
|
70
|
+
on_condition
|
71
|
+
end
|
68
72
|
|
69
|
-
|
70
|
-
|
73
|
+
def tags_that_matches_count
|
74
|
+
return [] if options[:match_all].blank?
|
71
75
|
|
72
|
-
|
73
|
-
return [] unless options[:match_all].present?
|
76
|
+
taggable_model.find_by_sql(tag_arel_table.project(Arel.star.count).where(tags_match_type).to_sql)
|
74
77
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
78
|
+
tagging_arel_table[:taggable_id].count.eq(
|
79
|
+
tag_arel_table.project(Arel.star.count).where(tags_match_type)
|
80
|
+
)
|
81
|
+
end
|
82
|
+
end
|
80
83
|
end
|
81
84
|
end
|
82
85
|
end
|
@@ -1,61 +1,69 @@
|
|
1
|
-
|
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
|
1
|
+
# frozen_string_literal: true
|
10
2
|
|
11
|
-
|
3
|
+
module ActsAsTaggableOn
|
4
|
+
module Taggable
|
5
|
+
module TaggedWithQuery
|
6
|
+
class QueryBase
|
7
|
+
def initialize(taggable_model, tag_model, tagging_model, tag_list, options)
|
8
|
+
@taggable_model = taggable_model
|
9
|
+
@tag_model = tag_model
|
10
|
+
@tagging_model = tagging_model
|
11
|
+
@tag_list = tag_list
|
12
|
+
@options = options
|
13
|
+
end
|
12
14
|
|
13
|
-
|
15
|
+
private
|
14
16
|
|
15
|
-
|
16
|
-
@taggable_arel_table ||= taggable_model.arel_table
|
17
|
-
end
|
17
|
+
attr_reader :taggable_model, :tag_model, :tagging_model, :tag_list, :options
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
def taggable_arel_table
|
20
|
+
@taggable_arel_table ||= taggable_model.arel_table
|
21
|
+
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
def tag_arel_table
|
24
|
+
@tag_arel_table ||= tag_model.arel_table
|
25
|
+
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
def tagging_arel_table
|
28
|
+
@tagging_arel_table ||= tagging_model.arel_table
|
29
|
+
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
matches_attribute.matches(escaped_tag(tag), "!", ActsAsTaggableOn.strict_case_match)
|
35
|
-
end
|
36
|
-
end
|
31
|
+
def tag_match_type(tag)
|
32
|
+
matches_attribute = tag_arel_table[:name]
|
33
|
+
matches_attribute = matches_attribute.lower unless ActsAsTaggableOn.strict_case_match
|
37
34
|
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
if options[:wild].present?
|
36
|
+
matches_attribute.matches("%#{escaped_tag(tag)}%", '!', ActsAsTaggableOn.strict_case_match)
|
37
|
+
else
|
38
|
+
matches_attribute.matches(escaped_tag(tag), '!', ActsAsTaggableOn.strict_case_match)
|
39
|
+
end
|
40
|
+
end
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
matches_attribute.matches_any(tag_list.map{|tag| "#{escaped_tag(tag)}"}, "!", ActsAsTaggableOn.strict_case_match)
|
46
|
-
end
|
47
|
-
end
|
42
|
+
def tags_match_type
|
43
|
+
matches_attribute = tag_arel_table[:name]
|
44
|
+
matches_attribute = matches_attribute.lower unless ActsAsTaggableOn.strict_case_match
|
48
45
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
46
|
+
if options[:wild].present?
|
47
|
+
matches_attribute.matches_any(tag_list.map do |tag|
|
48
|
+
"%#{escaped_tag(tag)}%"
|
49
|
+
end, '!', ActsAsTaggableOn.strict_case_match)
|
50
|
+
else
|
51
|
+
matches_attribute.matches_any(tag_list.map do |tag|
|
52
|
+
escaped_tag(tag).to_s
|
53
|
+
end, '!', ActsAsTaggableOn.strict_case_match)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def escaped_tag(tag)
|
58
|
+
tag = tag.downcase unless ActsAsTaggableOn.strict_case_match
|
59
|
+
ActsAsTaggableOn::Utils.escape_like(tag)
|
60
|
+
end
|
53
61
|
|
54
|
-
|
55
|
-
|
56
|
-
|
62
|
+
def adjust_taggings_alias(taggings_alias)
|
63
|
+
taggings_alias = "taggings_alias_#{Digest::SHA1.hexdigest(taggings_alias)}" if taggings_alias.size > 75
|
64
|
+
taggings_alias
|
65
|
+
end
|
57
66
|
end
|
58
|
-
taggings_alias
|
59
67
|
end
|
60
68
|
end
|
61
69
|
end
|
@@ -1,16 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'tagged_with_query/query_base'
|
2
4
|
require_relative 'tagged_with_query/exclude_tags_query'
|
3
5
|
require_relative 'tagged_with_query/any_tags_query'
|
4
6
|
require_relative 'tagged_with_query/all_tags_query'
|
5
7
|
|
6
|
-
module ActsAsTaggableOn
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
module ActsAsTaggableOn
|
9
|
+
module Taggable
|
10
|
+
module TaggedWithQuery
|
11
|
+
def self.build(taggable_model, tag_model, tagging_model, tag_list, options)
|
12
|
+
if options[:exclude].present?
|
13
|
+
ExcludeTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
|
14
|
+
elsif options[:any].present?
|
15
|
+
AnyTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
|
16
|
+
else
|
17
|
+
AllTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
|
18
|
+
end
|
19
|
+
end
|
14
20
|
end
|
15
21
|
end
|
16
22
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActsAsTaggableOn
|
2
4
|
module Taggable
|
3
|
-
|
4
5
|
def taggable?
|
5
6
|
false
|
6
7
|
end
|
@@ -56,11 +57,10 @@ module ActsAsTaggableOn
|
|
56
57
|
|
57
58
|
def acts_as_taggable_tenant(tenant)
|
58
59
|
if taggable?
|
59
|
-
self.tenant_column = tenant
|
60
60
|
else
|
61
61
|
class_attribute :tenant_column
|
62
|
-
self.tenant_column = tenant
|
63
62
|
end
|
63
|
+
self.tenant_column = tenant
|
64
64
|
|
65
65
|
# each of these add context-specific methods and must be
|
66
66
|
# called on each call of taggable_on
|
@@ -73,17 +73,17 @@ module ActsAsTaggableOn
|
|
73
73
|
|
74
74
|
private
|
75
75
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
76
|
+
# Make a model taggable on specified contexts
|
77
|
+
# and optionally preserves the order in which tags are created
|
78
|
+
#
|
79
|
+
# Separate methods used above for backwards compatibility
|
80
|
+
# so that the original acts_as_taggable_on method is unaffected
|
81
|
+
# as it's not possible to add another argument to the method
|
82
|
+
# without the tag_types being enclosed in square brackets
|
83
|
+
#
|
84
|
+
# NB: method overridden in core module in order to create tag type
|
85
|
+
# associations and methods after this logic has executed
|
86
|
+
#
|
87
87
|
def taggable_on(preserve_tag_order, *tag_types)
|
88
88
|
tag_types = tag_types.to_a.flatten.compact.map(&:to_sym)
|
89
89
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActsAsTaggableOn
|
2
4
|
module Tagger
|
3
5
|
def self.included(base)
|
@@ -13,7 +15,7 @@ module ActsAsTaggableOn
|
|
13
15
|
# class User < ActiveRecord::Base
|
14
16
|
# acts_as_tagger
|
15
17
|
# end
|
16
|
-
def acts_as_tagger(opts={})
|
18
|
+
def acts_as_tagger(opts = {})
|
17
19
|
class_eval do
|
18
20
|
owned_taggings_scope = opts.delete(:scope)
|
19
21
|
|
@@ -54,14 +56,16 @@ module ActsAsTaggableOn
|
|
54
56
|
#
|
55
57
|
# Example:
|
56
58
|
# @user.tag(@photo, :with => "paris, normandy", :on => :locations)
|
57
|
-
def tag(taggable, opts={})
|
59
|
+
def tag(taggable, opts = {})
|
58
60
|
opts.reverse_merge!(force: true)
|
59
61
|
skip_save = opts.delete(:skip_save)
|
60
62
|
return false unless taggable.respond_to?(:is_taggable?) && taggable.is_taggable?
|
61
63
|
|
62
|
-
|
63
|
-
|
64
|
-
|
64
|
+
raise 'You need to specify a tag context using :on' unless opts.key?(:on)
|
65
|
+
raise 'You need to specify some tags using :with' unless opts.key?(:with)
|
66
|
+
unless opts[:force] || taggable.tag_types.include?(opts[:on])
|
67
|
+
raise "No context :#{opts[:on]} defined in #{taggable.class}"
|
68
|
+
end
|
65
69
|
|
66
70
|
taggable.set_owner_tag_list_on(self, opts[:on].to_s, opts[:with])
|
67
71
|
taggable.save unless skip_save
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActsAsTaggableOn
|
2
|
-
class Tagging < ::ActiveRecord::Base
|
4
|
+
class Tagging < ::ActiveRecord::Base # :nodoc:
|
3
5
|
self.table_name = ActsAsTaggableOn.taggings_table
|
4
6
|
|
5
7
|
DEFAULT_CONTEXT = 'tags'
|
@@ -19,7 +21,7 @@ module ActsAsTaggableOn
|
|
19
21
|
validates_presence_of :context
|
20
22
|
validates_presence_of :tag_id
|
21
23
|
|
22
|
-
validates_uniqueness_of :tag_id, scope: [
|
24
|
+
validates_uniqueness_of :tag_id, scope: %i[taggable_type taggable_id context tagger_id tagger_type]
|
23
25
|
|
24
26
|
after_destroy :remove_unused_tags
|
25
27
|
|
@@ -29,8 +31,8 @@ module ActsAsTaggableOn
|
|
29
31
|
if ActsAsTaggableOn.remove_unused_tags
|
30
32
|
if ActsAsTaggableOn.tags_counter
|
31
33
|
tag.destroy if tag.reload.taggings_count.zero?
|
32
|
-
|
33
|
-
tag.destroy
|
34
|
+
elsif tag.reload.taggings.none?
|
35
|
+
tag.destroy
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActsAsTaggableOn
|
2
4
|
module TagsHelper
|
3
5
|
# See the wiki for an example using tag_cloud.
|
4
6
|
def tag_cloud(tags, classes)
|
5
7
|
return [] if tags.empty?
|
6
8
|
|
7
|
-
max_count = tags.
|
9
|
+
max_count = tags.max_by(&:taggings_count).taggings_count.to_f
|
8
10
|
|
9
11
|
tags.each do |tag|
|
10
12
|
index = ((tag.taggings_count / max_count) * (classes.size - 1))
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This module is deprecated and will be removed in the incoming versions
|
2
4
|
|
3
5
|
module ActsAsTaggableOn
|
@@ -9,7 +11,7 @@ module ActsAsTaggableOn
|
|
9
11
|
end
|
10
12
|
|
11
13
|
def using_postgresql?
|
12
|
-
connection && connection.adapter_name
|
14
|
+
connection && %w[PostgreSQL PostGIS].include?(connection.adapter_name)
|
13
15
|
end
|
14
16
|
|
15
17
|
def using_mysql?
|
@@ -30,7 +32,7 @@ module ActsAsTaggableOn
|
|
30
32
|
|
31
33
|
# escape _ and % characters in strings, since these are wildcards in SQL.
|
32
34
|
def escape_like(str)
|
33
|
-
str.gsub(/[!%_]/) { |x|
|
35
|
+
str.gsub(/[!%_]/) { |x| "!#{x}" }
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
data/spec/support/database.rb
CHANGED
@@ -1,36 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# set adapter to use, default is sqlite3
|
2
4
|
# to use an alternative adapter run => rake spec DB='postgresql'
|
3
5
|
db_name = ENV['DB'] || 'sqlite3'
|
4
|
-
database_yml = File.expand_path('
|
6
|
+
database_yml = File.expand_path('../internal/config/database.yml', __dir__)
|
5
7
|
|
6
|
-
|
8
|
+
unless File.exist?(database_yml)
|
9
|
+
raise "Please create #{database_yml} first to configure your database. Take a look at: #{database_yml}.sample"
|
10
|
+
end
|
7
11
|
|
8
|
-
|
12
|
+
ActiveRecord::Base.configurations = YAML.load_file(database_yml)
|
13
|
+
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), '../debug.log'))
|
14
|
+
ActiveRecord::Base.logger.level = ENV['CI'] ? ::Logger::ERROR : ::Logger::DEBUG
|
15
|
+
ActiveRecord::Migration.verbose = false
|
16
|
+
if ActiveRecord.version >= Gem::Version.new('7.0.0.alpha2')
|
17
|
+
ActiveRecord.default_timezone = :utc
|
18
|
+
else
|
9
19
|
ActiveRecord::Base.default_timezone = :utc
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
ActiveRecord::Base.establish_connection(db_name.to_sym)
|
17
|
-
ActiveRecord::Base.connection
|
18
|
-
rescue
|
19
|
-
case db_name
|
20
|
-
when /mysql/
|
21
|
-
ActiveRecord::Base.establish_connection(config.merge('database' => nil))
|
22
|
-
ActiveRecord::Base.connection.create_database(config['database'], {charset: 'utf8', collation: 'utf8_unicode_ci'})
|
23
|
-
when 'postgresql'
|
24
|
-
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
|
25
|
-
ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => 'utf8'))
|
26
|
-
end
|
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
|
27
26
|
|
28
|
-
|
27
|
+
begin
|
28
|
+
ActiveRecord::Base.establish_connection(db_name.to_sym)
|
29
|
+
ActiveRecord::Base.connection
|
30
|
+
rescue StandardError
|
31
|
+
case db_name
|
32
|
+
when /mysql/
|
33
|
+
ActiveRecord::Base.establish_connection(config.merge('database' => nil))
|
34
|
+
ActiveRecord::Base.connection.create_database(config['database'],
|
35
|
+
{ charset: 'utf8', collation: 'utf8_unicode_ci' })
|
36
|
+
when 'postgresql'
|
37
|
+
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres',
|
38
|
+
'schema_search_path' => 'public'))
|
39
|
+
ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => 'utf8'))
|
29
40
|
end
|
30
41
|
|
31
|
-
|
32
|
-
Dir[File.dirname(__dir__) + '/internal/app/models/*.rb'].each { |f| require f }
|
33
|
-
|
34
|
-
else
|
35
|
-
fail "Please create #{database_yml} first to configure your database. Take a look at: #{database_yml}.sample"
|
42
|
+
ActiveRecord::Base.establish_connection(config)
|
36
43
|
end
|
44
|
+
|
45
|
+
require "#{File.dirname(__FILE__)}/../internal/db/schema.rb"
|
46
|
+
Dir["#{File.dirname(__dir__)}/internal/app/models/*.rb"].each { |f| require f }
|