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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1f6ce2d51035fb3fb89a0fb231fed320cf3e2cad
4
- data.tar.gz: 97b5143f687988711ee7899914bd34524ead070c
3
+ metadata.gz: 8d93a7c1beb5fbe71abb708118e35b734203691f
4
+ data.tar.gz: 6ae2ad0b0060b5265c879338aa26d4684d551a26
5
5
  SHA512:
6
- metadata.gz: 1a2f8d3abef2f906dc49195efb0220f3bc90edd0f10ff35296efaf365f113bbad7f0533bf14506b883a7b327a4d627b2169af6078df91402bbdcfa8c4b67409a
7
- data.tar.gz: e26e1cc6db0bc21dc6341362e6789621410f651a61feb6e2ee34fe9550041ad8cf568ffd230e953f2dc03369d779d3300e04931717334fccd4e4a9d6ab515289
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.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
@@ -51,4 +51,5 @@ Gem::Specification.new do |spec|
51
51
  spec.add_development_dependency "rubocop"
52
52
  spec.add_development_dependency "simplecov"
53
53
  spec.add_development_dependency "simplecov-rcov"
54
+ spec.add_development_dependency "timecop"
54
55
  end
@@ -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
- options = args.extract_options!
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
- args.flatten!
164
- args.map! { |argument| run_parser.new(argument).parse } if options[:parse] || options_parser
164
+ dup_args.flatten!
165
+ dup_args.map! { |argument| run_parser.new(argument).parse } if options[:parse] || options_parser
165
166
 
166
- args.flatten!
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
- options = tag_types.extract_options!
86
+ dup_tag_types = tag_types.dup
87
+ options = dup_tag_types.extract_options!.dup
87
88
 
88
- taggable_on(*tag_types, options.merge(preserve_tag_order: true))
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
- options = tag_types.extract_options!
119
- tag_types.flatten!
120
+ dup_tag_types = tag_types.dup
121
+ options = dup_tag_types.extract_options!.dup
122
+ dup_tag_types.flatten!
120
123
 
121
- tag_types.each do |tag_type|
124
+ dup_tag_types.each do |tag_type|
122
125
  next if tag_type.blank?
123
126
 
124
127
  define_tag tag_type, options
@@ -138,6 +138,7 @@ module ActsAsTaggableOnMongoid
138
138
  next if public_send(tag_list_name).present?
139
139
 
140
140
  public_send("#{tag_list_name}=", default)
141
+ changed_attributes.delete tag_list_name
141
142
  end
142
143
  end
143
144
 
@@ -7,9 +7,17 @@ module ActsAsTaggableOnMongoid
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  included do
10
- class_attribute :tag_types
10
+ class_attribute :my_tag_types
11
11
 
12
- self.tag_types ||= {}.with_indifferent_access
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 = {}.with_indifferent_access.merge!(self.tag_types || {})
35
- self.tag_types = new_tag_types
36
- tag_definition = new_tag_types[tag_type] = ActsAsTaggableOnMongoid::Taggable::TagTypeDefinition.new(self, tag_type, options)
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.each do |key, value|
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
- # rubocop:disable Layout/SpaceAroundOperators
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
- options = tag_list.extract_options!
50
- options[:parser] ||= parser if options.key?(:parse) || options.key?(:parser)
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, *tag_list, options)
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
- new_tags = Array.wrap(new_tags)
167
- options = new_tags.extract_options!
168
- options[:parse] = true unless options.key?(: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(*new_tags, options)
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
- value = Array.wrap(value)
69
- options = value.extract_options!
70
- options[:parse] = true unless options.key?(: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, value, options
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActsAsTaggableOnMongoid
4
- VERSION = "6.0.1.1"
4
+ VERSION = "6.0.1.2"
5
5
  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.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-12 00:00:00.000000000 Z
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