acts-as-taggable-on-mongoid 6.0.1.1 → 6.0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -1
- data/acts-as-taggable-on-mongoid.gemspec +1 -0
- data/lib/acts-as-taggable-on-mongoid.rb +10 -0
- data/lib/acts_as_taggable_on_mongoid/tag_list.rb +7 -5
- data/lib/acts_as_taggable_on_mongoid/taggable.rb +8 -5
- data/lib/acts_as_taggable_on_mongoid/taggable/core.rb +1 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/list_tags.rb +28 -5
- data/lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition.rb +42 -13
- data/lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition/attributes.rb +4 -4
- data/lib/acts_as_taggable_on_mongoid/taggable/tagged_with.rb +56 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query.rb +96 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query/all_tags_query.rb +21 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query/any_tags_query.rb +21 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query/base.rb +68 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query/exclude_tags_query.rb +22 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query/match_all_tags_query.rb +22 -0
- data/lib/acts_as_taggable_on_mongoid/version.rb +1 -1
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d93a7c1beb5fbe71abb708118e35b734203691f
|
4
|
+
data.tar.gz: 6ae2ad0b0060b5265c879338aa26d4684d551a26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5ac3c342c801316fb57ace4fc70f143db77c1204ace3cc3ee7a192de608e64dd5d2737afa9bd6284a355719dda04d34d474105410ee3b07947df895d61de2cb
|
7
|
+
data.tar.gz: 13f2f14dd4cda732d427f4e0c71f3eaf8efacbaa9a8bc74417fbf7ad68edb1a0b9e72523ee9d3e26552682d9ed7c1045f63548e0b790384c658e03d9c8267658
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
acts-as-taggable-on-mongoid (6.0.1.
|
4
|
+
acts-as-taggable-on-mongoid (6.0.1.2)
|
5
5
|
activesupport (~> 4.2)
|
6
6
|
mongoid (~> 5.2)
|
7
7
|
|
@@ -165,6 +165,7 @@ GEM
|
|
165
165
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
166
166
|
thor (0.19.4)
|
167
167
|
thread_safe (0.3.6)
|
168
|
+
timecop (0.9.1)
|
168
169
|
tzinfo (1.2.5)
|
169
170
|
thread_safe (~> 0.1)
|
170
171
|
unicode-display_width (1.4.0)
|
@@ -198,6 +199,7 @@ DEPENDENCIES
|
|
198
199
|
rubocop
|
199
200
|
simplecov
|
200
201
|
simplecov-rcov
|
202
|
+
timecop
|
201
203
|
|
202
204
|
BUNDLED WITH
|
203
205
|
1.16.4
|
@@ -25,6 +25,14 @@ module ActsAsTaggableOnMongoid
|
|
25
25
|
autoload :TagListDiff
|
26
26
|
end
|
27
27
|
|
28
|
+
autoload_under "taggable/tagged_with_query" do
|
29
|
+
autoload :Base
|
30
|
+
autoload :AllTagsQuery
|
31
|
+
autoload :AnyTagsQuery
|
32
|
+
autoload :ExcludeTagsQuery
|
33
|
+
autoload :MatchAllTagsQuery
|
34
|
+
end
|
35
|
+
|
28
36
|
autoload_under :Taggable do
|
29
37
|
# autoload :Cache
|
30
38
|
# autoload :Collection
|
@@ -32,6 +40,8 @@ module ActsAsTaggableOnMongoid
|
|
32
40
|
autoload :Changeable
|
33
41
|
autoload :TagTypeDefinition
|
34
42
|
autoload :ListTags
|
43
|
+
autoload :TaggedWith
|
44
|
+
autoload :TaggedWithQuery
|
35
45
|
# autoload :Ownership
|
36
46
|
# autoload :Related
|
37
47
|
end
|
@@ -46,7 +46,7 @@ module ActsAsTaggableOnMongoid
|
|
46
46
|
# tag_list.add("Fun", "Happy")
|
47
47
|
# tag_list.add("Fun, Happy", :parse => true)
|
48
48
|
def add(*names)
|
49
|
-
extract_and_apply_options!(names)
|
49
|
+
names = extract_and_apply_options!(names)
|
50
50
|
concat(names)
|
51
51
|
clean!
|
52
52
|
|
@@ -154,16 +154,18 @@ module ActsAsTaggableOnMongoid
|
|
154
154
|
# :reek:FeatureEnvy
|
155
155
|
# :reek:DuplicateMethodCall
|
156
156
|
def extract_and_apply_options!(args)
|
157
|
-
|
157
|
+
dup_args = args.dup
|
158
|
+
options = dup_args.extract_options!.dup
|
158
159
|
options.assert_valid_keys :parse, :parser
|
159
160
|
|
160
161
|
options_parser = options[:parser]
|
161
162
|
run_parser = options_parser || tag_definition.parser
|
162
163
|
|
163
|
-
|
164
|
-
|
164
|
+
dup_args.flatten!
|
165
|
+
dup_args.map! { |argument| run_parser.new(argument).parse } if options[:parse] || options_parser
|
165
166
|
|
166
|
-
|
167
|
+
dup_args.flatten!
|
168
|
+
dup_args
|
167
169
|
end
|
168
170
|
end
|
169
171
|
end
|
@@ -83,9 +83,10 @@ module ActsAsTaggableOnMongoid
|
|
83
83
|
# acts_as_ordered_taggable_on :languages, :skills
|
84
84
|
# end
|
85
85
|
def acts_as_ordered_taggable_on(*tag_types)
|
86
|
-
|
86
|
+
dup_tag_types = tag_types.dup
|
87
|
+
options = dup_tag_types.extract_options!.dup
|
87
88
|
|
88
|
-
taggable_on(*
|
89
|
+
taggable_on(*dup_tag_types, options.merge(preserve_tag_order: true))
|
89
90
|
end
|
90
91
|
|
91
92
|
private
|
@@ -107,6 +108,7 @@ module ActsAsTaggableOnMongoid
|
|
107
108
|
# and add hooks/callbacks that aren't needed without tags.
|
108
109
|
[ActsAsTaggableOnMongoid::Taggable::Core,
|
109
110
|
ActsAsTaggableOnMongoid::Taggable::Changeable,
|
111
|
+
ActsAsTaggableOnMongoid::Taggable::TaggedWith,
|
110
112
|
# include Collection - not sure we will need as done here. Need to think more on this one.
|
111
113
|
# include Cache - TODO: Add this.
|
112
114
|
# include Ownership - TODO: Add this.
|
@@ -115,10 +117,11 @@ module ActsAsTaggableOnMongoid
|
|
115
117
|
include include_module unless included_modules.include?(include_module)
|
116
118
|
end
|
117
119
|
|
118
|
-
|
119
|
-
|
120
|
+
dup_tag_types = tag_types.dup
|
121
|
+
options = dup_tag_types.extract_options!.dup
|
122
|
+
dup_tag_types.flatten!
|
120
123
|
|
121
|
-
|
124
|
+
dup_tag_types.each do |tag_type|
|
122
125
|
next if tag_type.blank?
|
123
126
|
|
124
127
|
define_tag tag_type, options
|
@@ -7,9 +7,17 @@ module ActsAsTaggableOnMongoid
|
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
9
|
included do
|
10
|
-
class_attribute :
|
10
|
+
class_attribute :my_tag_types
|
11
11
|
|
12
|
-
self.
|
12
|
+
self.my_tag_types ||= {}.with_indifferent_access
|
13
|
+
end
|
14
|
+
|
15
|
+
def tag_types
|
16
|
+
klass = self.class
|
17
|
+
|
18
|
+
self.my_tag_types = klass.cleanup_tag_types(my_tag_types, klass)
|
19
|
+
|
20
|
+
my_tag_types
|
13
21
|
end
|
14
22
|
|
15
23
|
def tag_definition(tag_type)
|
@@ -19,6 +27,21 @@ module ActsAsTaggableOnMongoid
|
|
19
27
|
end
|
20
28
|
|
21
29
|
class_methods do
|
30
|
+
def tag_types
|
31
|
+
self.my_tag_types = cleanup_tag_types(my_tag_types, self)
|
32
|
+
|
33
|
+
my_tag_types
|
34
|
+
end
|
35
|
+
|
36
|
+
# :reek:UtilityFunction
|
37
|
+
def cleanup_tag_types(tag_types, klass)
|
38
|
+
return tag_types if tag_types.values.all? { |tag_definition| tag_definition.owner == klass }
|
39
|
+
|
40
|
+
tag_types.each_with_object({}.with_indifferent_access) do |(key, tag_definition), hash|
|
41
|
+
hash[key] = ActsAsTaggableOnMongoid::Taggable::TagTypeDefinition.copy_from(klass, tag_definition)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
22
45
|
# In order to allow dynamic tags, return a default tag_definition for any missing tag_type.
|
23
46
|
# This means that any dynamic tag necessarily is created with the current defaults
|
24
47
|
def define_tag(tag_type, options = {})
|
@@ -31,9 +54,9 @@ module ActsAsTaggableOnMongoid
|
|
31
54
|
# tag_types is a class_attribute
|
32
55
|
# As such, we have to replace it each time with a new array so that inherited classes and instances
|
33
56
|
# are able to maintain separate lists if need be.
|
34
|
-
new_tag_types
|
35
|
-
self.
|
36
|
-
tag_definition
|
57
|
+
new_tag_types = {}.with_indifferent_access.merge!(tag_types || {})
|
58
|
+
self.my_tag_types = new_tag_types
|
59
|
+
tag_definition = new_tag_types[tag_type] = ActsAsTaggableOnMongoid::Taggable::TagTypeDefinition.new(self, tag_type, options)
|
37
60
|
|
38
61
|
tag_definition.define_base_relations
|
39
62
|
tag_definition.define_relations
|
@@ -18,6 +18,8 @@ module ActsAsTaggableOnMongoid
|
|
18
18
|
include ActsAsTaggableOnMongoid::Taggable::TagTypeDefinition::Changeable
|
19
19
|
|
20
20
|
def initialize(owner, tag_type, options = {})
|
21
|
+
options = options.dup
|
22
|
+
|
21
23
|
options.assert_valid_keys(:parser,
|
22
24
|
:preserve_tag_order,
|
23
25
|
:cached_in_model,
|
@@ -30,15 +32,35 @@ module ActsAsTaggableOnMongoid
|
|
30
32
|
|
31
33
|
self.default_value = options.delete(:default)
|
32
34
|
|
33
|
-
options
|
34
|
-
instance_variable_set("@#{key}", value)
|
35
|
-
end
|
35
|
+
save_options(options)
|
36
36
|
|
37
37
|
@owner = owner
|
38
38
|
@tag_type = tag_type
|
39
39
|
end
|
40
40
|
|
41
|
-
|
41
|
+
def self.copy_from(klass, tag_definition)
|
42
|
+
dup_hash = %i[parser
|
43
|
+
preserve_tag_order
|
44
|
+
cached_in_model
|
45
|
+
force_lowercase
|
46
|
+
force_parameterize
|
47
|
+
remove_unused_tags
|
48
|
+
tags_table
|
49
|
+
taggings_table].each_with_object({}) { |dup_key, opts_hash| opts_hash[dup_key] = tag_definition.public_send(dup_key) }
|
50
|
+
|
51
|
+
dup_hash[:default] = [tag_definition.default, parse: false]
|
52
|
+
|
53
|
+
ActsAsTaggableOnMongoid::Taggable::TagTypeDefinition.new klass,
|
54
|
+
tag_definition.tag_type,
|
55
|
+
dup_hash
|
56
|
+
end
|
57
|
+
|
58
|
+
def conflicts_with?(tag_definition)
|
59
|
+
%i[parser preserve_tag_order force_lowercase force_parameterize taggings_table].any? do |setting_name|
|
60
|
+
public_send(setting_name) != tag_definition.public_send(setting_name)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
42
64
|
# :reek:FeatureEnvy
|
43
65
|
|
44
66
|
# I've defined the parser as being required to return an array of strings.
|
@@ -46,14 +68,13 @@ module ActsAsTaggableOnMongoid
|
|
46
68
|
# to apply the rules to that list (like case sensitivity and parameterization, etc.) to get the final
|
47
69
|
# list.
|
48
70
|
def parse(*tag_list)
|
49
|
-
|
50
|
-
options
|
71
|
+
dup_tag_list = tag_list.dup
|
72
|
+
options = dup_tag_list.extract_options!.dup
|
73
|
+
options[:parser] ||= parser if options[:parse] || options.key?(:parser)
|
51
74
|
|
52
|
-
ActsAsTaggableOnMongoid::TagList.new(self, *
|
75
|
+
ActsAsTaggableOnMongoid::TagList.new(self, *dup_tag_list, options)
|
53
76
|
end
|
54
77
|
|
55
|
-
# rubocop:enable Layout/SpaceAroundOperators
|
56
|
-
|
57
78
|
def taggings_order
|
58
79
|
@taggings_order = if preserve_tag_order?
|
59
80
|
[:created_at.asc, :id.asc]
|
@@ -163,11 +184,11 @@ module ActsAsTaggableOnMongoid
|
|
163
184
|
|
164
185
|
owner.taggable_mixin.module_eval do
|
165
186
|
define_method("#{tag_definition.tag_list_name}=") do |new_tags|
|
166
|
-
|
167
|
-
options =
|
168
|
-
options[:parse] =
|
187
|
+
dup_tags = Array.wrap(new_tags).dup
|
188
|
+
options = dup_tags.extract_options!.dup
|
189
|
+
options[:parse] = options.fetch(:parse) { true }
|
169
190
|
|
170
|
-
new_list = tag_definition.parse(*
|
191
|
+
new_list = tag_definition.parse(*dup_tags, options)
|
171
192
|
|
172
193
|
mark_tag_list_changed(new_list)
|
173
194
|
tag_list_set(new_list)
|
@@ -184,6 +205,14 @@ module ActsAsTaggableOnMongoid
|
|
184
205
|
end
|
185
206
|
end
|
186
207
|
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
def save_options(options)
|
212
|
+
options.each do |key, value|
|
213
|
+
instance_variable_set("@#{key}", value)
|
214
|
+
end
|
215
|
+
end
|
187
216
|
end
|
188
217
|
end
|
189
218
|
end
|
@@ -65,11 +65,11 @@ module ActsAsTaggableOnMongoid
|
|
65
65
|
private
|
66
66
|
|
67
67
|
def default_value=(value)
|
68
|
-
|
69
|
-
options =
|
70
|
-
options[:parse] =
|
68
|
+
dup_value = Array.wrap(value).dup
|
69
|
+
options = dup_value.extract_options!.dup
|
70
|
+
options[:parse] = options.fetch(:parse) { true }
|
71
71
|
|
72
|
-
@default = ActsAsTaggableOnMongoid::TagList.new self,
|
72
|
+
@default = ActsAsTaggableOnMongoid::TagList.new self, dup_value, options
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
module Taggable
|
5
|
+
# Include methods and scopes to a Taggable class to allow searching for Taggable objects.
|
6
|
+
module TaggedWith
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
##
|
11
|
+
# Return a scope of objects that are tagged within the given context with the specified tags.
|
12
|
+
#
|
13
|
+
# @param tags The tags that we want to query for
|
14
|
+
# @param [Hash] options A hash of options to alter you query:
|
15
|
+
# * <tt>:on</tt> - The context to filter the query by. (default - all contexts)
|
16
|
+
# To allow broader compatibility with `ActsAsTaggableOn`, context can be an array of values
|
17
|
+
# for a set of tags that are equivalent enough (the parsing of the list is the same
|
18
|
+
# and they use the same taggings table.)
|
19
|
+
#
|
20
|
+
# If you are trying to find values for a tag that downcases and one that doesn't, the
|
21
|
+
# code is unsure how to handle this and raises an exception.
|
22
|
+
# * <tt>:exclude</tt> - if set to true, return objects that are *NOT* tagged with the specified tags
|
23
|
+
# * <tt>:any</tt> - if set to true, return objects that are tagged with *ANY* of the specified tags
|
24
|
+
# * <tt>:match_all</tt> - if set to true, return objects that are tagged with *ONLY* the specified tags
|
25
|
+
# * <tt>:all</tt> - if set to true, return objects that are tagged with *ALL* of the specified tags
|
26
|
+
# If none of :any, :eclude, or :all are set, :all is the default.
|
27
|
+
# * <tt>:start_at</tt> - Restrict the tags to those created on or after a certain time
|
28
|
+
# * <tt>:end_at</tt> - Restrict the tags to those created before a certain time
|
29
|
+
# * <tt>:wild</tt> - Match all passed in tags as a regex of /%tag%/
|
30
|
+
# * <tt>:parse</tt> - Indicates if the tags should be parsed or not.
|
31
|
+
# * <tt>:parser</tt> - The parser to be used to parse the tags.
|
32
|
+
#
|
33
|
+
# The following options are not currently supported yet:
|
34
|
+
# * <tt>:order_by_matching_tag_count</tt> - if set to true and used with :any, sort by objects matching the most tags,
|
35
|
+
# descending
|
36
|
+
# * <tt>:owned_by</tt> - return objects that are *ONLY* owned by the owner
|
37
|
+
# * <tt>:order</tt> - Not supported because you cannot sort the results by
|
38
|
+
# the taggings in this implementation and an order on the taggable
|
39
|
+
# can easily be added by the consumer as needed.
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
# User.tagged_with("awesome", "cool", on: tags) # Users that are tagged with awesome and cool
|
43
|
+
# User.tagged_with("awesome", "cool", on: tags, :exclude => true) # Users that are not tagged with awesome or cool
|
44
|
+
# User.tagged_with("awesome", "cool", on: tags, :any => true) # Users that are tagged with awesome or cool
|
45
|
+
# User.tagged_with("awesome", "cool", on: tags, :any => true, :order_by_matching_tag_count => true) # Sort by users who match the most
|
46
|
+
# tags, descending
|
47
|
+
# User.tagged_with("awesome", "cool", on: tags, :match_all => true) # Users that are tagged with just awesome and cool
|
48
|
+
# User.tagged_with("awesome", "cool", on: tags, :owned_by => foo ) # Users that are tagged with just awesome and cool by 'foo'
|
49
|
+
# User.tagged_with("awesome", "cool", on: tags, :owned_by => foo, :start_at => Date.today ) # Users that are tagged with just awesome,
|
50
|
+
# cool by 'foo' and starting today
|
51
|
+
|
52
|
+
scope :tagged_with, (->(*tags) { where ::ActsAsTaggableOnMongoid::Taggable::TaggedWithQuery.new(self, *tags).build })
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
module Taggable
|
5
|
+
# A class finding all Taggable objects for the passed in tags based on the passed in parameters.
|
6
|
+
# Details for how the query will work are in the TaggedWith module.
|
7
|
+
class TaggedWithQuery
|
8
|
+
attr_reader :taggable_class,
|
9
|
+
:tags,
|
10
|
+
:options,
|
11
|
+
:tag_definition
|
12
|
+
|
13
|
+
def initialize(taggable_class, *tags)
|
14
|
+
new_tags = tags.dup
|
15
|
+
@taggable_class = taggable_class
|
16
|
+
@options = new_tags.extract_options!.dup
|
17
|
+
@tags = new_tags
|
18
|
+
|
19
|
+
cleanup_options
|
20
|
+
|
21
|
+
context = on_context(*options[:on])
|
22
|
+
@tag_definition = taggable_class.tag_types[context]
|
23
|
+
end
|
24
|
+
|
25
|
+
def build
|
26
|
+
klass = if options[:exclude].present?
|
27
|
+
ExcludeTagsQuery
|
28
|
+
elsif options[:any].present?
|
29
|
+
AnyTagsQuery
|
30
|
+
elsif options[:match_all]
|
31
|
+
MatchAllTagsQuery
|
32
|
+
else
|
33
|
+
AllTagsQuery
|
34
|
+
end
|
35
|
+
|
36
|
+
klass.new(tag_definition, tag_list, options).build
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def cleanup_options
|
42
|
+
options[:on] = Array.wrap(options[:on] || options.delete(:context))
|
43
|
+
options[:parse] = options.fetch(:parse) { true } || options.key?(:parser)
|
44
|
+
|
45
|
+
validate_options
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_options
|
49
|
+
options.assert_valid_keys :parse,
|
50
|
+
:parser,
|
51
|
+
:wild,
|
52
|
+
:exclude,
|
53
|
+
:match_all,
|
54
|
+
:all,
|
55
|
+
:any,
|
56
|
+
:on,
|
57
|
+
:start_at,
|
58
|
+
:end_at
|
59
|
+
end
|
60
|
+
|
61
|
+
def on_context(*contexts)
|
62
|
+
test_contexts = (contexts.presence || taggable_class.tag_types.keys).flatten
|
63
|
+
primary_context = test_contexts.first
|
64
|
+
|
65
|
+
test_contexts.each do |context|
|
66
|
+
raise "conflicting context definitions" if conflicting_context?(primary_context, context)
|
67
|
+
end
|
68
|
+
|
69
|
+
primary_context
|
70
|
+
end
|
71
|
+
|
72
|
+
# :reek:FeatureEnvy
|
73
|
+
|
74
|
+
def conflicting_context?(left, right)
|
75
|
+
return false if left == right
|
76
|
+
|
77
|
+
tag_types = taggable_class.tag_types
|
78
|
+
|
79
|
+
tag_types[left].conflicts_with? tag_types[right]
|
80
|
+
end
|
81
|
+
|
82
|
+
def tag_list
|
83
|
+
@tag_list ||= build_tag_list
|
84
|
+
end
|
85
|
+
|
86
|
+
def build_tag_list
|
87
|
+
return [] if tag_definition.blank?
|
88
|
+
|
89
|
+
tag_list = ActsAsTaggableOnMongoid::TagList.new(tag_definition, *tags, options.slice(:parse, :parser))
|
90
|
+
tag_list = tag_list.map { |tag| /#{tag}/ } if options[:wild]
|
91
|
+
|
92
|
+
tag_list
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
module Taggable
|
5
|
+
class TaggedWithQuery
|
6
|
+
# A class finding all Taggable objects which include all of the passed in tags (may include other tags as well).
|
7
|
+
class AllTagsQuery < ActsAsTaggableOnMongoid::Taggable::TaggedWithQuery::Base
|
8
|
+
def build
|
9
|
+
{ :id.in => included_ids }
|
10
|
+
end
|
11
|
+
|
12
|
+
def included_ids
|
13
|
+
selector = Origin::Selector.new
|
14
|
+
selector[:count] = tag_list.count
|
15
|
+
|
16
|
+
build_ids_from(selector)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
module Taggable
|
5
|
+
class TaggedWithQuery
|
6
|
+
# A class finding all Taggable objects which include any of the passed in tags.
|
7
|
+
class AnyTagsQuery < ActsAsTaggableOnMongoid::Taggable::TaggedWithQuery::Base
|
8
|
+
def build
|
9
|
+
{ :id.in => included_ids }
|
10
|
+
end
|
11
|
+
|
12
|
+
def included_ids
|
13
|
+
selector = Origin::Selector.new
|
14
|
+
selector[:count] = { "$gt" => 0 }
|
15
|
+
|
16
|
+
build_ids_from(selector)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
module Taggable
|
5
|
+
class TaggedWithQuery
|
6
|
+
# A base class with shared code for match queries.
|
7
|
+
class Base
|
8
|
+
attr_reader :taggable_model,
|
9
|
+
:tag_definition,
|
10
|
+
:tag_list,
|
11
|
+
:options
|
12
|
+
|
13
|
+
def initialize(tag_definition, tag_list, options)
|
14
|
+
@tag_definition = tag_definition
|
15
|
+
@tag_list = tag_list
|
16
|
+
@options = options
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Any is relatively simple, but All and exclude are a bit more complicated. To make the code simpler
|
22
|
+
# I'm treating all of them the same.
|
23
|
+
# We build an aggregation of all of the matching taggables whose key_name is in the list of tags with the
|
24
|
+
# count of the matching key names. We then filter on that count.
|
25
|
+
#
|
26
|
+
# * All - count == tag_list count
|
27
|
+
# * Any - count > 0
|
28
|
+
# * Exclude - count > 0 (but anything that isn't in that count)
|
29
|
+
def build_ids_from(count_selector)
|
30
|
+
where_query = tagging_query.where(:tag_name.in => tag_list)
|
31
|
+
build_ids_from_query(where_query, count_selector)
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_tagless_ids_from(count_selector)
|
35
|
+
build_ids_from_query(tagging_query, count_selector)
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_ids_from_query(where_query, count_selector)
|
39
|
+
pipeline = where_query.
|
40
|
+
group(_id: { taggable_id: "$taggable_id", tag_name: "$tag_name" }).
|
41
|
+
group(_id: "$_id.taggable_id", :count.sum => 1).
|
42
|
+
pipeline.
|
43
|
+
concat(count_selector.to_pipeline)
|
44
|
+
|
45
|
+
tag_definition.taggings_table.collection.aggregate(pipeline).to_a.map { |counts| counts[:_id] }
|
46
|
+
end
|
47
|
+
|
48
|
+
def tagging_query
|
49
|
+
context = options[:on]
|
50
|
+
tagging_query = tag_definition.taggings_table.where(taggable_type: tag_definition.owner.name)
|
51
|
+
tagging_query = tagging_query.where(:context.in => context) if context.present?
|
52
|
+
|
53
|
+
time_constraints tagging_query
|
54
|
+
end
|
55
|
+
|
56
|
+
def time_constraints(tagging_query)
|
57
|
+
start_at = options[:start_at]
|
58
|
+
end_at = options[:end_at]
|
59
|
+
|
60
|
+
tagging_query = tagging_query.where(:created_at.gte => start_at) if start_at.present?
|
61
|
+
tagging_query = tagging_query.where(:created_at.lt => end_at) if end_at.present?
|
62
|
+
|
63
|
+
tagging_query
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
module Taggable
|
5
|
+
class TaggedWithQuery
|
6
|
+
# A class finding all Taggable objects which exclude all of the passed in tags.
|
7
|
+
class ExcludeTagsQuery < ActsAsTaggableOnMongoid::Taggable::TaggedWithQuery::Base
|
8
|
+
def build
|
9
|
+
{ :id.in => included_ids }
|
10
|
+
end
|
11
|
+
|
12
|
+
def included_ids
|
13
|
+
selector = Origin::Selector.new
|
14
|
+
selector[:count] = { "$gt" => 0 }
|
15
|
+
|
16
|
+
ids = build_ids_from(selector)
|
17
|
+
build_tagless_ids_from(selector) - ids
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
module Taggable
|
5
|
+
class TaggedWithQuery
|
6
|
+
# A class finding all Taggable objects which include all and only all of the passed in tags.
|
7
|
+
class MatchAllTagsQuery < ActsAsTaggableOnMongoid::Taggable::TaggedWithQuery::Base
|
8
|
+
def build
|
9
|
+
{ :id.in => included_ids }
|
10
|
+
end
|
11
|
+
|
12
|
+
def included_ids
|
13
|
+
selector = Origin::Selector.new
|
14
|
+
selector[:count] = { "$ne" => tag_list.count }
|
15
|
+
|
16
|
+
AllTagsQuery.new(tag_definition, tag_list, options).included_ids -
|
17
|
+
build_tagless_ids_from(selector)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts-as-taggable-on-mongoid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.0.1.
|
4
|
+
version: 6.0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RealNobody
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-09-
|
11
|
+
date: 2018-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -282,6 +282,20 @@ dependencies:
|
|
282
282
|
- - ">="
|
283
283
|
- !ruby/object:Gem::Version
|
284
284
|
version: '0'
|
285
|
+
- !ruby/object:Gem::Dependency
|
286
|
+
name: timecop
|
287
|
+
requirement: !ruby/object:Gem::Requirement
|
288
|
+
requirements:
|
289
|
+
- - ">="
|
290
|
+
- !ruby/object:Gem::Version
|
291
|
+
version: '0'
|
292
|
+
type: :development
|
293
|
+
prerelease: false
|
294
|
+
version_requirements: !ruby/object:Gem::Requirement
|
295
|
+
requirements:
|
296
|
+
- - ">="
|
297
|
+
- !ruby/object:Gem::Version
|
298
|
+
version: '0'
|
285
299
|
description: A partial mongoid implementation of tagging based on/inspired by acts-as-taggable-on.
|
286
300
|
email:
|
287
301
|
- admin@cardtapp.com
|
@@ -323,6 +337,13 @@ files:
|
|
323
337
|
- lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition/attributes.rb
|
324
338
|
- lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition/changeable.rb
|
325
339
|
- lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition/names.rb
|
340
|
+
- lib/acts_as_taggable_on_mongoid/taggable/tagged_with.rb
|
341
|
+
- lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query.rb
|
342
|
+
- lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query/all_tags_query.rb
|
343
|
+
- lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query/any_tags_query.rb
|
344
|
+
- lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query/base.rb
|
345
|
+
- lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query/exclude_tags_query.rb
|
346
|
+
- lib/acts_as_taggable_on_mongoid/taggable/tagged_with_query/match_all_tags_query.rb
|
326
347
|
- lib/acts_as_taggable_on_mongoid/taggable/utils/tag_list_diff.rb
|
327
348
|
- lib/acts_as_taggable_on_mongoid/version.rb
|
328
349
|
homepage: http://www.cardtapp.com
|