acts-as-taggable-on 2.0.0 → 3.2.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.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.travis.yml +23 -0
  4. data/Appraisals +20 -0
  5. data/CHANGELOG.md +112 -0
  6. data/CONTRIBUTING.md +44 -0
  7. data/Gemfile +9 -5
  8. data/Guardfile +5 -0
  9. data/{MIT-LICENSE → LICENSE.md} +1 -1
  10. data/README.md +385 -0
  11. data/Rakefile +13 -53
  12. data/UPGRADING.md +13 -0
  13. data/acts-as-taggable-on.gemspec +38 -0
  14. data/{lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb → db/migrate/1_acts_as_taggable_on_migration.rb} +5 -3
  15. data/db/migrate/2_add_missing_unique_indices.rb +19 -0
  16. data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +14 -0
  17. data/gemfiles/activerecord_3.2.gemfile +15 -0
  18. data/gemfiles/activerecord_4.0.gemfile +15 -0
  19. data/gemfiles/activerecord_4.1.gemfile +15 -0
  20. data/gemfiles/activerecord_edge.gemfile +16 -0
  21. data/lib/acts-as-taggable-on.rb +65 -22
  22. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +53 -24
  23. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +120 -42
  24. data/lib/acts_as_taggable_on/acts_as_taggable_on/compatibility.rb +35 -0
  25. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +309 -124
  26. data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +36 -0
  27. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +96 -72
  28. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +43 -36
  29. data/lib/acts_as_taggable_on/engine.rb +5 -0
  30. data/lib/acts_as_taggable_on/tag.rb +133 -63
  31. data/lib/acts_as_taggable_on/tag_list.rb +162 -81
  32. data/lib/acts_as_taggable_on/taggable.rb +101 -0
  33. data/lib/acts_as_taggable_on/{acts_as_tagger.rb → tagger.rb} +34 -13
  34. data/lib/acts_as_taggable_on/tagging.rb +28 -18
  35. data/lib/acts_as_taggable_on/tags_helper.rb +12 -14
  36. data/lib/acts_as_taggable_on/utils.rb +62 -0
  37. data/lib/acts_as_taggable_on/version.rb +4 -0
  38. data/lib/acts_as_taggable_on.rb +6 -0
  39. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +189 -195
  40. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +78 -81
  41. data/spec/acts_as_taggable_on/caching_spec.rb +76 -0
  42. data/spec/acts_as_taggable_on/related_spec.rb +89 -0
  43. data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +210 -0
  44. data/spec/acts_as_taggable_on/tag_list_spec.rb +149 -61
  45. data/spec/acts_as_taggable_on/tag_spec.rb +245 -65
  46. data/spec/acts_as_taggable_on/taggable_spec.rb +776 -187
  47. data/spec/acts_as_taggable_on/tagger_spec.rb +119 -70
  48. data/spec/acts_as_taggable_on/tagging_spec.rb +17 -21
  49. data/spec/acts_as_taggable_on/tags_helper_spec.rb +38 -22
  50. data/spec/acts_as_taggable_on/utils_spec.rb +15 -0
  51. data/spec/internal/app/models/altered_inheriting_taggable_model.rb +3 -0
  52. data/spec/internal/app/models/cached_model.rb +3 -0
  53. data/spec/internal/app/models/cached_model_with_array.rb +5 -0
  54. data/spec/internal/app/models/company.rb +15 -0
  55. data/spec/internal/app/models/inheriting_taggable_model.rb +2 -0
  56. data/spec/internal/app/models/market.rb +2 -0
  57. data/spec/internal/app/models/models.rb +90 -0
  58. data/spec/internal/app/models/non_standard_id_taggable_model.rb +8 -0
  59. data/spec/internal/app/models/ordered_taggable_model.rb +4 -0
  60. data/spec/internal/app/models/other_cached_model.rb +3 -0
  61. data/spec/internal/app/models/other_taggable_model.rb +4 -0
  62. data/spec/internal/app/models/student.rb +2 -0
  63. data/spec/internal/app/models/taggable_model.rb +13 -0
  64. data/spec/internal/app/models/untaggable_model.rb +3 -0
  65. data/spec/internal/app/models/user.rb +3 -0
  66. data/spec/internal/config/database.yml.sample +19 -0
  67. data/spec/internal/db/schema.rb +97 -0
  68. data/spec/schema.rb +57 -18
  69. data/spec/spec_helper.rb +12 -47
  70. data/spec/support/array.rb +9 -0
  71. data/spec/support/database.rb +42 -0
  72. data/spec/support/database_cleaner.rb +21 -0
  73. metadata +286 -63
  74. data/CHANGELOG +0 -25
  75. data/README.rdoc +0 -221
  76. data/VERSION +0 -1
  77. data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +0 -7
  78. data/generators/acts_as_taggable_on_migration/templates/migration.rb +0 -29
  79. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +0 -53
  80. data/lib/acts_as_taggable_on/compatibility/Gemfile +0 -6
  81. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +0 -17
  82. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +0 -31
  83. data/rails/init.rb +0 -1
  84. data/spec/bm.rb +0 -52
  85. data/spec/models.rb +0 -30
  86. /data/{spec/spec.opts → .rspec} +0 -0
@@ -1,95 +1,176 @@
1
- class TagList < Array
1
+ require 'active_support/core_ext/module/delegation'
2
2
 
3
- cattr_accessor :delimiter
4
- self.delimiter = ','
3
+ module ActsAsTaggableOn
4
+ class TagList < Array
5
+ attr_accessor :owner
5
6
 
6
- attr_accessor :owner
7
+ def initialize(*args)
8
+ add(*args)
9
+ end
10
+
11
+ class << self
12
+ ##
13
+ # Returns a new TagList using the given tag string.
14
+ #
15
+ # Example:
16
+ # tag_list = ActsAsTaggableOn::TagList.from("One , Two, Three")
17
+ # tag_list # ["One", "Two", "Three"]
18
+ def from(string)
19
+ string = string.join(ActsAsTaggableOn.glue) if string.respond_to?(:join)
20
+
21
+ new.tap do |tag_list|
22
+ string = string.to_s.dup
23
+
24
+
25
+ string.gsub!(double_quote_pattern) {
26
+ # Append the matched tag to the tag list
27
+ tag_list << Regexp.last_match[2]
28
+ # Return the matched delimiter ($3) to replace the matched items
29
+ ''
30
+ }
31
+
32
+ string.gsub!(single_quote_pattern) {
33
+ # Append the matched tag ($2) to the tag list
34
+ tag_list << Regexp.last_match[2]
35
+ # Return an empty string to replace the matched items
36
+ ''
37
+ }
38
+
39
+ # split the string by the delimiter
40
+ # and add to the tag_list
41
+ tag_list.add(string.split(Regexp.new delimiter))
42
+ end
43
+ end
44
+
45
+ def delimiter
46
+ # Parse the quoted tags
47
+ d = ActsAsTaggableOn.delimiter
48
+ # Separate multiple delimiters by bitwise operator
49
+ d = d.join('|') if d.kind_of?(Array)
50
+
51
+ d
52
+ end
53
+
54
+ def single_quote_pattern
55
+ %r{
56
+ ( # Tag start delimiter ($1)
57
+ \A | # Either string start or
58
+ #{delimiter} # a delimiter
59
+ )
60
+ \s*' # quote (') optionally preceded by whitespace
61
+ (.*?) # Tag ($2)
62
+ '\s* # quote (') optionally followed by whitespace
63
+ (?= # Tag end delimiter (not consumed; is zero-length lookahead)
64
+ #{delimiter}\s* | # Either a delimiter optionally followed by whitespace or
65
+ \z # string end
66
+ )
67
+ }x
68
+ end
69
+
70
+ def double_quote_pattern
71
+ %r{
72
+ ( # Tag start delimiter ($1)
73
+ \A | # Either string start or
74
+ #{delimiter} # a delimiter
75
+ )
76
+ \s*" # quote (") optionally preceded by whitespace
77
+ (.*?) # Tag ($2)
78
+ "\s* # quote (") optionally followed by whitespace
79
+ (?= # Tag end delimiter (not consumed; is zero-length lookahead)
80
+ #{delimiter}\s* | # Either a delimiter optionally followed by whitespace or
81
+ \z # string end
82
+ )
83
+ }x
84
+ end
7
85
 
8
- def initialize(*args)
9
- add(*args)
10
- end
11
-
12
- ##
13
- # Returns a new TagList using the given tag string.
14
- #
15
- # Example:
16
- # tag_list = TagList.from("One , Two, Three")
17
- # tag_list # ["One", "Two", "Three"]
18
- def self.from(string)
19
- string = string.join(", ") if string.respond_to?(:join)
20
-
21
- new.tap do |tag_list|
22
- string = string.to_s.dup
23
-
24
- # Parse the quoted tags
25
- string.gsub!(/(\A|#{delimiter})\s*"(.*?)"\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
26
- string.gsub!(/(\A|#{delimiter})\s*'(.*?)'\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
27
-
28
- tag_list.add(string.split(delimiter))
29
86
  end
30
- end
87
+ ##
88
+ # Add tags to the tag_list. Duplicate or blank tags will be ignored.
89
+ # Use the <tt>:parse</tt> option to add an unparsed tag string.
90
+ #
91
+ # Example:
92
+ # tag_list.add("Fun", "Happy")
93
+ # tag_list.add("Fun, Happy", :parse => true)
94
+ def add(*names)
95
+ extract_and_apply_options!(names)
96
+ concat(names)
97
+ clean!
98
+ self
99
+ end
31
100
 
32
- ##
33
- # Add tags to the tag_list. Duplicate or blank tags will be ignored.
34
- # Use the <tt>:parse</tt> option to add an unparsed tag string.
35
- #
36
- # Example:
37
- # tag_list.add("Fun", "Happy")
38
- # tag_list.add("Fun, Happy", :parse => true)
39
- def add(*names)
40
- extract_and_apply_options!(names)
41
- concat(names)
42
- clean!
43
- self
44
- end
101
+ # Append---Add the tag to the tag_list. This
102
+ # expression returns the tag_list itself, so several appends
103
+ # may be chained together.
104
+ def <<(obj)
105
+ add(obj)
106
+ end
45
107
 
46
- ##
47
- # Remove specific tags from the tag_list.
48
- # Use the <tt>:parse</tt> option to add an unparsed tag string.
49
- #
50
- # Example:
51
- # tag_list.remove("Sad", "Lonely")
52
- # tag_list.remove("Sad, Lonely", :parse => true)
53
- def remove(*names)
54
- extract_and_apply_options!(names)
55
- delete_if { |name| names.include?(name) }
56
- self
57
- end
108
+ # Concatenation --- Returns a new tag list built by concatenating the
109
+ # two tag lists together to produce a third tag list.
110
+ def +(other_tag_list)
111
+ TagList.new.add(self).add(other_tag_list)
112
+ end
58
113
 
59
- ##
60
- # Transform the tag_list into a tag string suitable for edting in a form.
61
- # The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
62
- #
63
- # Example:
64
- # tag_list = TagList.new("Round", "Square,Cube")
65
- # tag_list.to_s # 'Round, "Square,Cube"'
66
- def to_s
67
- tags = frozen? ? self.dup : self
68
- tags.send(:clean!)
69
-
70
- tags.map do |name|
71
- name.include?(delimiter) ? "\"#{name}\"" : name
72
- end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
73
- end
114
+ # Appends the elements of +other_tag_list+ to +self+.
115
+ def concat(other_tag_list)
116
+ super(other_tag_list).send(:clean!)
117
+ end
74
118
 
75
- private
76
-
77
- # Remove whitespace, duplicates, and blanks.
78
- def clean!
79
- reject!(&:blank?)
80
- map!(&:strip)
81
- uniq!
82
- end
119
+ ##
120
+ # Remove specific tags from the tag_list.
121
+ # Use the <tt>:parse</tt> option to add an unparsed tag string.
122
+ #
123
+ # Example:
124
+ # tag_list.remove("Sad", "Lonely")
125
+ # tag_list.remove("Sad, Lonely", :parse => true)
126
+ def remove(*names)
127
+ extract_and_apply_options!(names)
128
+ delete_if { |name| names.include?(name) }
129
+ self
130
+ end
83
131
 
84
- def extract_and_apply_options!(args)
85
- options = args.last.is_a?(Hash) ? args.pop : {}
86
- options.assert_valid_keys :parse
132
+ ##
133
+ # Transform the tag_list into a tag string suitable for editing in a form.
134
+ # The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
135
+ #
136
+ # Example:
137
+ # tag_list = TagList.new("Round", "Square,Cube")
138
+ # tag_list.to_s # 'Round, "Square,Cube"'
139
+ def to_s
140
+ tags = frozen? ? self.dup : self
141
+ tags.send(:clean!)
87
142
 
88
- if options[:parse]
89
- args.map! { |a| self.class.from(a) }
143
+ tags.map do |name|
144
+ d = ActsAsTaggableOn.delimiter
145
+ d = Regexp.new d.join('|') if d.kind_of? Array
146
+ name.index(d) ? "\"#{name}\"" : name
147
+ end.join(ActsAsTaggableOn.glue)
90
148
  end
91
149
 
92
- args.flatten!
93
- end
150
+ private
151
+
152
+ # Convert everything to string, remove whitespace, duplicates, and blanks.
153
+ def clean!
154
+ reject!(&:blank?)
155
+ map!(&:to_s)
156
+ map!(&:strip)
157
+ map! { |tag| tag.mb_chars.downcase.to_s } if ActsAsTaggableOn.force_lowercase
158
+ map!(&:parameterize) if ActsAsTaggableOn.force_parameterize
159
+
160
+ uniq!
161
+ end
94
162
 
163
+
164
+ def extract_and_apply_options!(args)
165
+ options = args.last.is_a?(Hash) ? args.pop : {}
166
+ options.assert_valid_keys :parse
167
+
168
+ args.map! { |a| self.class.from(a) } if options[:parse]
169
+
170
+ args.flatten!
171
+ end
172
+
173
+
174
+ end
95
175
  end
176
+
@@ -0,0 +1,101 @@
1
+ module ActsAsTaggableOn
2
+ module Taggable
3
+ def taggable?
4
+ false
5
+ end
6
+
7
+ ##
8
+ # This is an alias for calling <tt>acts_as_taggable_on :tags</tt>.
9
+ #
10
+ # Example:
11
+ # class Book < ActiveRecord::Base
12
+ # acts_as_taggable
13
+ # end
14
+ def acts_as_taggable
15
+ acts_as_taggable_on :tags
16
+ end
17
+
18
+ ##
19
+ # This is an alias for calling <tt>acts_as_ordered_taggable_on :tags</tt>.
20
+ #
21
+ # Example:
22
+ # class Book < ActiveRecord::Base
23
+ # acts_as_ordered_taggable
24
+ # end
25
+ def acts_as_ordered_taggable
26
+ acts_as_ordered_taggable_on :tags
27
+ end
28
+
29
+ ##
30
+ # Make a model taggable on specified contexts.
31
+ #
32
+ # @param [Array] tag_types An array of taggable contexts
33
+ #
34
+ # Example:
35
+ # class User < ActiveRecord::Base
36
+ # acts_as_taggable_on :languages, :skills
37
+ # end
38
+ def acts_as_taggable_on(*tag_types)
39
+ taggable_on(false, tag_types)
40
+ end
41
+
42
+ ##
43
+ # Make a model taggable on specified contexts
44
+ # and preserves the order in which tags are created
45
+ #
46
+ # @param [Array] tag_types An array of taggable contexts
47
+ #
48
+ # Example:
49
+ # class User < ActiveRecord::Base
50
+ # acts_as_ordered_taggable_on :languages, :skills
51
+ # end
52
+ def acts_as_ordered_taggable_on(*tag_types)
53
+ taggable_on(true, tag_types)
54
+ end
55
+
56
+ private
57
+
58
+ # Make a model taggable on specified contexts
59
+ # and optionally preserves the order in which tags are created
60
+ #
61
+ # Separate methods used above for backwards compatibility
62
+ # so that the original acts_as_taggable_on method is unaffected
63
+ # as it's not possible to add another argument to the method
64
+ # without the tag_types being enclosed in square brackets
65
+ #
66
+ # NB: method overridden in core module in order to create tag type
67
+ # associations and methods after this logic has executed
68
+ #
69
+ def taggable_on(preserve_tag_order, *tag_types)
70
+ tag_types = tag_types.to_a.flatten.compact.map(&:to_sym)
71
+
72
+ if taggable?
73
+ self.tag_types = (self.tag_types + tag_types).uniq
74
+ self.preserve_tag_order = preserve_tag_order
75
+ else
76
+ class_attribute :tag_types
77
+ self.tag_types = tag_types
78
+ class_attribute :preserve_tag_order
79
+ self.preserve_tag_order = preserve_tag_order
80
+
81
+ class_eval do
82
+ has_many :taggings, as: :taggable, dependent: :destroy, class_name: 'ActsAsTaggableOn::Tagging'
83
+ has_many :base_tags, through: :taggings, source: :tag, class_name: 'ActsAsTaggableOn::Tag'
84
+
85
+ def self.taggable?
86
+ true
87
+ end
88
+ end
89
+ end
90
+
91
+ # each of these add context-specific methods and must be
92
+ # called on each call of taggable_on
93
+ include ActsAsTaggableOn::Taggable::Core
94
+ include ActsAsTaggableOn::Taggable::Collection
95
+ include ActsAsTaggableOn::Taggable::Cache
96
+ include ActsAsTaggableOn::Taggable::Ownership
97
+ include ActsAsTaggableOn::Taggable::Related
98
+ include ActsAsTaggableOn::Taggable::Dirty
99
+ end
100
+ end
101
+ end
@@ -15,23 +15,36 @@ module ActsAsTaggableOn
15
15
  # end
16
16
  def acts_as_tagger(opts={})
17
17
  class_eval do
18
- has_many :owned_taggings, opts.merge(:as => :tagger, :dependent => :destroy,
19
- :include => :tag, :class_name => "Tagging")
20
- has_many :owned_tags, :through => :owned_taggings, :source => :tag, :uniq => true
18
+ has_many_with_taggable_compatibility :owned_taggings,
19
+ opts.merge(
20
+ as: :tagger,
21
+ dependent: :destroy,
22
+ class_name: 'ActsAsTaggableOn::Tagging'
23
+ )
24
+
25
+ has_many_with_taggable_compatibility :owned_tags,
26
+ through: :owned_taggings,
27
+ source: :tag,
28
+ class_name: 'ActsAsTaggableOn::Tag',
29
+ uniq: true
21
30
  end
22
31
 
23
32
  include ActsAsTaggableOn::Tagger::InstanceMethods
24
33
  extend ActsAsTaggableOn::Tagger::SingletonMethods
25
34
  end
26
35
 
27
- def is_tagger?
36
+ def tagger?
28
37
  false
29
38
  end
39
+
40
+ def is_tagger?
41
+ tagger?
42
+ end
30
43
  end
31
44
 
32
45
  module InstanceMethods
33
46
  ##
34
- # Tag a taggable model with tags that are owned by the tagger.
47
+ # Tag a taggable model with tags that are owned by the tagger.
35
48
  #
36
49
  # @param taggable The object that will be tagged
37
50
  # @param [Hash] options An hash with options. Available options are:
@@ -41,27 +54,35 @@ module ActsAsTaggableOn
41
54
  # Example:
42
55
  # @user.tag(@photo, :with => "paris, normandy", :on => :locations)
43
56
  def tag(taggable, opts={})
44
- opts.reverse_merge!(:force => true)
45
-
57
+ opts.reverse_merge!(force: true)
58
+ skip_save = opts.delete(:skip_save)
46
59
  return false unless taggable.respond_to?(:is_taggable?) && taggable.is_taggable?
47
60
 
48
- raise "You need to specify a tag context using :on" unless opts.has_key?(:on)
49
- raise "You need to specify some tags using :with" unless opts.has_key?(:with)
50
- raise "No context :#{opts[:on]} defined in #{taggable.class.to_s}" unless (opts[:force] || taggable.tag_types.include?(opts[:on]))
61
+ fail 'You need to specify a tag context using :on' unless opts.key?(:on)
62
+ fail 'You need to specify some tags using :with' unless opts.key?(:with)
63
+ fail "No context :#{opts[:on]} defined in #{taggable.class}" unless opts[:force] || taggable.tag_types.include?(opts[:on])
51
64
 
52
65
  taggable.set_owner_tag_list_on(self, opts[:on].to_s, opts[:with])
53
- taggable.save
66
+ taggable.save unless skip_save
54
67
  end
55
68
 
56
- def is_tagger?
69
+ def tagger?
57
70
  self.class.is_tagger?
58
71
  end
72
+
73
+ def is_tagger?
74
+ tagger?
75
+ end
59
76
  end
60
77
 
61
78
  module SingletonMethods
62
- def is_tagger?
79
+ def tagger?
63
80
  true
64
81
  end
82
+
83
+ def is_tagger?
84
+ tagger?
85
+ end
65
86
  end
66
87
  end
67
88
  end
@@ -1,23 +1,33 @@
1
- class Tagging < ActiveRecord::Base #:nodoc:
2
- include ActsAsTaggableOn::ActiveRecord::Backports if ActiveRecord::VERSION::MAJOR < 3
1
+ module ActsAsTaggableOn
2
+ class Tagging < ::ActiveRecord::Base #:nodoc:
3
+ #TODO, remove from 4.0.0
4
+ attr_accessible :tag,
5
+ :tag_id,
6
+ :context,
7
+ :taggable,
8
+ :taggable_type,
9
+ :taggable_id,
10
+ :tagger,
11
+ :tagger_type,
12
+ :tagger_id if defined?(ActiveModel::MassAssignmentSecurity)
3
13
 
4
- attr_accessible :tag,
5
- :tag_id,
6
- :context,
7
- :taggable,
8
- :taggable_type,
9
- :taggable_id,
10
- :tagger,
11
- :tagger_type,
12
- :tagger_id
14
+ belongs_to :tag, class_name: 'ActsAsTaggableOn::Tag' , counter_cache: true
15
+ belongs_to :taggable, polymorphic: true
16
+ belongs_to :tagger, polymorphic: true
13
17
 
14
- belongs_to :tag
15
- belongs_to :taggable, :polymorphic => true
16
- belongs_to :tagger, :polymorphic => true
18
+ validates_presence_of :context
19
+ validates_presence_of :tag_id
17
20
 
18
- validates_presence_of :context
19
- validates_presence_of :tag_id
21
+ validates_uniqueness_of :tag_id, scope: [:taggable_type, :taggable_id, :context, :tagger_id, :tagger_type]
20
22
 
21
- validates_uniqueness_of :tag_id, :scope => [ :taggable_type, :taggable_id, :context, :tagger_id, :tagger_type ]
23
+ after_destroy :remove_unused_tags
22
24
 
23
- end
25
+ private
26
+
27
+ def remove_unused_tags
28
+ if ActsAsTaggableOn.remove_unused_tags
29
+ tag.destroy if tag.taggings_count.zero?
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,17 +1,15 @@
1
- module TagsHelper
2
-
3
- # See the README for an example using tag_cloud.
4
- def tag_cloud(tags, classes)
5
- tags = tags.all if tags.respond_to?(:all)
6
-
7
- return [] if tags.empty?
8
-
9
- max_count = tags.sort_by(&:count).last.count.to_f
10
-
11
- tags.each do |tag|
12
- index = ((tag.count / max_count) * (classes.size - 1)).round
13
- yield tag, classes[index]
1
+ module ActsAsTaggableOn
2
+ module TagsHelper
3
+ # See the wiki for an example using tag_cloud.
4
+ def tag_cloud(tags, classes)
5
+ return [] if tags.empty?
6
+
7
+ max_count = tags.sort_by(&:count).last.count.to_f
8
+
9
+ tags.each do |tag|
10
+ index = ((tag.count / max_count) * (classes.size - 1))
11
+ yield tag, classes[index.nan? ? 0 : index.round]
12
+ end
14
13
  end
15
14
  end
16
-
17
15
  end
@@ -0,0 +1,62 @@
1
+ module ActsAsTaggableOn
2
+ module Utils
3
+ class << self
4
+ # Use ActsAsTaggableOn::Tag connection
5
+ def connection
6
+ ActsAsTaggableOn::Tag.connection
7
+ end
8
+
9
+ def using_postgresql?
10
+ connection && connection.adapter_name == 'PostgreSQL'
11
+ end
12
+
13
+ def postgresql_version
14
+ if using_postgresql?
15
+ connection.execute('SHOW SERVER_VERSION').first['server_version'].to_f
16
+ end
17
+ end
18
+
19
+ def postgresql_support_json?
20
+ postgresql_version >= 9.2
21
+ end
22
+
23
+ def using_sqlite?
24
+ connection && connection.adapter_name == 'SQLite'
25
+ end
26
+
27
+ def using_mysql?
28
+ #We should probably use regex for mysql to support prehistoric adapters
29
+ connection && connection.adapter_name == 'Mysql2'
30
+ end
31
+
32
+ def using_case_insensitive_collation?
33
+ using_mysql? && connection.collation =~ /_ci\Z/
34
+ end
35
+
36
+ def supports_concurrency?
37
+ !using_sqlite?
38
+ end
39
+
40
+ def sha_prefix(string)
41
+ Digest::SHA1.hexdigest("#{string}#{rand}")[0..6]
42
+ end
43
+
44
+ def active_record4?
45
+ ::ActiveRecord::VERSION::MAJOR == 4
46
+ end
47
+
48
+ def active_record42?
49
+ active_record4? && ::ActiveRecord::VERSION::MINOR >= 2
50
+ end
51
+
52
+ def like_operator
53
+ using_postgresql? ? 'ILIKE' : 'LIKE'
54
+ end
55
+
56
+ # escape _ and % characters in strings, since these are wildcards in SQL.
57
+ def escape_like(str)
58
+ str.gsub(/[!%_]/) { |x| '!' + x }
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,4 @@
1
+ module ActsAsTaggableOn
2
+ VERSION = '3.2.2'
3
+ end
4
+
@@ -0,0 +1,6 @@
1
+ $stderr.puts <<EOF
2
+ You are requiring 'acts_as_taggable_on' in the 'acts-as-taggable-on' gem.
3
+
4
+ Please change your require to 'acts-as-taggable-on'.
5
+ EOF
6
+ require_relative 'acts-as-taggable-on'