acts-as-taggable-on-mongoid 6.0.1.1 → 6.0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|