acts-as-taggable-on 2.0.3 → 2.1.0

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 (41) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +10 -0
  3. data/Gemfile +2 -5
  4. data/Guardfile +5 -0
  5. data/README.rdoc +19 -16
  6. data/Rakefile +9 -55
  7. data/VERSION +1 -1
  8. data/acts-as-taggable-on.gemspec +27 -0
  9. data/lib/acts-as-taggable-on/version.rb +4 -0
  10. data/lib/acts-as-taggable-on.rb +8 -2
  11. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +6 -6
  12. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +40 -31
  13. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +67 -32
  14. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +16 -12
  15. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +17 -9
  16. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +16 -6
  17. data/lib/acts_as_taggable_on/acts_as_tagger.rb +2 -2
  18. data/lib/acts_as_taggable_on/compatibility/Gemfile +3 -1
  19. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +5 -1
  20. data/lib/acts_as_taggable_on/tag.rb +73 -57
  21. data/lib/acts_as_taggable_on/tag_list.rb +79 -78
  22. data/lib/acts_as_taggable_on/tagging.rb +19 -18
  23. data/lib/acts_as_taggable_on/tags_helper.rb +12 -12
  24. data/lib/acts_as_taggable_on/utils.rb +31 -0
  25. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +3 -2
  26. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +25 -2
  27. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +3 -3
  28. data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -3
  29. data/spec/acts_as_taggable_on/tag_spec.rb +41 -21
  30. data/spec/acts_as_taggable_on/taggable_spec.rb +54 -12
  31. data/spec/acts_as_taggable_on/tagger_spec.rb +5 -5
  32. data/spec/acts_as_taggable_on/tagging_spec.rb +7 -7
  33. data/spec/acts_as_taggable_on/tags_helper_spec.rb +3 -3
  34. data/spec/acts_as_taggable_on/utils_spec.rb +22 -0
  35. data/spec/database.yml.sample +19 -0
  36. data/spec/models.rb +4 -0
  37. data/spec/schema.rb +6 -0
  38. data/spec/spec_helper.rb +60 -33
  39. data/uninstall.rb +1 -0
  40. metadata +130 -15
  41. /data/{spec/spec.opts → .rspec} +0 -0
@@ -30,9 +30,13 @@ module ActsAsTaggableOn::Taggable
30
30
 
31
31
  module InstanceMethods
32
32
  def owner_tags_on(owner, context)
33
- base_tags.where([%(#{Tagging.table_name}.context = ? AND
34
- #{Tagging.table_name}.tagger_id = ? AND
35
- #{Tagging.table_name}.tagger_type = ?), context.to_s, owner.id, owner.class.to_s]).all
33
+ if owner.nil?
34
+ base_tags.where([%(#{ActsAsTaggableOn::Tagging.table_name}.context = ?), context.to_s]).all
35
+ else
36
+ base_tags.where([%(#{ActsAsTaggableOn::Tagging.table_name}.context = ? AND
37
+ #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = ? AND
38
+ #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = ?), context.to_s, owner.id, owner.class.to_s]).all
39
+ end
36
40
  end
37
41
 
38
42
  def cached_owned_tag_list_on(context)
@@ -46,7 +50,7 @@ module ActsAsTaggableOn::Taggable
46
50
  cache = cached_owned_tag_list_on(context)
47
51
  cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
48
52
 
49
- cache[owner] ||= TagList.new(*owner_tags_on(owner, context).map(&:name))
53
+ cache[owner] ||= ActsAsTaggableOn::TagList.new(*owner_tags_on(owner, context).map(&:name))
50
54
  end
51
55
 
52
56
  def set_owner_tag_list_on(owner, context, new_list)
@@ -55,22 +59,22 @@ module ActsAsTaggableOn::Taggable
55
59
  cache = cached_owned_tag_list_on(context)
56
60
  cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
57
61
 
58
- cache[owner] = TagList.from(new_list)
62
+ cache[owner] = ActsAsTaggableOn::TagList.from(new_list)
59
63
  end
60
64
 
61
- def reload
65
+ def reload(*args)
62
66
  self.class.tag_types.each do |context|
63
67
  instance_variable_set("@owned_#{context}_list", nil)
64
68
  end
65
69
 
66
- super
70
+ super(*args)
67
71
  end
68
72
 
69
73
  def save_owned_tags
70
74
  tagging_contexts.each do |context|
71
75
  cached_owned_tag_list_on(context).each do |owner, tag_list|
72
76
  # Find existing tags or create non-existing tags:
73
- tag_list = Tag.find_or_create_all_with_like_by_name(tag_list.uniq)
77
+ tag_list = ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name(tag_list.uniq)
74
78
 
75
79
  owned_tags = owner_tags_on(owner, context)
76
80
  old_tags = owned_tags - tag_list
@@ -78,13 +82,13 @@ module ActsAsTaggableOn::Taggable
78
82
 
79
83
  # Find all taggings that belong to the taggable (self), are owned by the owner,
80
84
  # have the correct context, and are removed from the list.
81
- old_taggings = Tagging.where(:taggable_id => id, :taggable_type => self.class.base_class.to_s,
82
- :tagger_type => owner.class.to_s, :tagger_id => owner.id,
83
- :tag_id => old_tags, :context => context).all
85
+ old_taggings = ActsAsTaggableOn::Tagging.where(:taggable_id => id, :taggable_type => self.class.base_class.to_s,
86
+ :tagger_type => owner.class.to_s, :tagger_id => owner.id,
87
+ :tag_id => old_tags, :context => context).all
84
88
 
85
89
  if old_taggings.present?
86
90
  # Destroy old taggings:
87
- Tagging.destroy_all(:id => old_taggings.map(&:id))
91
+ ActsAsTaggableOn::Tagging.destroy_all(:id => old_taggings.map(&:id))
88
92
  end
89
93
 
90
94
  # Create new taggings:
@@ -18,7 +18,11 @@ module ActsAsTaggableOn::Taggable
18
18
  def find_related_#{tag_type}_for(klass, options = {})
19
19
  related_tags_for('#{tag_type}', klass, options)
20
20
  end
21
-
21
+ )
22
+ end
23
+
24
+ unless tag_types.empty?
25
+ class_eval %(
22
26
  def find_matching_contexts(search_context, result_context, options = {})
23
27
  matching_contexts_for(search_context.to_s, result_context.to_s, self.class, options)
24
28
  end
@@ -42,10 +46,12 @@ module ActsAsTaggableOn::Taggable
42
46
 
43
47
  exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
44
48
 
45
- klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
46
- :from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
47
- :conditions => ["#{exclude_self} #{klass.table_name}.id = #{Tagging.table_name}.taggable_id AND #{Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND #{Tag.table_name}.name IN (?) AND #{Tagging.table_name}.context = ?", tags_to_find, result_context],
48
- :group => grouped_column_names_for(klass),
49
+ group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(klass) : "#{klass.table_name}.#{klass.primary_key}"
50
+
51
+ klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{ActsAsTaggableOn::Tag.table_name}.id) AS count",
52
+ :from => "#{klass.table_name}, #{ActsAsTaggableOn::Tag.table_name}, #{ActsAsTaggableOn::Tagging.table_name}",
53
+ :conditions => ["#{exclude_self} #{klass.table_name}.id = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id AND #{ActsAsTaggableOn::Tag.table_name}.name IN (?) AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_to_find, result_context],
54
+ :group => group_columns,
49
55
  :order => "count DESC" }.update(options))
50
56
  end
51
57
 
@@ -54,10 +60,12 @@ module ActsAsTaggableOn::Taggable
54
60
 
55
61
  exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
56
62
 
57
- klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
58
- :from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
59
- :conditions => ["#{exclude_self} #{klass.table_name}.id = #{Tagging.table_name}.taggable_id AND #{Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND #{Tag.table_name}.name IN (?)", tags_to_find],
60
- :group => grouped_column_names_for(klass),
63
+ group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(klass) : "#{klass.table_name}.#{klass.primary_key}"
64
+
65
+ klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{ActsAsTaggableOn::Tag.table_name}.id) AS count",
66
+ :from => "#{klass.table_name}, #{ActsAsTaggableOn::Tag.table_name}, #{ActsAsTaggableOn::Tagging.table_name}",
67
+ :conditions => ["#{exclude_self} #{klass.table_name}.id = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id AND #{ActsAsTaggableOn::Tag.table_name}.name IN (?)", tags_to_find],
68
+ :group => group_columns,
61
69
  :order => "count DESC" }.update(options))
62
70
  end
63
71
  end
@@ -28,19 +28,29 @@ module ActsAsTaggableOn
28
28
  tag_types = tag_types.to_a.flatten.compact.map(&:to_sym)
29
29
 
30
30
  if taggable?
31
- write_inheritable_attribute(:tag_types, (self.tag_types + tag_types).uniq)
31
+ if RAILS_3
32
+ self.tag_types = (self.tag_types + tag_types).uniq
33
+ else
34
+ write_inheritable_attribute(:tag_types, (self.tag_types + tag_types).uniq)
35
+ end
32
36
  else
33
- write_inheritable_attribute(:tag_types, tag_types)
34
- class_inheritable_reader(:tag_types)
37
+ if RAILS_3
38
+ class_attribute :tag_types
39
+ self.tag_types = tag_types
40
+ else
41
+ write_inheritable_attribute(:tag_types, tag_types)
42
+ class_inheritable_reader(:tag_types)
43
+ end
35
44
 
36
45
  class_eval do
37
- has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
38
- has_many :base_tags, :class_name => "Tag", :through => :taggings, :source => :tag
46
+ has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "ActsAsTaggableOn::Tagging"
47
+ has_many :base_tags, :through => :taggings, :source => :tag, :class_name => "ActsAsTaggableOn::Tag"
39
48
 
40
49
  def self.taggable?
41
50
  true
42
51
  end
43
-
52
+
53
+ include ActsAsTaggableOn::Utils
44
54
  include ActsAsTaggableOn::Taggable::Core
45
55
  include ActsAsTaggableOn::Taggable::Collection
46
56
  include ActsAsTaggableOn::Taggable::Cache
@@ -16,8 +16,8 @@ module ActsAsTaggableOn
16
16
  def acts_as_tagger(opts={})
17
17
  class_eval do
18
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
19
+ :include => :tag, :class_name => "ActsAsTaggableOn::Tagging")
20
+ has_many :owned_tags, :through => :owned_taggings, :source => :tag, :uniq => true, :class_name => "ActsAsTaggableOn::Tag"
21
21
  end
22
22
 
23
23
  include ActsAsTaggableOn::Tagger::InstanceMethods
@@ -3,4 +3,6 @@ source :gemcutter
3
3
  # Rails 2.3
4
4
  gem 'rails', '2.3.5'
5
5
  gem 'rspec', '1.3.0', :require => 'spec'
6
- gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3'
6
+ gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3'
7
+ gem 'mysql2', '~> 0.2.7'
8
+ gem 'pg'
@@ -9,7 +9,11 @@ module ActsAsTaggableOn
9
9
  named_scope :order, lambda { |order| { :order => order } }
10
10
  named_scope :select, lambda { |select| { :select => select } }
11
11
  named_scope :limit, lambda { |limit| { :limit => limit } }
12
- named_scope :readonly, lambda { |readonly| { :readonly => readonly } }
12
+ named_scope :readonly, lambda { |readonly| { :readonly => readonly } }
13
+
14
+ def self.to_sql
15
+ construct_finder_sql({})
16
+ end
13
17
  end
14
18
  end
15
19
  end
@@ -1,65 +1,81 @@
1
- class Tag < ActiveRecord::Base
2
- include ActsAsTaggableOn::ActiveRecord::Backports if ActiveRecord::VERSION::MAJOR < 3
3
-
4
- attr_accessible :name
5
-
6
- ### ASSOCIATIONS:
1
+ module ActsAsTaggableOn
2
+ class Tag < ::ActiveRecord::Base
3
+ include ActsAsTaggableOn::ActiveRecord::Backports if ::ActiveRecord::VERSION::MAJOR < 3
4
+ include ActsAsTaggableOn::Utils
5
+
6
+ attr_accessible :name
7
7
 
8
- has_many :taggings, :dependent => :destroy
8
+ ### ASSOCIATIONS:
9
9
 
10
- ### VALIDATIONS:
10
+ has_many :taggings, :dependent => :destroy, :class_name => 'ActsAsTaggableOn::Tagging'
11
11
 
12
- validates_presence_of :name
13
- validates_uniqueness_of :name
12
+ ### VALIDATIONS:
14
13
 
15
- ### SCOPES:
14
+ validates_presence_of :name
15
+ validates_uniqueness_of :name
16
16
 
17
- def self.named(name)
18
- where(["name LIKE ?", name])
19
- end
17
+ ### SCOPES:
18
+
19
+ def self.named(name)
20
+ where(["name #{like_operator} ?", escape_like(name)])
21
+ end
20
22
 
21
- def self.named_any(list)
22
- where(list.map { |tag| sanitize_sql(["name LIKE ?", tag.to_s]) }.join(" OR "))
23
- end
23
+ def self.named_any(list)
24
+ where(list.map { |tag| sanitize_sql(["name #{like_operator} ?", escape_like(tag.to_s)]) }.join(" OR "))
25
+ end
24
26
 
25
- def self.named_like(name)
26
- where(["name LIKE ?", "%#{name}%"])
27
- end
28
-
29
- def self.named_like_any(list)
30
- where(list.map { |tag| sanitize_sql(["name LIKE ?", "%#{tag.to_s}%"]) }.join(" OR "))
31
- end
32
-
33
- ### CLASS METHODS:
34
-
35
- def self.find_or_create_with_like_by_name(name)
36
- named_like(name).first || create(:name => name)
37
- end
38
-
39
- def self.find_or_create_all_with_like_by_name(*list)
40
- list = [list].flatten
41
-
42
- return [] if list.empty?
43
-
44
- existing_tags = Tag.named_any(list).all
45
- new_tag_names = list.reject { |name| existing_tags.any? { |tag| tag.name.mb_chars.downcase == name.mb_chars.downcase } }
46
- created_tags = new_tag_names.map { |name| Tag.create(:name => name) }
47
-
48
- existing_tags + created_tags
49
- end
50
-
51
- ### INSTANCE METHODS:
52
-
53
- def ==(object)
54
- super || (object.is_a?(Tag) && name == object.name)
27
+ def self.named_like(name)
28
+ where(["name #{like_operator} ?", "%#{escape_like(name)}%"])
29
+ end
30
+
31
+ def self.named_like_any(list)
32
+ where(list.map { |tag| sanitize_sql(["name #{like_operator} ?", "%#{escape_like(tag.to_s)}%"]) }.join(" OR "))
33
+ end
34
+
35
+ ### CLASS METHODS:
36
+
37
+ def self.find_or_create_with_like_by_name(name)
38
+ named_like(name).first || create(:name => name)
39
+ end
40
+
41
+ def self.find_or_create_all_with_like_by_name(*list)
42
+ list = [list].flatten
43
+
44
+ return [] if list.empty?
45
+
46
+ existing_tags = Tag.named_any(list).all
47
+ new_tag_names = list.reject do |name|
48
+ name = comparable_name(name)
49
+ existing_tags.any? { |tag| comparable_name(tag.name) == name }
50
+ end
51
+ created_tags = new_tag_names.map { |name| Tag.create(:name => name) }
52
+
53
+ existing_tags + created_tags
54
+ end
55
+
56
+ ### INSTANCE METHODS:
57
+
58
+ def ==(object)
59
+ super || (object.is_a?(Tag) && name == object.name)
60
+ end
61
+
62
+ def to_s
63
+ name
64
+ end
65
+
66
+ def count
67
+ read_attribute(:count).to_i
68
+ end
69
+
70
+ def safe_name
71
+ name.gsub(/[^a-zA-Z0-9]/, '')
72
+ end
73
+
74
+ class << self
75
+ private
76
+ def comparable_name(str)
77
+ RUBY_VERSION >= "1.9" ? str.downcase : str.mb_chars.downcase
78
+ end
79
+ end
55
80
  end
56
-
57
- def to_s
58
- name
59
- end
60
-
61
- def count
62
- read_attribute(:count).to_i
63
- end
64
-
65
- end
81
+ end
@@ -1,95 +1,96 @@
1
- class TagList < Array
1
+ module ActsAsTaggableOn
2
+ class TagList < Array
3
+ cattr_accessor :delimiter
4
+ self.delimiter = ','
2
5
 
3
- cattr_accessor :delimiter
4
- self.delimiter = ','
6
+ attr_accessor :owner
5
7
 
6
- attr_accessor :owner
7
-
8
- def initialize(*args)
9
- add(*args)
10
- end
8
+ def initialize(*args)
9
+ add(*args)
10
+ end
11
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)
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
+ glue = delimiter.ends_with?(" ") ? delimiter : "#{delimiter} "
20
+ string = string.join(glue) if string.respond_to?(:join)
20
21
 
21
- new.tap do |tag_list|
22
- string = string.to_s.dup
22
+ new.tap do |tag_list|
23
+ string = string.to_s.dup
23
24
 
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 }
25
+ # Parse the quoted tags
26
+ string.gsub!(/(\A|#{delimiter})\s*"(.*?)"\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
27
+ string.gsub!(/(\A|#{delimiter})\s*'(.*?)'\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
27
28
 
28
- tag_list.add(string.split(delimiter))
29
+ tag_list.add(string.split(delimiter))
30
+ end
29
31
  end
30
- end
31
32
 
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
33
+ ##
34
+ # Add tags to the tag_list. Duplicate or blank tags will be ignored.
35
+ # Use the <tt>:parse</tt> option to add an unparsed tag string.
36
+ #
37
+ # Example:
38
+ # tag_list.add("Fun", "Happy")
39
+ # tag_list.add("Fun, Happy", :parse => true)
40
+ def add(*names)
41
+ extract_and_apply_options!(names)
42
+ concat(names)
43
+ clean!
44
+ self
45
+ end
45
46
 
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
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
58
59
 
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!)
60
+ ##
61
+ # Transform the tag_list into a tag string suitable for edting 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? ? self.dup : self
69
+ tags.send(:clean!)
69
70
 
70
- tags.map do |name|
71
- name.include?(delimiter) ? "\"#{name}\"" : name
72
- end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
73
- end
71
+ tags.map do |name|
72
+ name.include?(delimiter) ? "\"#{name}\"" : name
73
+ end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
74
+ end
74
75
 
75
- private
76
+ private
76
77
 
77
- # Remove whitespace, duplicates, and blanks.
78
- def clean!
79
- reject!(&:blank?)
80
- map!(&:strip)
81
- uniq!
82
- end
78
+ # Remove whitespace, duplicates, and blanks.
79
+ def clean!
80
+ reject!(&:blank?)
81
+ map!(&:strip)
82
+ uniq!
83
+ end
83
84
 
84
- def extract_and_apply_options!(args)
85
- options = args.last.is_a?(Hash) ? args.pop : {}
86
- options.assert_valid_keys :parse
85
+ def extract_and_apply_options!(args)
86
+ options = args.last.is_a?(Hash) ? args.pop : {}
87
+ options.assert_valid_keys :parse
87
88
 
88
- if options[:parse]
89
- args.map! { |a| self.class.from(a) }
90
- end
89
+ if options[:parse]
90
+ args.map! { |a| self.class.from(a) }
91
+ end
91
92
 
92
- args.flatten!
93
+ args.flatten!
94
+ end
93
95
  end
94
-
95
- end
96
+ end
@@ -1,23 +1,24 @@
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
+ include ActsAsTaggableOn::ActiveRecord::Backports if ::ActiveRecord::VERSION::MAJOR < 3
3
4
 
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
5
+ attr_accessible :tag,
6
+ :tag_id,
7
+ :context,
8
+ :taggable,
9
+ :taggable_type,
10
+ :taggable_id,
11
+ :tagger,
12
+ :tagger_type,
13
+ :tagger_id
13
14
 
14
- belongs_to :tag
15
- belongs_to :taggable, :polymorphic => true
16
- belongs_to :tagger, :polymorphic => true
15
+ belongs_to :tag, :class_name => 'ActsAsTaggableOn::Tag'
16
+ belongs_to :taggable, :polymorphic => true
17
+ belongs_to :tagger, :polymorphic => true
17
18
 
18
- validates_presence_of :context
19
- validates_presence_of :tag_id
20
-
21
- validates_uniqueness_of :tag_id, :scope => [ :taggable_type, :taggable_id, :context, :tagger_id, :tagger_type ]
19
+ validates_presence_of :context
20
+ validates_presence_of :tag_id
22
21
 
22
+ validates_uniqueness_of :tag_id, :scope => [ :taggable_type, :taggable_id, :context, :tagger_id, :tagger_type ]
23
+ end
23
24
  end
@@ -1,17 +1,17 @@
1
- module TagsHelper
1
+ module ActsAsTaggableOn
2
+ module TagsHelper
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)
2
6
 
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)
7
+ return [] if tags.empty?
6
8
 
7
- return [] if tags.empty?
9
+ max_count = tags.sort_by(&:count).last.count.to_f
8
10
 
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]
11
+ tags.each do |tag|
12
+ index = ((tag.count / max_count) * (classes.size - 1)).round
13
+ yield tag, classes[index]
14
+ end
14
15
  end
15
16
  end
16
-
17
- end
17
+ end
@@ -0,0 +1,31 @@
1
+ module ActsAsTaggableOn
2
+ module Utils
3
+ def self.included(base)
4
+
5
+ base.send :include, ActsAsTaggableOn::Utils::OverallMethods
6
+ base.extend ActsAsTaggableOn::Utils::OverallMethods
7
+ end
8
+
9
+ module OverallMethods
10
+ def using_postgresql?
11
+ ::ActiveRecord::Base.connection && ::ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
12
+ end
13
+
14
+ def using_sqlite?
15
+ ::ActiveRecord::Base.connection && ::ActiveRecord::Base.connection.adapter_name == 'SQLite'
16
+ end
17
+
18
+ private
19
+ def like_operator
20
+ using_postgresql? ? 'ILIKE' : 'LIKE'
21
+ end
22
+
23
+ # escape _ and % characters in strings, since these are wildcards in SQL.
24
+ def escape_like(str)
25
+ return str if using_sqlite? # skip escaping for SQLite
26
+ str.to_s.gsub("_", "\\\_").gsub("%", "\\\%")
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -11,7 +11,7 @@ module ActsAsTaggableOn
11
11
  end
12
12
 
13
13
  def self.source_root
14
- File.join(File.dirname(__FILE__), 'templates', orm)
14
+ File.join(File.dirname(__FILE__), 'templates', (orm.to_s unless orm.class.eql?(String)) )
15
15
  end
16
16
 
17
17
  def self.orm_has_migration?
@@ -19,7 +19,7 @@ module ActsAsTaggableOn
19
19
  end
20
20
 
21
21
  def self.next_migration_number(path)
22
- Time.now.utc.strftime("%Y%m%d%H%M%S")
22
+ ActiveRecord::Generators::Base.next_migration_number(path)
23
23
  end
24
24
 
25
25
  def create_migration_file
@@ -29,3 +29,4 @@ module ActsAsTaggableOn
29
29
  end
30
30
  end
31
31
  end
32
+