acts-as-taggable-on-mongoid 6.0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.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
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "acts_as_taggable_on_mongoid/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "acts-as-taggable-on-mongoid"
|
9
|
+
spec.version = ActsAsTaggableOnMongoid::VERSION
|
10
|
+
spec.authors = ["RealNobody"]
|
11
|
+
spec.email = ["admin@cardtapp.com"]
|
12
|
+
|
13
|
+
spec.summary = "A partial mongoid implementation of tagging based on/inspired by acts-as-taggable-on."
|
14
|
+
spec.description = "A partial mongoid implementation of tagging based on/inspired by acts-as-taggable-on."
|
15
|
+
spec.homepage = "http://www.cardtapp.com"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
# # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
19
|
+
# # to allow pushing to a single host or delete this section to allow pushing to any host.
|
20
|
+
# if spec.respond_to?(:metadata)
|
21
|
+
# spec.metadata["allowed_push_host"] = "http://RubyGems.org"
|
22
|
+
# else
|
23
|
+
# raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
24
|
+
# end
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
f.match(%r{^(test|spec|features)/})
|
28
|
+
end
|
29
|
+
|
30
|
+
spec.bindir = "exe"
|
31
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
spec.add_dependency "activesupport", "~> 4.2"
|
35
|
+
spec.add_dependency "mongoid", "~> 5.2"
|
36
|
+
|
37
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
38
|
+
spec.add_development_dependency "codecov", "~> 0.1", "~> 0.1.0"
|
39
|
+
spec.add_development_dependency "cornucopia"
|
40
|
+
spec.add_development_dependency "database_cleaner"
|
41
|
+
spec.add_development_dependency "pronto"
|
42
|
+
spec.add_development_dependency "pronto-brakeman"
|
43
|
+
spec.add_development_dependency "pronto-circleci"
|
44
|
+
spec.add_development_dependency "pronto-fasterer"
|
45
|
+
spec.add_development_dependency "pronto-rails_best_practices"
|
46
|
+
spec.add_development_dependency "pronto-reek"
|
47
|
+
spec.add_development_dependency "pronto-rubocop"
|
48
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
49
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
50
|
+
spec.add_development_dependency "rspec_junit_formatter", "~> 0.3.0"
|
51
|
+
spec.add_development_dependency "rubocop"
|
52
|
+
spec.add_development_dependency "simplecov"
|
53
|
+
spec.add_development_dependency "simplecov-rcov"
|
54
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "acts-as-taggable-on-mongoid"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/codecov.yml
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "acts_as_taggable_on_mongoid/version"
|
4
|
+
|
5
|
+
# The base module for the gem under which all classes are namespaced.
|
6
|
+
module ActsAsTaggableOnMongoid
|
7
|
+
extend ::ActiveSupport::Autoload
|
8
|
+
|
9
|
+
# rubocop:disable Metrics/BlockLength
|
10
|
+
|
11
|
+
eager_autoload do
|
12
|
+
autoload :Configuration
|
13
|
+
autoload :TagList
|
14
|
+
autoload :GenericParser
|
15
|
+
autoload :DefaultParser
|
16
|
+
# autoload :TagsHelper
|
17
|
+
|
18
|
+
autoload_under "taggable/tag_type_definition" do
|
19
|
+
autoload :Attributes
|
20
|
+
autoload "Changeable"
|
21
|
+
autoload :Names
|
22
|
+
end
|
23
|
+
|
24
|
+
autoload_under "taggable/utils" do
|
25
|
+
autoload :TagListDiff
|
26
|
+
end
|
27
|
+
|
28
|
+
autoload_under :Taggable do
|
29
|
+
# autoload :Cache
|
30
|
+
# autoload :Collection
|
31
|
+
autoload :Core
|
32
|
+
autoload :Changeable
|
33
|
+
autoload :TagTypeDefinition
|
34
|
+
autoload :ListTags
|
35
|
+
# autoload :Ownership
|
36
|
+
# autoload :Related
|
37
|
+
end
|
38
|
+
|
39
|
+
autoload :Taggable
|
40
|
+
|
41
|
+
autoload_under :Models do
|
42
|
+
autoload :Tag
|
43
|
+
autoload :Tagging
|
44
|
+
# autoload :Tagger
|
45
|
+
end
|
46
|
+
|
47
|
+
autoload_under :Errors do
|
48
|
+
autoload :DuplicateTagError
|
49
|
+
end
|
50
|
+
|
51
|
+
# autoload :Utils
|
52
|
+
# autoload :Compatibility
|
53
|
+
end
|
54
|
+
|
55
|
+
# rubocop:enable Metrics/BlockLength
|
56
|
+
|
57
|
+
def self.configuration
|
58
|
+
@configuration ||= Configuration.new
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.configure
|
62
|
+
yield configuration if block_given?
|
63
|
+
end
|
64
|
+
|
65
|
+
# :reek:ManualDispatch
|
66
|
+
def self.method_missing(method_name, *args, &block)
|
67
|
+
configuration.respond_to?(method_name) ? configuration.public_send(method_name, *args, &block) : super
|
68
|
+
end
|
69
|
+
|
70
|
+
# :reek:BooleanParameter
|
71
|
+
# :reek:ManualDispatch
|
72
|
+
def self.respond_to_missing?(method_name, _include_private = false)
|
73
|
+
configuration.respond_to?(method_name) || super
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
::ActiveSupport.on_load(:mongoid) do
|
78
|
+
include ActsAsTaggableOnMongoid::Taggable
|
79
|
+
# include ActsAsTaggableOn::Tagger
|
80
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
# A configuration class for the ActsAsTaggableOnMongoid gem.
|
5
|
+
#
|
6
|
+
# These global configurations are default configuration settings for the gem and will drive individual
|
7
|
+
# tag definitions if the tag does not specify a different value when the tag is defined/added to a model.
|
8
|
+
#
|
9
|
+
# The configuration options are:
|
10
|
+
# * force_lowercase - If set, values stored into the tags and taggings table will be downcased before being saved.
|
11
|
+
# this allows for case-insensitive comparisons of values in the database.
|
12
|
+
#
|
13
|
+
# Because Mongo does not support methods on database calls like SQL does, the strict_case_match
|
14
|
+
# option from ActsAsTaggableOn is not supported. All comparisons are done case sensitive.
|
15
|
+
# * force_parameterize - If set, values stored into the tags and taggings table will be parameterized before being saved.
|
16
|
+
# * remove_unused_tags - If set when a Tagging is destroyed, the correlated Tag will also be destroyed if there are no
|
17
|
+
# other Taggings associated with that Tag.
|
18
|
+
# * default_parser - The parser class to be used to convert strings to arrays of values and to convert arrays of
|
19
|
+
# values back to strings. See GenericParser for details on creating your own parser.
|
20
|
+
# * tags_table - The model class to be used to store Tag objects.
|
21
|
+
# Custom classes can be used if you wish to store Tags in different tables for some tags if desired,
|
22
|
+
# but it is expected that the Tags table will have equivalent fields, scopes and methods as the
|
23
|
+
# ActsAsTaggableOn::Models::Tag class. In most cases, you can derive from the Tag class and simply
|
24
|
+
# override the relations or methods to achieve any differences you want/need.
|
25
|
+
# * taggings_table - The model class to be used to store Tagging objects.
|
26
|
+
# Custom classes can be used if you wish to store Taggings in different tables for some tags if desired,
|
27
|
+
# but it is expected that the Taggings table will have equivalent fields, scopes and methods as the
|
28
|
+
# ActsAsTaggableOn::Models::Tagging class. In most cases, you can derive from the Tagging class and simply
|
29
|
+
# override the relations or methods to achieve any differences you want/need.
|
30
|
+
#
|
31
|
+
# Unsupported configurations:
|
32
|
+
# * strict_case_match - This is not supported. Use the `forc_lowercase` option to achieve case insensitive data storage and
|
33
|
+
# comparisons.
|
34
|
+
# * tags_counter - This is defined in the Tags and Taggings models, and is not configured through the configuration
|
35
|
+
# nor tag defnitions.
|
36
|
+
#
|
37
|
+
# See spec tests for examples in creating your own Tag or Tagging models to customize this behavior.
|
38
|
+
|
39
|
+
# :reek:UtilityFunction
|
40
|
+
# :reek:Attribute
|
41
|
+
# :reek:TooManyInstanceVariables
|
42
|
+
class Configuration
|
43
|
+
attr_accessor :force_lowercase,
|
44
|
+
:force_parameterize,
|
45
|
+
:remove_unused_tags,
|
46
|
+
:default_parser,
|
47
|
+
:tags_table,
|
48
|
+
:taggings_table,
|
49
|
+
:preserve_tag_order
|
50
|
+
|
51
|
+
# For duck compatibility with ActsAsTaggableOn. Do not use.
|
52
|
+
def tags_counter
|
53
|
+
Mongoid.logger.warn "tags_counter is not supported."
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def strict_case_match
|
58
|
+
Mongoid.logger.warn "strict_case_match= is not supported."
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# rubocop:disable Lint/Void
|
63
|
+
|
64
|
+
def tags_counter=(_value)
|
65
|
+
Mongoid.logger.warn "tags_counter is not supported."
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def strict_case_match=(_value)
|
70
|
+
Mongoid.logger.warn "strict_case_match= is not supported."
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# rubocop:enable Lint/Void
|
75
|
+
|
76
|
+
alias force_lowercase? force_lowercase
|
77
|
+
alias force_parameterize? force_parameterize
|
78
|
+
alias preserve_tag_order? preserve_tag_order
|
79
|
+
alias remove_unused_tags? remove_unused_tags
|
80
|
+
alias strict_case_match? strict_case_match
|
81
|
+
alias tags_counter? tags_counter
|
82
|
+
|
83
|
+
def initialize
|
84
|
+
@force_lowercase = false
|
85
|
+
@force_parameterize = false
|
86
|
+
@preserve_tag_order = false
|
87
|
+
@remove_unused_tags = false
|
88
|
+
@tags_counter = true
|
89
|
+
@default_parser = ActsAsTaggableOnMongoid::DefaultParser
|
90
|
+
@tags_table = ActsAsTaggableOnMongoid::Models::Tag
|
91
|
+
@taggings_table = ActsAsTaggableOnMongoid::Models::Tagging
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
##
|
5
|
+
# Returns a new Array using the given tag string.
|
6
|
+
#
|
7
|
+
# Parsing is done based on an array of delimiters that is set at the class level. Parsing will split
|
8
|
+
# on any delimiter value that is found. By default strings are split by commas (,).
|
9
|
+
#
|
10
|
+
# To allow more complex strings, parsing will parse out quoted strings (either single or double quoted)as a block.
|
11
|
+
# (This is only partially implemented for quick not accurate/complete implementation that is
|
12
|
+
# "good enough" for most expected tags.)
|
13
|
+
#
|
14
|
+
# examples:
|
15
|
+
#
|
16
|
+
# # Delimiters
|
17
|
+
# # You can set the delimiters to a single value:
|
18
|
+
# DefaultParser.delimiter = "\\|"
|
19
|
+
#
|
20
|
+
# # You can set the delimiters to an array value:
|
21
|
+
# DefaultParser.delimiter = %w[\\| , break_here]
|
22
|
+
#
|
23
|
+
# # Parsing a string by multiple delimters
|
24
|
+
# DefaultParser.new("a|stupid,stringbreak_hereparses").parse
|
25
|
+
# # > ["a", "stupid", "string", "parses"]
|
26
|
+
#
|
27
|
+
# # Parsing works with simple quoted strings:
|
28
|
+
# DefaultParser.new("a,\"more,interesting\",string").parse
|
29
|
+
# # > ["a", "more,interesting", "string"]
|
30
|
+
class DefaultParser < GenericParser
|
31
|
+
class_attribute :delimiter
|
32
|
+
|
33
|
+
def parse
|
34
|
+
@tags = [].tap do |tag_list|
|
35
|
+
tags.each do |tag|
|
36
|
+
string = tag.to_s.dup
|
37
|
+
|
38
|
+
extract_quoted_strings(string, tag_list, double_quote_pattern)
|
39
|
+
extract_quoted_strings(string, tag_list, single_quote_pattern)
|
40
|
+
|
41
|
+
# split the string by the delimiter
|
42
|
+
# and add to the tag_list
|
43
|
+
tag_list.concat(string.split(delimiter_regex))
|
44
|
+
end
|
45
|
+
end.flatten
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
tag_list = tags.frozen? ? tags.dup : tags
|
50
|
+
|
51
|
+
join_delimiter = ActsAsTaggableOnMongoid::DefaultParser.delimiters.first
|
52
|
+
|
53
|
+
tag_list.map do |name|
|
54
|
+
name.index(escape_regex) ? "\"#{name}\"" : name
|
55
|
+
end.join(join_delimiter)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.delimiters
|
59
|
+
Array.wrap(delimiter.presence || DEFAULT_DELIMITER)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def escape_regex
|
65
|
+
@escape_regex ||= Regexp.new((Array.wrap(ActsAsTaggableOnMongoid::DefaultParser.delimiters) + %w[" ']).join("|"))
|
66
|
+
end
|
67
|
+
|
68
|
+
# :reek:UtilityFunction
|
69
|
+
def extract_quoted_strings(string, tag_list, quote_pattern)
|
70
|
+
string.gsub!(quote_pattern) do
|
71
|
+
# Append the matched tag to the tag list
|
72
|
+
tag_list << Regexp.last_match[2]
|
73
|
+
# Return the matched delimiter ($3) to replace the matched items
|
74
|
+
""
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def delimiter
|
79
|
+
# Parse the quoted tags
|
80
|
+
delimiter_list = self.class.delimiters
|
81
|
+
# Separate multiple delimiters by bitwise operator
|
82
|
+
delimiter_list = delimiter_list.join("|") if delimiter_list.is_a?(Array)
|
83
|
+
delimiter_list
|
84
|
+
end
|
85
|
+
|
86
|
+
def delimiter_regex
|
87
|
+
Regexp.new(delimiter)
|
88
|
+
end
|
89
|
+
|
90
|
+
# ( # Tag start delimiter ($1)
|
91
|
+
# \A | # Either string start or
|
92
|
+
# #{delimiter} # a delimiter
|
93
|
+
# )
|
94
|
+
# \s*" # quote (") optionally preceded by whitespace
|
95
|
+
# (.*?) # Tag ($2)
|
96
|
+
# "\s* # quote (") optionally followed by whitespace
|
97
|
+
# (?= # Tag end delimiter (not consumed; is zero-length lookahead)
|
98
|
+
# #{delimiter}\s* | # Either a delimiter optionally followed by whitespace or
|
99
|
+
# \z # string end
|
100
|
+
# )
|
101
|
+
def double_quote_pattern
|
102
|
+
/(\A|#{delimiter})\s*"(.*?)"\s*(?=#{delimiter}\s*|\z)/
|
103
|
+
end
|
104
|
+
|
105
|
+
# ( # Tag start delimiter ($1)
|
106
|
+
# \A | # Either string start or
|
107
|
+
# #{delimiter} # a delimiter
|
108
|
+
# )
|
109
|
+
# \s*' # quote (') optionally preceded by whitespace
|
110
|
+
# (.*?) # Tag ($2)
|
111
|
+
# '\s* # quote (') optionally followed by whitespace
|
112
|
+
# (?= # Tag end delimiter (not consumed; is zero-length lookahead)
|
113
|
+
# #{delimiter}\s* | delimiter_list # Either a delimiter optionally followed by whitespace or
|
114
|
+
# \z # string end
|
115
|
+
# )
|
116
|
+
def single_quote_pattern
|
117
|
+
/(\A|#{delimiter})\s*'(.*?)'\s*(?=#{delimiter}\s*|\z)/
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsTaggableOnMongoid
|
4
|
+
##
|
5
|
+
# Returns a new list of tags (array of strings) using the given tag string.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
# tag_list = ActsAsTaggableOn::GenericParser.new.parse("One , Two, Three")
|
9
|
+
# tag_list # ["One ", " Two", " Three"]
|
10
|
+
#
|
11
|
+
# All parsers are required to support two methods:
|
12
|
+
# * parse - parse the tag_list into an array of strings
|
13
|
+
# * to_s - return a parsed array of tags (may be passed in parsed) in a format that
|
14
|
+
# is suitable for parsing.
|
15
|
+
#
|
16
|
+
# NOTE: The ablitity to parse a list of tags and convert it to a string then back to
|
17
|
+
# the same list of tags is dependent on the complexity of the parser. This is
|
18
|
+
# not actually assumed to be true, though it is best if it is.
|
19
|
+
#
|
20
|
+
# Cleansing the list of tags for the tag is the responsibility of the tags TagList which knows
|
21
|
+
# if the tags need to be stripped, downcased, etc. The parser need only return an array of
|
22
|
+
# strings that are split out.
|
23
|
+
class GenericParser
|
24
|
+
attr_reader :tags
|
25
|
+
|
26
|
+
DEFAULT_DELIMITER = ","
|
27
|
+
|
28
|
+
def initialize(*tag_list)
|
29
|
+
@tags = tag_list.flatten
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse
|
33
|
+
@tags = [].tap do |tag_list|
|
34
|
+
tags.each do |tag|
|
35
|
+
tag_list.concat tag.split(DEFAULT_DELIMITER).map(&:strip).reject(&:empty?)
|
36
|
+
end
|
37
|
+
end.flatten
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
tags.join(DEFAULT_DELIMITER)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|