acts-as-taggable-on-mongoid 6.0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +63 -0
- data/.gitignore +54 -0
- data/.reek.yml +8 -0
- data/.rspec +2 -0
- data/.rubocop.yml +59 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +203 -0
- data/LICENSE.txt +21 -0
- data/PULL_REQUEST_TEMPLATE.md +11 -0
- data/README.md +741 -0
- data/Rakefile +8 -0
- data/acts-as-taggable-on-mongoid.gemspec +54 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/codecov.yml +3 -0
- data/config/pronto-circleci.yml +7 -0
- data/lib/acts-as-taggable-on-mongoid.rb +80 -0
- data/lib/acts_as_taggable_on_mongoid/configuration.rb +94 -0
- data/lib/acts_as_taggable_on_mongoid/default_parser.rb +120 -0
- data/lib/acts_as_taggable_on_mongoid/errors/duplicate_tag_error.rb +9 -0
- data/lib/acts_as_taggable_on_mongoid/generic_parser.rb +44 -0
- data/lib/acts_as_taggable_on_mongoid/models/tag.rb +103 -0
- data/lib/acts_as_taggable_on_mongoid/models/tagging.rb +80 -0
- data/lib/acts_as_taggable_on_mongoid/tag_list.rb +169 -0
- data/lib/acts_as_taggable_on_mongoid/taggable.rb +131 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/changeable.rb +71 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/core.rb +219 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/list_tags.rb +45 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition.rb +189 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition/attributes.rb +77 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition/changeable.rb +140 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition/names.rb +39 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/utils/tag_list_diff.rb +121 -0
- data/lib/acts_as_taggable_on_mongoid/version.rb +5 -0
- metadata +352 -0
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
module Models
|
5
|
+
# A class representing all tags that have ever been set on a model.
|
6
|
+
class Tag
|
7
|
+
include Mongoid::Document
|
8
|
+
include Mongoid::Timestamps
|
9
|
+
|
10
|
+
field :name, type: String
|
11
|
+
field :taggings_count, type: Integer, default: 0
|
12
|
+
field :context, type: String
|
13
|
+
field :taggable_type, type: String
|
14
|
+
|
15
|
+
# field :type, type: String
|
16
|
+
|
17
|
+
index({ name: 1, taggable_type: 1, context: 1 }, unique: true)
|
18
|
+
|
19
|
+
### ASSOCIATIONS:
|
20
|
+
|
21
|
+
has_many :taggings, dependent: :destroy, class_name: "ActsAsTaggableOnMongoid::Models::Tagging"
|
22
|
+
|
23
|
+
### VALIDATIONS:
|
24
|
+
|
25
|
+
validates :name, presence: true
|
26
|
+
validates :context, presence: true
|
27
|
+
validates :taggable_type, presence: true
|
28
|
+
validates :name, uniqueness: { scope: %i[context taggable_type] }
|
29
|
+
|
30
|
+
### SCOPES:
|
31
|
+
scope :most_used, ->(limit = 20) { order("taggings_count desc").limit(limit) }
|
32
|
+
scope :least_used, ->(limit = 20) { order("taggings_count asc").limit(limit) }
|
33
|
+
|
34
|
+
scope :named, ->(name) { where(name: as_8bit_ascii(name)) }
|
35
|
+
scope :named_any, ->(*names) { where(:name.in => names.map { |name| as_8bit_ascii(name) }) }
|
36
|
+
scope :named_like, ->(name) { where(name: /#{as_8bit_ascii(name)}/i) }
|
37
|
+
scope :named_like_any, ->(*names) { where(:name.in => names.map { |name| /#{as_8bit_ascii(name)}/i }) }
|
38
|
+
scope :for_context, ->(context) { where(context: context) }
|
39
|
+
scope :for_taggable_class, ->(taggable_type) { where(taggable_type: taggable_type.name) }
|
40
|
+
scope :for_tag, ->(tag_definition) { for_taggable_class(tag_definition.owner).for_context(tag_definition.tag_type) }
|
41
|
+
|
42
|
+
### CLASS METHODS:
|
43
|
+
|
44
|
+
class << self
|
45
|
+
def find_or_create_all_with_like_by_name(tag_definition, *list)
|
46
|
+
list = ActsAsTaggableOnMongoid::TagList.new(tag_definition, *Array.wrap(list).flatten)
|
47
|
+
|
48
|
+
return [] if list.empty?
|
49
|
+
|
50
|
+
list.map do |tag_name|
|
51
|
+
begin
|
52
|
+
tries ||= 3
|
53
|
+
|
54
|
+
existing_tag = tag_definition.tags_table.for_tag(tag_definition).named(tag_name).first
|
55
|
+
|
56
|
+
existing_tag || create_tag(tag_definition, tag_name)
|
57
|
+
rescue Mongoid::Errors::Validations
|
58
|
+
# :nocov:
|
59
|
+
if (tries -= 1).positive?
|
60
|
+
retry
|
61
|
+
end
|
62
|
+
|
63
|
+
raise ActsAsTaggableOnMongoid::Errors::DuplicateTagError.new, "'#{tag_name}' has already been taken"
|
64
|
+
# :nocov:
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def create_tag(tag_definition, name)
|
72
|
+
tag_definition.tags_table.create(name: name,
|
73
|
+
context: tag_definition.tag_type,
|
74
|
+
taggable_type: tag_definition.owner.name)
|
75
|
+
end
|
76
|
+
|
77
|
+
def as_8bit_ascii(string)
|
78
|
+
string = string.to_s
|
79
|
+
if defined?(Encoding)
|
80
|
+
string.dup.force_encoding("BINARY")
|
81
|
+
else
|
82
|
+
# :nocov:
|
83
|
+
string.mb_chars
|
84
|
+
# :nocov:
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
### INSTANCE METHODS:
|
90
|
+
|
91
|
+
def ==(other)
|
92
|
+
super || (other.class == self.class &&
|
93
|
+
name == other.name &&
|
94
|
+
context == other.context &&
|
95
|
+
taggable_type == other.taggable_type)
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_s
|
99
|
+
name
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
module Models
|
5
|
+
# A class representing the actual tags assigned to a particular model object
|
6
|
+
class Tagging
|
7
|
+
include Mongoid::Document
|
8
|
+
include Mongoid::Timestamps
|
9
|
+
|
10
|
+
DEFAULT_CONTEXT = "tags"
|
11
|
+
|
12
|
+
after_save :tagging_saved
|
13
|
+
after_destroy :tagging_destroyed
|
14
|
+
|
15
|
+
field :tag_name, type: String
|
16
|
+
field :context, type: String
|
17
|
+
|
18
|
+
belongs_to :tag, counter_cache: true, inverse_of: :taggings
|
19
|
+
belongs_to :taggable, polymorphic: true
|
20
|
+
# belongs_to :tagger, { polymorphic: true, optional: true }
|
21
|
+
|
22
|
+
# If/when adding the concept of a tagger, this index will need to be changed.
|
23
|
+
index({ taggable_id: 1, taggable_type: 1, context: 1, tag_name: 1 }, unique: true, name: "tagging_taggable_context_tag_name")
|
24
|
+
index(tag_name: 1)
|
25
|
+
index(tag_id: 1, tag_type: 1)
|
26
|
+
|
27
|
+
# scope :owned_by, ->(owner) { where(tagger: owner) }
|
28
|
+
# scope :not_owned, -> { where(tagger_id: nil, tagger_type: nil) }
|
29
|
+
|
30
|
+
scope :by_contexts, ->(*contexts) { where(:context.in => Array.wrap(contexts.presence || DEFAULT_CONTEXT)) }
|
31
|
+
scope :by_context, ->(context = DEFAULT_CONTEXT) { by_contexts(context.to_s) }
|
32
|
+
scope :for_tag, ->(tag_definition) { where(taggable_type: tag_definition.owner.name).by_context(tag_definition.tag_type) }
|
33
|
+
|
34
|
+
validates :tag_name, presence: true
|
35
|
+
validates :context, presence: true
|
36
|
+
validates :tag, presence: true
|
37
|
+
validates :taggable, presence: true
|
38
|
+
|
39
|
+
# validates :tag_id, uniqueness: {scope: [:taggable_type, :taggable_id, :context, :tagger_id, :tagger_type]}
|
40
|
+
validates :tag_name, uniqueness: { scope: %i[taggable_type taggable_id context] }
|
41
|
+
# validates :tag_id, uniqueness: {scope: [:taggable_type, :taggable_id, :context, :tagger_id, :tagger_type]}
|
42
|
+
validates :tag_id, uniqueness: { scope: %i[taggable_type taggable_id context] }
|
43
|
+
|
44
|
+
after_destroy :remove_unused_tags
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def remove_unused_tags
|
49
|
+
return nil unless taggable
|
50
|
+
|
51
|
+
tag_definition = taggable.tag_types[context]
|
52
|
+
|
53
|
+
return unless tag_definition&.remove_unused_tags?
|
54
|
+
|
55
|
+
tag.destroy if tag.reload.taggings_count.zero?
|
56
|
+
end
|
57
|
+
|
58
|
+
def tagging_saved
|
59
|
+
tag_definition = taggable.tag_types[context]
|
60
|
+
|
61
|
+
return unless tag_definition
|
62
|
+
|
63
|
+
tag_list = taggable.public_send(tag_definition.tag_list_name)
|
64
|
+
tag_list.add_tagging(self)
|
65
|
+
end
|
66
|
+
|
67
|
+
def tagging_destroyed
|
68
|
+
taggable_was = taggable_type_was.constantize.where(id: taggable_id_was).first
|
69
|
+
|
70
|
+
return unless taggable_was
|
71
|
+
|
72
|
+
tag_definition = taggable_was.tag_types[context_was]
|
73
|
+
|
74
|
+
return unless tag_definition
|
75
|
+
|
76
|
+
taggable_was.public_send(tag_definition.tag_list_name).remove(tag_name_was)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require "active_support/core_ext/module/delegation"
|
4
|
+
|
5
|
+
module ActsAsTaggableOnMongoid
|
6
|
+
# A list of tags. The TagList must be initialized with a tag definition so that it knows how to clean
|
7
|
+
# the list properly and to convert the list to a string.
|
8
|
+
#
|
9
|
+
# All methods that add objects to the list (initialization, concat, etc.) optionally take an array of values including
|
10
|
+
# options to parse the values and to optionally specifiy the parser to use. If no parser is specified, then
|
11
|
+
# parser for the tag_definition is used.
|
12
|
+
#
|
13
|
+
# If the input value(s) are to be parsed, then all values passed in are parsed.
|
14
|
+
#
|
15
|
+
# Examples:
|
16
|
+
# TagList.new(tag_definition, "value 1", "value 2")
|
17
|
+
# # > TagList<> ["value 1", "value 2"]
|
18
|
+
#
|
19
|
+
# TagList.new(tag_definition, "value 1, value 2", parse: true)
|
20
|
+
# # > TagList<> ["value 1", "value 2"]
|
21
|
+
#
|
22
|
+
# TagList.new(tag_definition, "value 1, value 2", "value 3, value 4", parse: true)
|
23
|
+
# # > TagList<> ["value 1", "value 2", "value 3", "value 4"]
|
24
|
+
#
|
25
|
+
# TagList.new(tag_definition, "value 1, value 2", "value 3, value 4", parser: ActsAsTaggableOnMongoid::GenericParser)
|
26
|
+
# # > TagList<> ["value 1", "value 2", "value 3", "value 4"]
|
27
|
+
|
28
|
+
# :reek:MissingSafeMethod
|
29
|
+
# :reek:SubclassedFromCoreClass
|
30
|
+
class TagList < Array
|
31
|
+
# :reek:Attribute
|
32
|
+
attr_accessor :taggable
|
33
|
+
attr_reader :tag_definition
|
34
|
+
|
35
|
+
def initialize(tag_definition, *args)
|
36
|
+
@tag_definition = tag_definition
|
37
|
+
|
38
|
+
add(*args)
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Add tags to the tag_list. Duplicate or blank tags will be ignored.
|
43
|
+
# Use the <tt>:parse</tt> option to add an unparsed tag string.
|
44
|
+
#
|
45
|
+
# Example:
|
46
|
+
# tag_list.add("Fun", "Happy")
|
47
|
+
# tag_list.add("Fun, Happy", :parse => true)
|
48
|
+
def add(*names)
|
49
|
+
extract_and_apply_options!(names)
|
50
|
+
concat(names)
|
51
|
+
clean!
|
52
|
+
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
# Append---Add the tag to the tag_list. This
|
57
|
+
# expression returns the tag_list itself, so several appends
|
58
|
+
# may be chained together.
|
59
|
+
def <<(obj)
|
60
|
+
add(obj)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Concatenation --- Returns a new tag list built by concatenating the
|
64
|
+
# two tag lists together to produce a third tag list.
|
65
|
+
def +(other)
|
66
|
+
TagList.new(tag_definition).add(*self).add(other)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Appends the elements of +other_tag_list+ to +self+.
|
70
|
+
def concat(other_tag_list)
|
71
|
+
notify_will_change
|
72
|
+
|
73
|
+
super(other_tag_list).send(:clean!)
|
74
|
+
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# Remove specific tags from the tag_list.
|
80
|
+
# Use the <tt>:parse</tt> option to add an unparsed tag string.
|
81
|
+
#
|
82
|
+
# Example:
|
83
|
+
# tag_list.remove("Sad", "Lonely")
|
84
|
+
# tag_list.remove("Sad, Lonely", :parse => true)
|
85
|
+
def remove(*names)
|
86
|
+
remove_list = ActsAsTaggableOnMongoid::TagList.new(tag_definition, *names)
|
87
|
+
|
88
|
+
notify_will_change
|
89
|
+
|
90
|
+
delete_if { |name| remove_list.include?(name) }
|
91
|
+
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Transform the tag_list into a tag string suitable for editing in a form.
|
97
|
+
# The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
|
98
|
+
#
|
99
|
+
# Example:
|
100
|
+
# tag_list = TagList.new("Round", "Square,Cube")
|
101
|
+
# tag_list.to_s # 'Round, "Square,Cube"'
|
102
|
+
def to_s
|
103
|
+
tag_definition.parser.new(*self).to_s
|
104
|
+
end
|
105
|
+
|
106
|
+
def notify_will_change
|
107
|
+
return unless taggable
|
108
|
+
|
109
|
+
taggable.tag_list_on_changed tag_definition
|
110
|
+
end
|
111
|
+
|
112
|
+
# :reek:ManualDispatch
|
113
|
+
def ==(other)
|
114
|
+
if tag_definition.preserve_tag_order?
|
115
|
+
super
|
116
|
+
elsif other.respond_to?(:sort)
|
117
|
+
self&.sort == other.sort
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_tagging(tagging)
|
122
|
+
orig_taggable = taggable
|
123
|
+
@taggable = nil
|
124
|
+
|
125
|
+
begin
|
126
|
+
tag = tagging.tag_name
|
127
|
+
|
128
|
+
self << tag unless include?(tag)
|
129
|
+
ensure
|
130
|
+
@taggable = orig_taggable
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def clean!
|
137
|
+
reject!(&:blank?)
|
138
|
+
|
139
|
+
map!(&:to_s)
|
140
|
+
map!(&:strip)
|
141
|
+
|
142
|
+
conditional_clean_rules
|
143
|
+
end
|
144
|
+
|
145
|
+
def conditional_clean_rules
|
146
|
+
map! { |tag| tag.mb_chars.downcase.to_s } if tag_definition.force_lowercase?
|
147
|
+
map!(&:parameterize) if tag_definition.force_parameterize?
|
148
|
+
|
149
|
+
uniq!
|
150
|
+
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
154
|
+
# :reek:FeatureEnvy
|
155
|
+
# :reek:DuplicateMethodCall
|
156
|
+
def extract_and_apply_options!(args)
|
157
|
+
options = args.extract_options!
|
158
|
+
options.assert_valid_keys :parse, :parser
|
159
|
+
|
160
|
+
options_parser = options[:parser]
|
161
|
+
run_parser = options_parser || tag_definition.parser
|
162
|
+
|
163
|
+
args.flatten!
|
164
|
+
args.map! { |argument| run_parser.new(argument).parse } if options[:parse] || options_parser
|
165
|
+
|
166
|
+
args.flatten!
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
# This module defines the class methods to be added to the Mongoid model so that
|
5
|
+
# tags can be defined on and added to a model.
|
6
|
+
#
|
7
|
+
# When a tag is added to a model, additional modules will be included to add methods that
|
8
|
+
# are needed only if a tag is actually being used.
|
9
|
+
module Taggable
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
# rubocop:disable Metrics/BlockLength
|
13
|
+
|
14
|
+
class_methods do
|
15
|
+
# Options include:
|
16
|
+
# * parser
|
17
|
+
# The class to be used to parse strings.
|
18
|
+
# * preserve_tag_order
|
19
|
+
# If true, the _list accessor will save and returns tags in the order they are added to the object.
|
20
|
+
# * cached_in_model
|
21
|
+
# Not currently supported
|
22
|
+
# * force_lowercase
|
23
|
+
# If true, values stored for tags will first be downcased to make the values effectively case-insensitive
|
24
|
+
# * force_parameterize
|
25
|
+
# If true, values stored for tags will be parameterized
|
26
|
+
# * remove_unused_tags
|
27
|
+
# If true, when there are no more taggings for a tag, the tag will be destroyed
|
28
|
+
# * tags_table
|
29
|
+
# The class to use for Tags
|
30
|
+
# * taggings_table
|
31
|
+
# The class to use for Taggings
|
32
|
+
# * default
|
33
|
+
# A default value. Any value that can be used for list assignment or adding values to a list
|
34
|
+
# can be used. If custom options like `parse` or `parser` are to be used for the default, the value
|
35
|
+
# must be passed in as an array with a hash as the last value. Like list setters, parsing is assumed.
|
36
|
+
# Example: default: ["this, is, a, list", parser: ActsAsTaggableOnMongoid::GenericParser]
|
37
|
+
|
38
|
+
##
|
39
|
+
# This is an alias for calling <tt>acts_as_taggable_on :tags</tt>.
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
# class Book < ActiveRecord::Base
|
43
|
+
# acts_as_taggable
|
44
|
+
# end
|
45
|
+
def acts_as_taggable(options = {})
|
46
|
+
acts_as_taggable_on :tags, options
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# This is an alias for calling <tt>acts_as_ordered_taggable_on :tags</tt>.
|
51
|
+
#
|
52
|
+
# Example:
|
53
|
+
# class Book < ActiveRecord::Base
|
54
|
+
# acts_as_ordered_taggable
|
55
|
+
# end
|
56
|
+
def acts_as_ordered_taggable(options = {})
|
57
|
+
acts_as_ordered_taggable_on :tags, options
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Make a model taggable on specified contexts.
|
62
|
+
#
|
63
|
+
# @param [Array] tag_types An array of taggable contexts
|
64
|
+
#
|
65
|
+
# Example:
|
66
|
+
# class User < ActiveRecord::Base
|
67
|
+
# acts_as_taggable_on :languages, :skills
|
68
|
+
# end
|
69
|
+
def acts_as_taggable_on(*tag_types)
|
70
|
+
taggable_on(*tag_types)
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Make a model taggable on specified contexts
|
75
|
+
# and preserves the order in which tags are created.
|
76
|
+
#
|
77
|
+
# An alias for acts_as_taggable_on *tag_types, preserve_tag_order: true
|
78
|
+
#
|
79
|
+
# @param [Array] tag_types An array of taggable contexts
|
80
|
+
#
|
81
|
+
# Example:
|
82
|
+
# class User < ActiveRecord::Base
|
83
|
+
# acts_as_ordered_taggable_on :languages, :skills
|
84
|
+
# end
|
85
|
+
def acts_as_ordered_taggable_on(*tag_types)
|
86
|
+
options = tag_types.extract_options!
|
87
|
+
|
88
|
+
taggable_on(*tag_types, options.merge(preserve_tag_order: true))
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# Make a model taggable on specified contexts
|
94
|
+
# and optionally preserves the order in which tags are created
|
95
|
+
#
|
96
|
+
# Separate methods used above for backwards compatibility
|
97
|
+
# so that the original acts_as_taggable_on method is unaffected
|
98
|
+
# as it's not possible to add another argument to the method
|
99
|
+
# without the tag_types being enclosed in square brackets
|
100
|
+
#
|
101
|
+
# NB: method overridden in core module in order to create tag type
|
102
|
+
# associations and methods after this logic has executed
|
103
|
+
#
|
104
|
+
def taggable_on(*tag_types)
|
105
|
+
# if we are actually defining tags on a module, add these modules to add hooks and global methods
|
106
|
+
# used by tagging. We only add them dynamically like this so that they don't bloat the model
|
107
|
+
# and add hooks/callbacks that aren't needed without tags.
|
108
|
+
[ActsAsTaggableOnMongoid::Taggable::Core,
|
109
|
+
ActsAsTaggableOnMongoid::Taggable::Changeable,
|
110
|
+
# include Collection - not sure we will need as done here. Need to think more on this one.
|
111
|
+
# include Cache - TODO: Add this.
|
112
|
+
# include Ownership - TODO: Add this.
|
113
|
+
# include Related - TODO: Add this.
|
114
|
+
ActsAsTaggableOnMongoid::Taggable::ListTags].each do |include_module|
|
115
|
+
include include_module unless included_modules.include?(include_module)
|
116
|
+
end
|
117
|
+
|
118
|
+
options = tag_types.extract_options!
|
119
|
+
tag_types.flatten!
|
120
|
+
|
121
|
+
tag_types.each do |tag_type|
|
122
|
+
next if tag_type.blank?
|
123
|
+
|
124
|
+
define_tag tag_type, options
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# rubocop:enable Metrics/BlockLength
|
130
|
+
end
|
131
|
+
end
|