nano-max-tool 0.0.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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/acts-as-taggable-on-13.0.0/LICENSE.md +20 -0
  3. data/acts-as-taggable-on-13.0.0/db/migrate/1_acts_as_taggable_on_migration.rb +33 -0
  4. data/acts-as-taggable-on-13.0.0/db/migrate/2_add_missing_unique_indices.rb +23 -0
  5. data/acts-as-taggable-on-13.0.0/db/migrate/3_add_taggings_counter_cache_to_tags.rb +16 -0
  6. data/acts-as-taggable-on-13.0.0/db/migrate/4_add_missing_taggable_index.rb +12 -0
  7. data/acts-as-taggable-on-13.0.0/db/migrate/5_change_collation_for_tag_names.rb +12 -0
  8. data/acts-as-taggable-on-13.0.0/db/migrate/6_add_missing_indexes_on_taggings.rb +24 -0
  9. data/acts-as-taggable-on-13.0.0/db/migrate/7_add_tenant_to_taggings.rb +13 -0
  10. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/default_parser.rb +77 -0
  11. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/engine.rb +6 -0
  12. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/generic_parser.rb +21 -0
  13. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/tag.rb +138 -0
  14. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/tag_list.rb +103 -0
  15. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/caching.rb +46 -0
  16. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/collection.rb +220 -0
  17. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/core.rb +333 -0
  18. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/ownership.rb +146 -0
  19. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/related.rb +84 -0
  20. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tag_list_type.rb +8 -0
  21. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query/all_tags_query.rb +115 -0
  22. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query/any_tags_query.rb +74 -0
  23. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query/exclude_tags_query.rb +85 -0
  24. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query/query_base.rb +78 -0
  25. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable/tagged_with_query.rb +17 -0
  26. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/taggable.rb +119 -0
  27. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/tagger.rb +85 -0
  28. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/tagging.rb +40 -0
  29. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/tags_helper.rb +17 -0
  30. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/utils.rb +35 -0
  31. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on/version.rb +5 -0
  32. data/acts-as-taggable-on-13.0.0/lib/acts-as-taggable-on.rb +118 -0
  33. data/acts-as-taggable-on-13.0.0/lib/tasks/example/acts-as-taggable-on.rb.example +8 -0
  34. data/acts-as-taggable-on-13.0.0/lib/tasks/install_initializer.rake +23 -0
  35. data/acts-as-taggable-on-13.0.0/lib/tasks/tags_collate_utf8.rake +21 -0
  36. data/nano-max-tool.gemspec +12 -0
  37. metadata +76 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 12be2e253491b0f5f52de412784a49a4674b2bd890de68f50e9a1674bbb5e7e4
4
+ data.tar.gz: d46a31f512e27ea5bec7e95ac95333673751ef71b491b1fda3d66176413eaec6
5
+ SHA512:
6
+ metadata.gz: 5b50a2916ee3034e5dc3ae9430c2ee4c1af07620be90475c1d8c8d1e8b18a8413c57c6718b6e7b7e90434477f6f51ba816a6f766d57d827e9325dc8bb34ae46a
7
+ data.tar.gz: f9a2dd2a81b54695b0591c7c1d960b642d982014f2117705eba4c4e67ae4dd307749deaaf8875f7337cb79b5cbbcde7ec48c99e66bd570a461ad88d0a84e83d4
@@ -0,0 +1,20 @@
1
+ __Copyright (c) 2007 Michael Bleigh and Intridea Inc.__
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ActsAsTaggableOnMigration < ActiveRecord::Migration[6.0]
4
+ def self.up
5
+ create_table ActsAsTaggableOn.tags_table do |t|
6
+ t.string :name
7
+ t.timestamps
8
+ end
9
+
10
+ create_table ActsAsTaggableOn.taggings_table do |t|
11
+ t.references :tag, foreign_key: { to_table: ActsAsTaggableOn.tags_table }
12
+
13
+ # You should make sure that the column created is
14
+ # long enough to store the required class names.
15
+ t.references :taggable, polymorphic: true
16
+ t.references :tagger, polymorphic: true
17
+
18
+ # Limit is created to prevent MySQL error on index
19
+ # length for MyISAM table type: http://bit.ly/vgW2Ql
20
+ t.string :context, limit: 128
21
+
22
+ t.datetime :created_at
23
+ end
24
+
25
+ add_index ActsAsTaggableOn.taggings_table, %i[taggable_id taggable_type context],
26
+ name: 'taggings_taggable_context_idx'
27
+ end
28
+
29
+ def self.down
30
+ drop_table ActsAsTaggableOn.taggings_table
31
+ drop_table ActsAsTaggableOn.tags_table
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddMissingUniqueIndices < ActiveRecord::Migration[6.0]
4
+ def self.up
5
+ add_index ActsAsTaggableOn.tags_table, :name, unique: true
6
+
7
+ remove_index ActsAsTaggableOn.taggings_table, :tag_id if index_exists?(ActsAsTaggableOn.taggings_table, :tag_id)
8
+ remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx'
9
+ add_index ActsAsTaggableOn.taggings_table,
10
+ %i[tag_id taggable_id taggable_type context tagger_id tagger_type],
11
+ unique: true, name: 'taggings_idx'
12
+ end
13
+
14
+ def self.down
15
+ remove_index ActsAsTaggableOn.tags_table, :name
16
+
17
+ remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_idx'
18
+
19
+ add_index ActsAsTaggableOn.taggings_table, :tag_id unless index_exists?(ActsAsTaggableOn.taggings_table, :tag_id)
20
+ add_index ActsAsTaggableOn.taggings_table, %i[taggable_id taggable_type context],
21
+ name: 'taggings_taggable_context_idx'
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddTaggingsCounterCacheToTags < ActiveRecord::Migration[6.0]
4
+ def self.up
5
+ add_column ActsAsTaggableOn.tags_table, :taggings_count, :integer, default: 0
6
+
7
+ ActsAsTaggableOn::Tag.reset_column_information
8
+ ActsAsTaggableOn::Tag.find_each do |tag|
9
+ ActsAsTaggableOn::Tag.reset_counters(tag.id, ActsAsTaggableOn.taggings_table)
10
+ end
11
+ end
12
+
13
+ def self.down
14
+ remove_column ActsAsTaggableOn.tags_table, :taggings_count
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddMissingTaggableIndex < ActiveRecord::Migration[6.0]
4
+ def self.up
5
+ add_index ActsAsTaggableOn.taggings_table, %i[taggable_id taggable_type context],
6
+ name: 'taggings_taggable_context_idx'
7
+ end
8
+
9
+ def self.down
10
+ remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx'
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This migration is added to circumvent issue #623 and have special characters
4
+ # work properly
5
+
6
+ class ChangeCollationForTagNames < ActiveRecord::Migration[6.0]
7
+ def up
8
+ if ActsAsTaggableOn::Utils.using_mysql?
9
+ execute("ALTER TABLE #{ActsAsTaggableOn.tags_table} MODIFY name varchar(255) CHARACTER SET utf8 COLLATE utf8_bin;")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddMissingIndexesOnTaggings < ActiveRecord::Migration[6.0]
4
+ def change
5
+ add_index ActsAsTaggableOn.taggings_table, :tag_id unless index_exists? ActsAsTaggableOn.taggings_table, :tag_id
6
+ add_index ActsAsTaggableOn.taggings_table, :taggable_id unless index_exists? ActsAsTaggableOn.taggings_table,
7
+ :taggable_id
8
+ add_index ActsAsTaggableOn.taggings_table, :taggable_type unless index_exists? ActsAsTaggableOn.taggings_table,
9
+ :taggable_type
10
+ add_index ActsAsTaggableOn.taggings_table, :tagger_id unless index_exists? ActsAsTaggableOn.taggings_table,
11
+ :tagger_id
12
+ add_index ActsAsTaggableOn.taggings_table, :context unless index_exists? ActsAsTaggableOn.taggings_table, :context
13
+
14
+ unless index_exists? ActsAsTaggableOn.taggings_table, %i[tagger_id tagger_type]
15
+ add_index ActsAsTaggableOn.taggings_table, %i[tagger_id tagger_type]
16
+ end
17
+
18
+ unless index_exists? ActsAsTaggableOn.taggings_table, %i[taggable_id taggable_type tagger_id context],
19
+ name: 'taggings_idy'
20
+ add_index ActsAsTaggableOn.taggings_table, %i[taggable_id taggable_type tagger_id context],
21
+ name: 'taggings_idy'
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddTenantToTaggings < ActiveRecord::Migration[6.0]
4
+ def self.up
5
+ add_column ActsAsTaggableOn.taggings_table, :tenant, :string, limit: 128
6
+ add_index ActsAsTaggableOn.taggings_table, :tenant unless index_exists? ActsAsTaggableOn.taggings_table, :tenant
7
+ end
8
+
9
+ def self.down
10
+ remove_index ActsAsTaggableOn.taggings_table, :tenant
11
+ remove_column ActsAsTaggableOn.taggings_table, :tenant
12
+ end
13
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActsAsTaggableOn
4
+ ##
5
+ # Returns a new TagList using the given tag string.
6
+ #
7
+ # Example:
8
+ # tag_list = ActsAsTaggableOn::DefaultParser.parse("One , Two, Three")
9
+ # tag_list # ["One", "Two", "Three"]
10
+ class DefaultParser < GenericParser
11
+ def parse
12
+ string = @tag_list
13
+
14
+ string = string.join(ActsAsTaggableOn.glue) if string.respond_to?(:join)
15
+ TagList.new.tap do |tag_list|
16
+ string = string.to_s.dup
17
+
18
+ string.gsub!(double_quote_pattern) do
19
+ # Append the matched tag to the tag list
20
+ tag_list << Regexp.last_match[2]
21
+ # Return the matched delimiter ($3) to replace the matched items
22
+ ''
23
+ end
24
+
25
+ string.gsub!(single_quote_pattern) do
26
+ # Append the matched tag ($2) to the tag list
27
+ tag_list << Regexp.last_match[2]
28
+ # Return an empty string to replace the matched items
29
+ ''
30
+ end
31
+
32
+ # split the string by the delimiter
33
+ # and add to the tag_list
34
+ tag_list.add(string.split(Regexp.new(delimiter)))
35
+ end
36
+ end
37
+
38
+ # private
39
+ def delimiter
40
+ # Parse the quoted tags
41
+ d = ActsAsTaggableOn.delimiter
42
+ # Separate multiple delimiters by bitwise operator
43
+ d = d.join('|') if d.is_a?(Array)
44
+ d
45
+ end
46
+
47
+ # ( # Tag start delimiter ($1)
48
+ # \A | # Either string start or
49
+ # #{delimiter} # a delimiter
50
+ # )
51
+ # \s*" # quote (") optionally preceded by whitespace
52
+ # (.*?) # Tag ($2)
53
+ # "\s* # quote (") optionally followed by whitespace
54
+ # (?= # Tag end delimiter (not consumed; is zero-length lookahead)
55
+ # #{delimiter}\s* | # Either a delimiter optionally followed by whitespace or
56
+ # \z # string end
57
+ # )
58
+ def double_quote_pattern
59
+ /(\A|#{delimiter})\s*"(.*?)"\s*(?=#{delimiter}\s*|\z)/
60
+ end
61
+
62
+ # ( # Tag start delimiter ($1)
63
+ # \A | # Either string start or
64
+ # #{delimiter} # a delimiter
65
+ # )
66
+ # \s*' # quote (') optionally preceded by whitespace
67
+ # (.*?) # Tag ($2)
68
+ # '\s* # quote (') optionally followed by whitespace
69
+ # (?= # Tag end delimiter (not consumed; is zero-length lookahead)
70
+ # #{delimiter}\s* | d # Either a delimiter optionally followed by whitespace or
71
+ # \z # string end
72
+ # )
73
+ def single_quote_pattern
74
+ /(\A|#{delimiter})\s*'(.*?)'\s*(?=#{delimiter}\s*|\z)/
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActsAsTaggableOn
4
+ class Engine < Rails::Engine
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActsAsTaggableOn
4
+ ##
5
+ # Returns a new TagList 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
+ class GenericParser
11
+ def initialize(tag_list)
12
+ @tag_list = tag_list
13
+ end
14
+
15
+ def parse
16
+ TagList.new.tap do |tag_list|
17
+ tag_list.add @tag_list.split(',').map(&:strip).reject(&:empty?)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActsAsTaggableOn
4
+ class Tag < ActsAsTaggableOn.base_class.constantize
5
+ self.table_name = ActsAsTaggableOn.tags_table
6
+
7
+ ### ASSOCIATIONS:
8
+
9
+ has_many :taggings, dependent: :destroy, class_name: '::ActsAsTaggableOn::Tagging'
10
+
11
+ ### VALIDATIONS:
12
+
13
+ validates_presence_of :name
14
+ validates_uniqueness_of :name, if: :validates_name_uniqueness?, case_sensitive: true
15
+ validates_length_of :name, maximum: 255
16
+
17
+ # monkey patch this method if don't need name uniqueness validation
18
+ def validates_name_uniqueness?
19
+ true
20
+ end
21
+
22
+ ### SCOPES:
23
+ scope :most_used, ->(limit = 20) { order('taggings_count desc').limit(limit) }
24
+ scope :least_used, ->(limit = 20) { order('taggings_count asc').limit(limit) }
25
+
26
+ def self.named(name)
27
+ if ActsAsTaggableOn.strict_case_match
28
+ where(["name = #{binary}?", name.to_s])
29
+ else
30
+ where(['LOWER(name) = LOWER(?)', name.to_s.downcase])
31
+ end
32
+ end
33
+
34
+ def self.named_any(list)
35
+ clause = list.map do |tag|
36
+ sanitize_sql_for_named_any(tag)
37
+ end.join(' OR ')
38
+ where(clause)
39
+ end
40
+
41
+ def self.named_like(name)
42
+ clause = ["name #{ActsAsTaggableOn::Utils.like_operator} ? ESCAPE '!'",
43
+ "%#{ActsAsTaggableOn::Utils.escape_like(name)}%"]
44
+ where(clause)
45
+ end
46
+
47
+ def self.named_like_any(list)
48
+ clause = list.map do |tag|
49
+ sanitize_sql(["name #{ActsAsTaggableOn::Utils.like_operator} ? ESCAPE '!'",
50
+ "%#{ActsAsTaggableOn::Utils.escape_like(tag.to_s)}%"])
51
+ end.join(' OR ')
52
+ where(clause)
53
+ end
54
+
55
+ def self.for_context(context)
56
+ joins(:taggings)
57
+ .where(["#{ActsAsTaggableOn.taggings_table}.context = ?", context])
58
+ .select("DISTINCT #{ActsAsTaggableOn.tags_table}.*")
59
+ end
60
+
61
+ def self.for_tenant(tenant)
62
+ joins(:taggings)
63
+ .where("#{ActsAsTaggableOn.taggings_table}.tenant = ?", tenant.to_s)
64
+ .select("DISTINCT #{ActsAsTaggableOn.tags_table}.*")
65
+ end
66
+
67
+ ### CLASS METHODS:
68
+
69
+ def self.find_or_create_with_like_by_name(name)
70
+ if ActsAsTaggableOn.strict_case_match
71
+ find_or_create_all_with_like_by_name([name]).first
72
+ else
73
+ named_like(name).first || create(name: name)
74
+ end
75
+ end
76
+
77
+ def self.find_or_create_all_with_like_by_name(*list)
78
+ list = Array(list).flatten
79
+
80
+ return [] if list.empty?
81
+
82
+ existing_tags = named_any(list)
83
+ list.map do |tag_name|
84
+ tries ||= 3
85
+ comparable_tag_name = comparable_name(tag_name)
86
+ existing_tag = existing_tags.find { |tag| comparable_name(tag.name) == comparable_tag_name }
87
+ next existing_tag if existing_tag
88
+
89
+ transaction(requires_new: true) { create(name: tag_name) }
90
+ rescue ActiveRecord::RecordNotUnique
91
+ if (tries -= 1).positive?
92
+ existing_tags = named_any(list)
93
+ retry
94
+ end
95
+
96
+ raise DuplicateTagError, "'#{tag_name}' has already been taken"
97
+ end
98
+ end
99
+
100
+ ### INSTANCE METHODS:
101
+
102
+ def ==(other)
103
+ super || (other.is_a?(Tag) && name == other.name)
104
+ end
105
+
106
+ def to_s
107
+ name
108
+ end
109
+
110
+ def count
111
+ read_attribute(:count).to_i
112
+ end
113
+
114
+ class << self
115
+ private
116
+
117
+ def comparable_name(str)
118
+ if ActsAsTaggableOn.strict_case_match
119
+ str
120
+ else
121
+ str.to_s.downcase
122
+ end
123
+ end
124
+
125
+ def binary
126
+ ActsAsTaggableOn::Utils.using_mysql? ? 'BINARY ' : nil
127
+ end
128
+
129
+ def sanitize_sql_for_named_any(tag)
130
+ if ActsAsTaggableOn.strict_case_match
131
+ sanitize_sql(["name = #{binary}?", tag.to_s])
132
+ else
133
+ sanitize_sql(['LOWER(name) = LOWER(?)', tag.to_s.downcase])
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/module/delegation'
4
+
5
+ module ActsAsTaggableOn
6
+ class TagList < Array
7
+ attr_accessor :owner, :parser
8
+
9
+ def initialize(*args)
10
+ @parser = ActsAsTaggableOn.default_parser
11
+ add(*args)
12
+ end
13
+
14
+ ##
15
+ # Add tags to the tag_list. Duplicate or blank tags will be ignored.
16
+ # Use the <tt>:parse</tt> option to add an unparsed tag string.
17
+ #
18
+ # Example:
19
+ # tag_list.add("Fun", "Happy")
20
+ # tag_list.add("Fun, Happy", :parse => true)
21
+ def add(*names)
22
+ extract_and_apply_options!(names)
23
+ concat(names)
24
+ clean!
25
+ self
26
+ end
27
+
28
+ # Append---Add the tag to the tag_list. This
29
+ # expression returns the tag_list itself, so several appends
30
+ # may be chained together.
31
+ def <<(obj)
32
+ add(obj)
33
+ end
34
+
35
+ # Concatenation --- Returns a new tag list built by concatenating the
36
+ # two tag lists together to produce a third tag list.
37
+ def +(other)
38
+ TagList.new.add(self).add(other)
39
+ end
40
+
41
+ # Appends the elements of +other_tag_list+ to +self+.
42
+ def concat(other_tag_list)
43
+ super(other_tag_list).send(:clean!)
44
+ self
45
+ end
46
+
47
+ ##
48
+ # Remove specific tags from the tag_list.
49
+ # Use the <tt>:parse</tt> option to add an unparsed tag string.
50
+ #
51
+ # Example:
52
+ # tag_list.remove("Sad", "Lonely")
53
+ # tag_list.remove("Sad, Lonely", :parse => true)
54
+ def remove(*names)
55
+ extract_and_apply_options!(names)
56
+ delete_if { |name| names.include?(name) }
57
+ self
58
+ end
59
+
60
+ ##
61
+ # Transform the tag_list into a tag string suitable for editing in a form.
62
+ # The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
63
+ #
64
+ # Example:
65
+ # tag_list = TagList.new("Round", "Square,Cube")
66
+ # tag_list.to_s # 'Round, "Square,Cube"'
67
+ def to_s
68
+ tags = frozen? ? dup : self
69
+ tags.send(:clean!)
70
+
71
+ tags.map do |name|
72
+ d = ActsAsTaggableOn.delimiter
73
+ d = Regexp.new d.join('|') if d.is_a? Array
74
+ name.index(d) ? "\"#{name}\"" : name
75
+ end.join(ActsAsTaggableOn.glue)
76
+ end
77
+
78
+ private
79
+
80
+ # Convert everything to string, remove whitespace, duplicates, and blanks.
81
+ def clean!
82
+ reject!(&:blank?)
83
+ map!(&:to_s)
84
+ map!(&:strip)
85
+ map! { |tag| tag.to_s.downcase } if ActsAsTaggableOn.force_lowercase
86
+ map!(&:parameterize) if ActsAsTaggableOn.force_parameterize
87
+
88
+ ActsAsTaggableOn.strict_case_match ? uniq! : uniq!(&:downcase)
89
+ self
90
+ end
91
+
92
+ def extract_and_apply_options!(args)
93
+ options = args.last.is_a?(Hash) ? args.pop : {}
94
+ options.assert_valid_keys :parse, :parser
95
+
96
+ parser = options[:parser] || @parser
97
+
98
+ args.map! { |a| parser.new(a).parse } if options[:parse] || options[:parser]
99
+
100
+ args.flatten!
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActsAsTaggableOn
4
+ module Taggable
5
+ module Caching
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ initialize_tags_cache
10
+ before_save :save_cached_tag_list
11
+ end
12
+
13
+ class_methods do
14
+ def initialize_tags_cache
15
+ tag_types.map(&:to_s).each do |tag_type|
16
+ define_singleton_method("caching_#{tag_type.singularize}_list?") do
17
+ caching_tag_list_on?(tag_type)
18
+ end
19
+ end
20
+ end
21
+
22
+ def acts_as_taggable_on(*args)
23
+ super(*args)
24
+ initialize_tags_cache
25
+ end
26
+
27
+ def caching_tag_list_on?(context)
28
+ column_names.include?("cached_#{context.to_s.singularize}_list")
29
+ end
30
+ end
31
+
32
+ def save_cached_tag_list
33
+ tag_types.map(&:to_s).each do |tag_type|
34
+ next unless self.class.respond_to?("caching_#{tag_type.singularize}_list?")
35
+ if self.class.send("caching_#{tag_type.singularize}_list?") && tag_list_cache_set_on(tag_type)
36
+ list = tag_list_cache_on(tag_type).to_a.flatten.compact.join("#{ActsAsTaggableOn.delimiter} ")
37
+ self["cached_#{tag_type.singularize}_list"] = list
38
+ end
39
+ end
40
+
41
+ true
42
+ end
43
+ end
44
+ end
45
+ end
46
+