nano-max-tool 0.0.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 +7 -0
- data/acts-as-taggable-on-13.0.0/LICENSE.md +20 -0
- data/acts-as-taggable-on-13.0.0/db/migrate/1_acts_as_taggable_on_migration.rb +33 -0
- data/acts-as-taggable-on-13.0.0/db/migrate/2_add_missing_unique_indices.rb +23 -0
- data/acts-as-taggable-on-13.0.0/db/migrate/3_add_taggings_counter_cache_to_tags.rb +16 -0
- data/acts-as-taggable-on-13.0.0/db/migrate/4_add_missing_taggable_index.rb +12 -0
- data/acts-as-taggable-on-13.0.0/db/migrate/5_change_collation_for_tag_names.rb +12 -0
- data/acts-as-taggable-on-13.0.0/db/migrate/6_add_missing_indexes_on_taggings.rb +24 -0
- data/acts-as-taggable-on-13.0.0/db/migrate/7_add_tenant_to_taggings.rb +13 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/default_parser.rb +77 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/engine.rb +6 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/generic_parser.rb +21 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/tag.rb +138 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/tag_list.rb +103 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/caching.rb +46 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/collection.rb +220 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/core.rb +333 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/ownership.rb +146 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/related.rb +84 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tag_list_type.rb +8 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query/all_tags_query.rb +115 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query/any_tags_query.rb +74 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query/exclude_tags_query.rb +85 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query/query_base.rb +78 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query.rb +17 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable.rb +119 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/tagger.rb +85 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/tagging.rb +40 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/tags_helper.rb +17 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/utils.rb +35 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/version.rb +5 -0
- data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on.rb +118 -0
- data/acts-as-taggable-on-13.0.0/lib/tasks/example/acts-as-taggable-on.rb.example +8 -0
- data/acts-as-taggable-on-13.0.0/lib/tasks/install_initializer.rake +23 -0
- data/acts-as-taggable-on-13.0.0/lib/tasks/tags_collate_utf8.rake +21 -0
- data/nano-max-tool.gemspec +12 -0
- metadata +76 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActsAsTaggableOn
|
|
4
|
+
module Taggable
|
|
5
|
+
module Related
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.extend ActsAsTaggableOn::Taggable::Related::ClassMethods
|
|
8
|
+
base.initialize_acts_as_taggable_on_related
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def initialize_acts_as_taggable_on_related
|
|
13
|
+
tag_types.map(&:to_s).each do |tag_type|
|
|
14
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
15
|
+
def find_related_#{tag_type}(options = {})
|
|
16
|
+
related_tags_for('#{tag_type}', self.class, options)
|
|
17
|
+
end
|
|
18
|
+
alias_method :find_related_on_#{tag_type}, :find_related_#{tag_type}
|
|
19
|
+
|
|
20
|
+
def find_related_#{tag_type}_for(klass, options = {})
|
|
21
|
+
related_tags_for('#{tag_type}', klass, options)
|
|
22
|
+
end
|
|
23
|
+
RUBY
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def acts_as_taggable_on(*args)
|
|
28
|
+
super(*args)
|
|
29
|
+
initialize_acts_as_taggable_on_related
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def find_matching_contexts(search_context, result_context, options = {})
|
|
34
|
+
matching_contexts_for(search_context.to_s, result_context.to_s, self.class, options)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def find_matching_contexts_for(klass, search_context, result_context, options = {})
|
|
38
|
+
matching_contexts_for(search_context.to_s, result_context.to_s, klass, options)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def matching_contexts_for(search_context, result_context, klass, _options = {})
|
|
42
|
+
tags_to_find = tags_on(search_context).map(&:name)
|
|
43
|
+
related_where(klass,
|
|
44
|
+
[
|
|
45
|
+
"#{exclude_self(klass,
|
|
46
|
+
id)} #{klass.table_name}.#{klass.primary_key} = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = '#{klass.base_class}' AND #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key} AND #{ActsAsTaggableOn::Tag.table_name}.name IN (?) AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_to_find, result_context
|
|
47
|
+
])
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def related_tags_for(context, klass, options = {})
|
|
51
|
+
tags_to_ignore = Array.wrap(options[:ignore]).map(&:to_s) || []
|
|
52
|
+
tags_to_find = tags_on(context).map(&:name).reject { |t| tags_to_ignore.include? t }
|
|
53
|
+
related_where(klass,
|
|
54
|
+
[
|
|
55
|
+
"#{exclude_self(klass,
|
|
56
|
+
id)} #{klass.table_name}.#{klass.primary_key} = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = '#{klass.base_class}' AND #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key} AND #{ActsAsTaggableOn::Tag.table_name}.name IN (?) AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_to_find, context
|
|
57
|
+
])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def exclude_self(klass, id)
|
|
63
|
+
"#{klass.arel_table[klass.primary_key].not_eq(id).to_sql} AND" if [self.class.base_class,
|
|
64
|
+
self.class].include? klass
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def group_columns(klass)
|
|
68
|
+
if ActsAsTaggableOn::Utils.using_postgresql?
|
|
69
|
+
grouped_column_names_for(klass)
|
|
70
|
+
else
|
|
71
|
+
"#{klass.table_name}.#{klass.primary_key}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def related_where(klass, conditions)
|
|
76
|
+
klass.select("#{klass.table_name}.*, COUNT(#{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key}) AS count")
|
|
77
|
+
.from("#{klass.table_name}, #{ActsAsTaggableOn::Tag.table_name}, #{ActsAsTaggableOn::Tagging.table_name}")
|
|
78
|
+
.group(group_columns(klass))
|
|
79
|
+
.order('count DESC')
|
|
80
|
+
.where(conditions)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query/all_tags_query.rb
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActsAsTaggableOn
|
|
4
|
+
module Taggable
|
|
5
|
+
module TaggedWithQuery
|
|
6
|
+
class AllTagsQuery < QueryBase
|
|
7
|
+
def build
|
|
8
|
+
taggable_model.joins(each_tag_in_list)
|
|
9
|
+
.group(by_taggable)
|
|
10
|
+
.having(tags_that_matches_count)
|
|
11
|
+
.order(order_conditions)
|
|
12
|
+
.readonly(false)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def each_tag_in_list
|
|
18
|
+
arel_join = taggable_arel_table
|
|
19
|
+
|
|
20
|
+
tag_list.each do |tag|
|
|
21
|
+
tagging_alias = tagging_arel_table.alias(tagging_alias(tag))
|
|
22
|
+
arel_join = arel_join
|
|
23
|
+
.join(tagging_alias)
|
|
24
|
+
.on(on_conditions(tag, tagging_alias))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if options[:match_all].present?
|
|
28
|
+
arel_join = arel_join
|
|
29
|
+
.join(tagging_arel_table, Arel::Nodes::OuterJoin)
|
|
30
|
+
.on(
|
|
31
|
+
match_all_on_conditions
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
arel_join.join_sources
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def on_conditions(tag, tagging_alias)
|
|
39
|
+
on_condition = tagging_alias[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
|
|
40
|
+
.and(tagging_alias[:taggable_type].eq(taggable_model.base_class.name))
|
|
41
|
+
.and(
|
|
42
|
+
tagging_alias[:tag_id].in(
|
|
43
|
+
tag_arel_table.project(tag_arel_table[:id]).where(tag_match_type(tag))
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if options[:start_at].present?
|
|
48
|
+
on_condition = on_condition.and(tagging_alias[:created_at].gteq(options[:start_at]))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if options[:end_at].present?
|
|
52
|
+
on_condition = on_condition.and(tagging_alias[:created_at].lteq(options[:end_at]))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
on_condition = on_condition.and(tagging_alias[:context].eq(options[:on])) if options[:on].present?
|
|
56
|
+
|
|
57
|
+
if (owner = options[:owned_by]).present?
|
|
58
|
+
on_condition = on_condition.and(tagging_alias[:tagger_id].eq(owner.id))
|
|
59
|
+
.and(tagging_alias[:tagger_type].eq(owner.class.base_class.to_s))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
on_condition
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def match_all_on_conditions
|
|
66
|
+
on_condition = tagging_arel_table[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
|
|
67
|
+
.and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
|
|
68
|
+
|
|
69
|
+
if options[:start_at].present?
|
|
70
|
+
on_condition = on_condition.and(tagging_arel_table[:created_at].gteq(options[:start_at]))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
if options[:end_at].present?
|
|
74
|
+
on_condition = on_condition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
on_condition = on_condition.and(tagging_arel_table[:context].eq(options[:on])) if options[:on].present?
|
|
78
|
+
|
|
79
|
+
on_condition
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def by_taggable
|
|
83
|
+
return [] if options[:match_all].blank?
|
|
84
|
+
|
|
85
|
+
taggable_arel_table[taggable_model.primary_key]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def tags_that_matches_count
|
|
89
|
+
return [] if options[:match_all].blank?
|
|
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
|
+
if options[:order_by_matching_tag_count].present? && options[:match_all].blank?
|
|
101
|
+
order_by << tagging_arel_table.project(tagging_arel_table[Arel.star].count.as('taggings_count')).order('taggings_count DESC').to_sql
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
order_by << options[:order] if options[:order].present?
|
|
105
|
+
order_by.join(', ')
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def tagging_alias(tag)
|
|
109
|
+
alias_base_name = taggable_model.base_class.name.downcase
|
|
110
|
+
adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tag)}")
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query/any_tags_query.rb
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
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
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def all_fields
|
|
17
|
+
taggable_arel_table[Arel.star]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def model_has_at_least_one_tag
|
|
21
|
+
tagging_arel_table.project(Arel.star).where(at_least_one_tag).exists
|
|
22
|
+
end
|
|
23
|
+
|
|
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
|
+
|
|
33
|
+
if options[:start_at].present?
|
|
34
|
+
exists_contition = exists_contition.and(tagging_arel_table[:created_at].gteq(options[:start_at]))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if options[:end_at].present?
|
|
38
|
+
exists_contition = exists_contition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if options[:on].present?
|
|
42
|
+
exists_contition = exists_contition.and(tagging_arel_table[:context].eq(options[:on]))
|
|
43
|
+
end
|
|
44
|
+
|
|
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
|
|
49
|
+
|
|
50
|
+
exists_contition
|
|
51
|
+
end
|
|
52
|
+
|
|
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
|
+
|
|
59
|
+
order_by << options[:order] if options[:order].present?
|
|
60
|
+
order_by.join(', ')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def alias_name(tag_list)
|
|
64
|
+
alias_base_name = taggable_model.base_class.name.downcase
|
|
65
|
+
taggings_context = options[:on] ? "_#{options[:on]}" : ''
|
|
66
|
+
|
|
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
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
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(
|
|
18
|
+
tagging_arel_table
|
|
19
|
+
.project(tagging_arel_table[:taggable_id])
|
|
20
|
+
.join(tag_arel_table)
|
|
21
|
+
.on(
|
|
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
|
+
)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# FIXME: missing time scope, this is also missing in the original implementation
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def owning_to_tagger
|
|
32
|
+
return [] if options[:owned_by].blank?
|
|
33
|
+
|
|
34
|
+
owner = options[:owned_by]
|
|
35
|
+
|
|
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
|
+
)
|
|
44
|
+
|
|
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
|
|
52
|
+
|
|
53
|
+
arel_join.join_sources
|
|
54
|
+
end
|
|
55
|
+
|
|
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))
|
|
59
|
+
|
|
60
|
+
if options[:start_at].present?
|
|
61
|
+
on_condition = on_condition.and(tagging_arel_table[:created_at].gteq(options[:start_at]))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
if options[:end_at].present?
|
|
65
|
+
on_condition = on_condition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
on_condition = on_condition.and(tagging_arel_table[:context].eq(options[:on])) if options[:on].present?
|
|
69
|
+
|
|
70
|
+
on_condition
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def tags_that_matches_count
|
|
74
|
+
return [] if options[:match_all].blank?
|
|
75
|
+
|
|
76
|
+
taggable_model.find_by_sql(tag_arel_table.project(Arel.star.count).where(tags_match_type).to_sql)
|
|
77
|
+
|
|
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
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query/query_base.rb
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
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
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
attr_reader :taggable_model, :tag_model, :tagging_model, :tag_list, :options
|
|
18
|
+
|
|
19
|
+
def taggable_arel_table
|
|
20
|
+
@taggable_arel_table ||= taggable_model.arel_table
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def tag_arel_table
|
|
24
|
+
@tag_arel_table ||= tag_model.arel_table
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def tagging_arel_table
|
|
28
|
+
@tagging_arel_table ||= tagging_model.arel_table
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def tag_match_type(tag)
|
|
32
|
+
matches_attribute = tag_arel_table[:name]
|
|
33
|
+
matches_attribute = matches_attribute.lower unless ActsAsTaggableOn.strict_case_match
|
|
34
|
+
|
|
35
|
+
if options[:wild].present?
|
|
36
|
+
matches_attribute.matches(wildcard_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
|
+
|
|
42
|
+
def tags_match_type
|
|
43
|
+
matches_attribute = tag_arel_table[:name]
|
|
44
|
+
matches_attribute = matches_attribute.lower unless ActsAsTaggableOn.strict_case_match
|
|
45
|
+
|
|
46
|
+
if options[:wild].present?
|
|
47
|
+
matches_attribute.matches_any(tag_list.map do |tag|
|
|
48
|
+
wildcard_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
|
|
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
|
+
|
|
71
|
+
def adjust_taggings_alias(taggings_alias)
|
|
72
|
+
taggings_alias = "taggings_alias_#{Digest::SHA1.hexdigest(taggings_alias)}" if taggings_alias.size > 75
|
|
73
|
+
taggings_alias
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActsAsTaggableOn
|
|
4
|
+
module Taggable
|
|
5
|
+
module TaggedWithQuery
|
|
6
|
+
def self.build(taggable_model, tag_model, tagging_model, tag_list, options)
|
|
7
|
+
if options[:exclude].present?
|
|
8
|
+
ExcludeTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
|
|
9
|
+
elsif options[:any].present?
|
|
10
|
+
AnyTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
|
|
11
|
+
else
|
|
12
|
+
AllTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActsAsTaggableOn
|
|
4
|
+
module Taggable
|
|
5
|
+
def taggable?
|
|
6
|
+
false
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
# This is an alias for calling <tt>acts_as_taggable_on :tags</tt>.
|
|
11
|
+
#
|
|
12
|
+
# Example:
|
|
13
|
+
# class Book < ActiveRecord::Base
|
|
14
|
+
# acts_as_taggable
|
|
15
|
+
# end
|
|
16
|
+
def acts_as_taggable
|
|
17
|
+
acts_as_taggable_on :tags
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
# This is an alias for calling <tt>acts_as_ordered_taggable_on :tags</tt>.
|
|
22
|
+
#
|
|
23
|
+
# Example:
|
|
24
|
+
# class Book < ActiveRecord::Base
|
|
25
|
+
# acts_as_ordered_taggable
|
|
26
|
+
# end
|
|
27
|
+
def acts_as_ordered_taggable
|
|
28
|
+
acts_as_ordered_taggable_on :tags
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# Make a model taggable on specified contexts.
|
|
33
|
+
#
|
|
34
|
+
# @param [Array] tag_types An array of taggable contexts
|
|
35
|
+
#
|
|
36
|
+
# Example:
|
|
37
|
+
# class User < ActiveRecord::Base
|
|
38
|
+
# acts_as_taggable_on :languages, :skills
|
|
39
|
+
# end
|
|
40
|
+
def acts_as_taggable_on(*tag_types)
|
|
41
|
+
taggable_on(false, tag_types)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
##
|
|
45
|
+
# Make a model taggable on specified contexts
|
|
46
|
+
# and preserves the order in which tags are created
|
|
47
|
+
#
|
|
48
|
+
# @param [Array] tag_types An array of taggable contexts
|
|
49
|
+
#
|
|
50
|
+
# Example:
|
|
51
|
+
# class User < ActiveRecord::Base
|
|
52
|
+
# acts_as_ordered_taggable_on :languages, :skills
|
|
53
|
+
# end
|
|
54
|
+
def acts_as_ordered_taggable_on(*tag_types)
|
|
55
|
+
taggable_on(true, tag_types)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def acts_as_taggable_tenant(tenant)
|
|
59
|
+
if taggable?
|
|
60
|
+
else
|
|
61
|
+
class_attribute :tenant_column
|
|
62
|
+
end
|
|
63
|
+
self.tenant_column = tenant
|
|
64
|
+
|
|
65
|
+
# each of these add context-specific methods and must be
|
|
66
|
+
# called on each call of taggable_on
|
|
67
|
+
include Core
|
|
68
|
+
include Collection
|
|
69
|
+
include Caching
|
|
70
|
+
include Ownership
|
|
71
|
+
include Related
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
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
|
+
def taggable_on(preserve_tag_order, *tag_types)
|
|
88
|
+
tag_types = tag_types.to_a.flatten.compact.map(&:to_sym)
|
|
89
|
+
|
|
90
|
+
if taggable?
|
|
91
|
+
self.tag_types = (self.tag_types + tag_types).uniq
|
|
92
|
+
self.preserve_tag_order = preserve_tag_order
|
|
93
|
+
else
|
|
94
|
+
class_eval do
|
|
95
|
+
class_attribute :tag_types
|
|
96
|
+
class_attribute :preserve_tag_order
|
|
97
|
+
class_attribute :tenant_column
|
|
98
|
+
self.tag_types = tag_types
|
|
99
|
+
self.preserve_tag_order = preserve_tag_order
|
|
100
|
+
|
|
101
|
+
has_many :taggings, as: :taggable, dependent: :destroy, class_name: '::ActsAsTaggableOn::Tagging'
|
|
102
|
+
has_many :base_tags, through: :taggings, source: :tag, class_name: '::ActsAsTaggableOn::Tag'
|
|
103
|
+
|
|
104
|
+
def self.taggable?
|
|
105
|
+
true
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# each of these add context-specific methods and must be
|
|
111
|
+
# called on each call of taggable_on
|
|
112
|
+
include Core
|
|
113
|
+
include Collection
|
|
114
|
+
include Caching
|
|
115
|
+
include Ownership
|
|
116
|
+
include Related
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActsAsTaggableOn
|
|
4
|
+
module Tagger
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
class_methods do
|
|
8
|
+
##
|
|
9
|
+
# Make a model a tagger. This allows an instance of a model to claim ownership
|
|
10
|
+
# of tags.
|
|
11
|
+
#
|
|
12
|
+
# Example:
|
|
13
|
+
# class User < ActiveRecord::Base
|
|
14
|
+
# acts_as_tagger
|
|
15
|
+
# end
|
|
16
|
+
def acts_as_tagger(opts = {})
|
|
17
|
+
class_eval do
|
|
18
|
+
owned_taggings_scope = opts.delete(:scope)
|
|
19
|
+
|
|
20
|
+
has_many :owned_taggings, owned_taggings_scope,
|
|
21
|
+
**opts.merge(
|
|
22
|
+
as: :tagger,
|
|
23
|
+
class_name: '::ActsAsTaggableOn::Tagging',
|
|
24
|
+
dependent: :destroy
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
has_many :owned_tags, -> { distinct },
|
|
28
|
+
class_name: '::ActsAsTaggableOn::Tag',
|
|
29
|
+
source: :tag,
|
|
30
|
+
through: :owned_taggings
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
include ActsAsTaggableOn::Tagger::InstanceMethods
|
|
34
|
+
extend ActsAsTaggableOn::Tagger::SingletonMethods
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def tagger?
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
alias is_tagger? tagger?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
module InstanceMethods
|
|
45
|
+
##
|
|
46
|
+
# Tag a taggable model with tags that are owned by the tagger.
|
|
47
|
+
#
|
|
48
|
+
# @param taggable The object that will be tagged
|
|
49
|
+
# @param [Hash] options An hash with options. Available options are:
|
|
50
|
+
# * <tt>:with</tt> - The tags that you want to
|
|
51
|
+
# * <tt>:on</tt> - The context on which you want to tag
|
|
52
|
+
#
|
|
53
|
+
# Example:
|
|
54
|
+
# @user.tag(@photo, :with => "paris, normandy", :on => :locations)
|
|
55
|
+
def tag(taggable, opts = {})
|
|
56
|
+
opts.reverse_merge!(force: true)
|
|
57
|
+
skip_save = opts.delete(:skip_save)
|
|
58
|
+
return false unless taggable.respond_to?(:is_taggable?) && taggable.is_taggable?
|
|
59
|
+
|
|
60
|
+
raise 'You need to specify a tag context using :on' unless opts.key?(:on)
|
|
61
|
+
raise 'You need to specify some tags using :with' unless opts.key?(:with)
|
|
62
|
+
unless opts[:force] || taggable.tag_types.include?(opts[:on])
|
|
63
|
+
raise "No context :#{opts[:on]} defined in #{taggable.class}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
taggable.set_owner_tag_list_on(self, opts[:on].to_s, opts[:with])
|
|
67
|
+
taggable.save unless skip_save
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def tagger?
|
|
71
|
+
self.class.is_tagger?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
alias is_tagger? tagger?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
module SingletonMethods
|
|
78
|
+
def tagger?
|
|
79
|
+
true
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
alias is_tagger? tagger?
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|