acts-as-taggable-on 2.0.0 → 2.0.6

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 (32) hide show
  1. data/Gemfile +7 -3
  2. data/README.rdoc +14 -14
  3. data/VERSION +1 -1
  4. data/lib/acts-as-taggable-on.rb +1 -1
  5. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +3 -3
  6. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +56 -22
  7. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +26 -22
  8. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +12 -12
  9. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +7 -6
  10. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +2 -2
  11. data/lib/acts_as_taggable_on/acts_as_tagger.rb +2 -2
  12. data/lib/acts_as_taggable_on/compatibility/Gemfile +3 -1
  13. data/lib/acts_as_taggable_on/compatibility/postgresql.rb +44 -0
  14. data/lib/acts_as_taggable_on/tag.rb +53 -44
  15. data/lib/acts_as_taggable_on/tag_list.rb +79 -78
  16. data/lib/acts_as_taggable_on/tagging.rb +19 -18
  17. data/lib/acts_as_taggable_on/tags_helper.rb +12 -12
  18. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +2 -1
  19. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +4 -2
  20. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +3 -3
  21. data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -3
  22. data/spec/acts_as_taggable_on/tag_spec.rb +21 -21
  23. data/spec/acts_as_taggable_on/taggable_spec.rb +37 -12
  24. data/spec/acts_as_taggable_on/tagger_spec.rb +5 -5
  25. data/spec/acts_as_taggable_on/tagging_spec.rb +7 -7
  26. data/spec/acts_as_taggable_on/tags_helper_spec.rb +3 -3
  27. data/spec/database.yml +17 -0
  28. data/spec/database.yml.sample +17 -0
  29. data/spec/models.rb +1 -0
  30. data/spec/spec_helper.rb +40 -33
  31. metadata +6 -4
  32. data/spec/spec.opts +0 -2
data/Gemfile CHANGED
@@ -1,6 +1,10 @@
1
1
  source :gemcutter
2
2
 
3
3
  # Rails 3.0
4
- gem 'rails', '3.0.0.beta'
5
- gem 'rspec', '2.0.0.beta.1'
6
- gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3'
4
+ gem 'rails', '3.0.0.beta3'
5
+ gem 'rspec', '2.0.0.beta.8'
6
+ gem 'sqlite3-ruby', :require => 'sqlite3'
7
+ gem 'mysql'
8
+ gem 'pg'
9
+ gem 'jeweler'
10
+ gem 'rcov'
data/README.rdoc CHANGED
@@ -38,21 +38,21 @@ After that, you can run "rake gems:install" to install the gem if you don't alre
38
38
  1. script/generate acts_as_taggable_on_migration
39
39
  2. rake db:migrate
40
40
 
41
- == Rails 3.0
41
+ === Rails 3.0
42
42
 
43
43
  Acts As Taggable On is now useable in Rails 3.0, thanks to the excellent work of Szymon Nowak
44
44
  and Jelle Vandebeeck.
45
45
 
46
46
  To use it, add it to your Gemfile:
47
47
 
48
- gem 'acts-as-taggable-on', '2.0.0.rc1'
48
+ gem 'acts-as-taggable-on'
49
49
 
50
- === Post Installation
50
+ ==== Post Installation
51
51
 
52
52
  1. rails generate acts_as_taggable_on:migration
53
53
  2. rake db:migrate
54
54
 
55
- = Testing
55
+ == Testing
56
56
 
57
57
  Acts As Taggable On uses RSpec for its test coverage. Inside the plugin
58
58
  directory, you can run the specs for RoR 3.0.0 with:
@@ -68,7 +68,7 @@ If you already have RSpec on your application, the specs will run while using:
68
68
  rake spec:plugins
69
69
 
70
70
 
71
- = Usage
71
+ == Usage
72
72
 
73
73
  class User < ActiveRecord::Base
74
74
  # Alias for <tt>acts_as_taggable_on :tags</tt>:
@@ -89,7 +89,7 @@ rake spec:plugins
89
89
  User.skill_counts # => [<Tag name="joking" count=2>,<Tag name="clowning" count=1>...]
90
90
  @frankie.skill_counts
91
91
 
92
- == Finding Tagged Objects
92
+ === Finding Tagged Objects
93
93
 
94
94
  Acts As Taggable On utilizes named_scopes to create an association for tags.
95
95
  This way you can mix and match to filter down your results, and it also improves
@@ -109,7 +109,7 @@ compatibility with the will_paginate gem:
109
109
  # Find a user with any of the tags:
110
110
  User.tagged_with(["awesome", "cool"], :any => true)
111
111
 
112
- == Relationships
112
+ === Relationships
113
113
 
114
114
  You can find objects of the same type based on similar tags on certain contexts.
115
115
  Also, objects will be returned in descending order based on the total number of
@@ -128,7 +128,7 @@ matched tags.
128
128
  @bobby.find_related_skills # => [<User name="Tom">]
129
129
  @frankie.find_related_skills # => [<User name="Tom">]
130
130
 
131
- == Dynamic Tag Contexts
131
+ === Dynamic Tag Contexts
132
132
 
133
133
  In addition to the generated tag contexts in the definition, it is also possible
134
134
  to allow for dynamic tag contexts (this could be user generated tag contexts!)
@@ -139,9 +139,9 @@ to allow for dynamic tag contexts (this could be user generated tag contexts!)
139
139
  @user.save
140
140
  @user.tags_on(:customs) # => [<Tag name='same'>,...]
141
141
  @user.tag_counts_on(:customs)
142
- User.find_tagged_with("same", :on => :customs) # => [@user]
142
+ User.tagged_with("same", :on => :customs) # => [@user]
143
143
 
144
- == Tag Ownership
144
+ === Tag Ownership
145
145
 
146
146
  Tags can have owners:
147
147
 
@@ -158,7 +158,7 @@ Tags can have owners:
158
158
  @some_user.owned_tags
159
159
  @some_photo.locations_from(@some_user)
160
160
 
161
- == Tag cloud calculations
161
+ === Tag cloud calculations
162
162
 
163
163
  To construct tag clouds, the frequency of each tag needs to be calculated.
164
164
  Because we specified +acts_as_taggable_on+ on the <tt>User</tt> class, we can
@@ -174,7 +174,7 @@ Here is an example that generates a tag cloud.
174
174
  Helper:
175
175
 
176
176
  module PostsHelper
177
- include TagsHelper
177
+ include ActsAsTaggableOn::TagsHelper
178
178
  end
179
179
 
180
180
  Controller:
@@ -198,7 +198,7 @@ CSS:
198
198
  .css3 { font-size: 1.4em; }
199
199
  .css4 { font-size: 1.6em; }
200
200
 
201
- = Contributors
201
+ == Contributors
202
202
 
203
203
  * TomEric (i76) - Maintainer
204
204
  * Michael Bleigh - Original Author
@@ -208,7 +208,7 @@ CSS:
208
208
  * Pradeep Elankumaran - Taggers
209
209
  * Sinclair Bain - Patch King
210
210
 
211
- == Patch Contributors
211
+ === Patch Contributors
212
212
 
213
213
  * tristanzdunn - Related objects of other classes
214
214
  * azabaj - Fixed migrate down
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0
1
+ 2.0.6
@@ -26,5 +26,5 @@ if defined?(ActiveRecord::Base)
26
26
  end
27
27
 
28
28
  if defined?(ActionView::Base)
29
- ActionView::Base.send :include, TagsHelper
29
+ ActionView::Base.send :include, ActsAsTaggableOn::TagsHelper
30
30
  end
@@ -1,8 +1,8 @@
1
1
  module ActsAsTaggableOn::Taggable
2
2
  module Cache
3
3
  def self.included(base)
4
- # Skip adding caching capabilities if no cache columns exist
5
- return unless base.tag_types.any? { |context| base.column_names.include?("cached_#{context.to_s.singularize}_list") }
4
+ # Skip adding caching capabilities if table not exists or no cache columns exist
5
+ return unless base.table_exists? && base.tag_types.any? { |context| base.column_names.include?("cached_#{context.to_s.singularize}_list") }
6
6
 
7
7
  base.send :include, ActsAsTaggableOn::Taggable::Cache::InstanceMethods
8
8
  base.extend ActsAsTaggableOn::Taggable::Cache::ClassMethods
@@ -50,4 +50,4 @@ module ActsAsTaggableOn::Taggable
50
50
  end
51
51
  end
52
52
  end
53
- end
53
+ end
@@ -53,39 +53,73 @@ module ActsAsTaggableOn::Taggable
53
53
  def all_tag_counts(options = {})
54
54
  options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :on, :id
55
55
 
56
- start_at = sanitize_sql(["#{Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
57
- end_at = sanitize_sql(["#{Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
56
+ scope = if ActiveRecord::VERSION::MAJOR >= 3
57
+ {}
58
+ else
59
+ scope(:find) || {}
60
+ end
58
61
 
59
- taggable_type = sanitize_sql(["#{Tagging.table_name}.taggable_type = ?", base_class.name])
60
- taggable_id = sanitize_sql(["#{Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
61
- options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
62
+ ## Generate conditions:
63
+ options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
64
+
65
+ start_at_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
66
+ end_at_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
67
+
68
+ taggable_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.taggable_type = ?", base_class.name])
69
+ taggable_conditions << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
62
70
 
63
71
  conditions = [
64
- taggable_type,
65
- taggable_id,
72
+ taggable_conditions,
66
73
  options[:conditions],
67
- start_at,
68
- end_at
69
- ]
74
+ scope[:conditions],
75
+ start_at_conditions,
76
+ end_at_conditions
77
+ ].compact.reverse
78
+
79
+ ## Generate joins:
80
+ tagging_join = "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} ON #{ActsAsTaggableOn::Tag.table_name}.id = #{ActsAsTaggableOn::Tagging.table_name}.tag_id"
81
+ tagging_join << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", options.delete(:on).to_s]) if options[:on]
70
82
 
71
- conditions = conditions.compact.join(' AND ')
83
+ taggable_join = "INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id"
84
+ taggable_join << " AND #{table_name}.#{inheritance_column} = '#{name}'" unless descends_from_active_record? # Current model is STI descendant, so add type checking to the join condition
72
85
 
73
- joins = ["LEFT OUTER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
74
- joins << sanitize_sql(["AND #{Tagging.table_name}.context = ?",options.delete(:on).to_s]) unless options[:on].nil?
75
- joins << " INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
86
+ joins = [
87
+ tagging_join,
88
+ taggable_join,
89
+ scope[:joins]
90
+ ].compact
91
+
92
+ joins = joins.reverse if ActiveRecord::VERSION::MAJOR < 3
76
93
 
77
- unless descends_from_active_record?
78
- # Current model is STI descendant, so add type checking to the join condition
79
- joins << " AND #{table_name}.#{inheritance_column} = '#{name}'"
80
- end
81
94
 
95
+ ## Generate scope:
96
+ scope = ActsAsTaggableOn::Tag.scoped(:select => "#{ActsAsTaggableOn::Tag.table_name}.*, COUNT(*) AS count").order(options[:order]).limit(options[:limit])
97
+
98
+ # Joins and conditions
99
+ joins.each { |join| scope = scope.joins(join) }
100
+ conditions.each { |condition| scope = scope.where(condition) }
101
+
102
+ # GROUP BY and HAVING clauses:
82
103
  at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
83
104
  at_most = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
84
- having = [at_least, at_most].compact.join(' AND ')
85
- group_by = "#{grouped_column_names_for(Tag)} HAVING COUNT(*) > 0"
86
- group_by << " AND #{having}" unless having.blank?
105
+ having = [at_least, at_most].compact.join(' AND ')
106
+
107
+ if ActiveRecord::VERSION::MAJOR >= 3
108
+ # Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore:
109
+ scoped_select = "#{table_name}.#{primary_key}"
110
+ scope = scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{select(scoped_select).to_sql})")
111
+
112
+ # We have having() in RoR 3.0 so use it:
113
+ having = having.blank? ? "COUNT(*) > 0" : "COUNT(*) > 0 AND #{having}"
114
+ scope = scope.group(grouped_column_names_for(ActsAsTaggableOn::Tag)).having(having)
115
+ else
116
+ # Having is not available in 2.3.x:
117
+ group_by = "#{grouped_column_names_for(ActsAsTaggableOn::Tag)} HAVING COUNT(*) > 0"
118
+ group_by << " AND #{having}" unless having.blank?
119
+ scope = scope.group(group_by)
120
+ end
87
121
 
88
- Tag.select("#{Tag.table_name}.*, COUNT(*) AS count").joins(joins.join(" ")).where(conditions).group(group_by).limit(options[:limit]).order(options[:order])
122
+ scope
89
123
  end
90
124
  end
91
125
 
@@ -20,9 +20,9 @@ module ActsAsTaggableOn::Taggable
20
20
  context_tags = tags_type.to_sym
21
21
 
22
22
  class_eval do
23
- has_many context_taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "Tagging",
24
- :conditions => ['#{Tagging.table_name}.tagger_id IS NULL AND #{Tagging.table_name}.context = ?', tags_type]
25
- has_many context_tags, :through => context_taggings, :source => :tag
23
+ has_many context_taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "ActsAsTaggableOn::Tagging",
24
+ :conditions => ["#{ActsAsTaggableOn::Tagging.table_name}.tagger_id IS NULL AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_type]
25
+ has_many context_tags, :through => context_taggings, :source => :tag, :class_name => "ActsAsTaggableOn::Tag"
26
26
  end
27
27
 
28
28
  class_eval %(
@@ -66,7 +66,7 @@ module ActsAsTaggableOn::Taggable
66
66
  # User.tagged_with("awesome", "cool", :any => true) # Users that are tagged with awesome or cool
67
67
  # User.tagged_with("awesome", "cool", :match_all => true) # Users that are tagged with just awesome and cool
68
68
  def tagged_with(tags, options = {})
69
- tag_list = TagList.from(tags)
69
+ tag_list = ActsAsTaggableOn::TagList.from(tags)
70
70
 
71
71
  return {} if tag_list.empty?
72
72
 
@@ -76,16 +76,16 @@ module ActsAsTaggableOn::Taggable
76
76
  context = options.delete(:on)
77
77
 
78
78
  if options.delete(:exclude)
79
- tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
80
- conditions << "#{table_name}.#{primary_key} NOT IN (SELECT #{Tagging.table_name}.taggable_id FROM #{Tagging.table_name} JOIN #{Tag.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND (#{tags_conditions}) WHERE #{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
79
+ tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
80
+ conditions << "#{table_name}.#{primary_key} NOT IN (SELECT #{ActsAsTaggableOn::Tagging.table_name}.taggable_id FROM #{ActsAsTaggableOn::Tagging.table_name} JOIN #{ActsAsTaggableOn::Tag.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
81
81
 
82
82
  elsif options.delete(:any)
83
- tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
84
- conditions << "#{table_name}.#{primary_key} IN (SELECT #{Tagging.table_name}.taggable_id FROM #{Tagging.table_name} JOIN #{Tag.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND (#{tags_conditions}) WHERE #{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
83
+ tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
84
+ conditions << "#{table_name}.#{primary_key} IN (SELECT #{ActsAsTaggableOn::Tagging.table_name}.taggable_id FROM #{ActsAsTaggableOn::Tagging.table_name} JOIN #{ActsAsTaggableOn::Tag.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
85
85
 
86
86
  else
87
- tags = Tag.named_any(tag_list)
88
- return where("1 = 0") unless tags.length == tag_list.length
87
+ tags = ActsAsTaggableOn::Tag.named_any(tag_list)
88
+ return scoped(:conditions => "1 = 0") unless tags.length == tag_list.length
89
89
 
90
90
  tags.each do |tag|
91
91
  safe_tag = tag.name.gsub(/[^a-zA-Z0-9]/, '')
@@ -93,7 +93,7 @@ module ActsAsTaggableOn::Taggable
93
93
 
94
94
  taggings_alias = "#{table_name}_taggings_#{prefix}"
95
95
 
96
- tagging_join = "JOIN #{Tagging.table_name} #{taggings_alias}" +
96
+ tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
97
97
  " ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
98
98
  " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}" +
99
99
  " AND #{taggings_alias}.tag_id = #{tag.id}"
@@ -106,7 +106,7 @@ module ActsAsTaggableOn::Taggable
106
106
  taggings_alias, tags_alias = "#{table_name}_taggings_group", "#{table_name}_tags_group"
107
107
 
108
108
  if options.delete(:match_all)
109
- joins << "LEFT OUTER JOIN #{Tagging.table_name} #{taggings_alias}" +
109
+ joins << "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
110
110
  " ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
111
111
  " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
112
112
 
@@ -117,6 +117,7 @@ module ActsAsTaggableOn::Taggable
117
117
  scoped(:joins => joins.join(" "),
118
118
  :group => group,
119
119
  :conditions => conditions.join(" AND "),
120
+ :order => options[:order],
120
121
  :readonly => false)
121
122
  end
122
123
 
@@ -154,7 +155,7 @@ module ActsAsTaggableOn::Taggable
154
155
 
155
156
  def tag_list_cache_on(context)
156
157
  variable_name = "@#{context.to_s.singularize}_list"
157
- instance_variable_get(variable_name) || instance_variable_set(variable_name, TagList.new(tags_on(context).map(&:name)))
158
+ instance_variable_get(variable_name) || instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(tags_on(context).map(&:name)))
158
159
  end
159
160
 
160
161
  def tag_list_on(context)
@@ -166,40 +167,43 @@ module ActsAsTaggableOn::Taggable
166
167
  variable_name = "@all_#{context.to_s.singularize}_list"
167
168
  return instance_variable_get(variable_name) if instance_variable_get(variable_name)
168
169
 
169
- instance_variable_set(variable_name, TagList.new(all_tags_on(context).map(&:name)).freeze)
170
+ instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(all_tags_on(context).map(&:name)).freeze)
170
171
  end
171
172
 
172
173
  ##
173
174
  # Returns all tags of a given context
174
175
  def all_tags_on(context)
175
- opts = ["#{Tagging.table_name}.context = ?", context.to_s]
176
- base_tags.where(opts).order("#{Tagging.table_name}.created_at").group("#{Tagging.table_name}.tag_id").all
176
+ tag_table_name = ActsAsTaggableOn::Tag.table_name
177
+ tagging_table_name = ActsAsTaggableOn::Tagging.table_name
178
+
179
+ opts = ["#{tagging_table_name}.context = ?", context.to_s]
180
+ base_tags.where(opts).order("max(#{tagging_table_name}.created_at)").group("#{tag_table_name}.id, #{tag_table_name}.name").all
177
181
  end
178
182
 
179
183
  ##
180
184
  # Returns all tags that are not owned of a given context
181
185
  def tags_on(context)
182
- base_tags.where(["#{Tagging.table_name}.context = ? AND #{Tagging.table_name}.tagger_id IS NULL", context.to_s]).all
186
+ base_tags.where(["#{ActsAsTaggableOn::Tagging.table_name}.context = ? AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id IS NULL", context.to_s]).all
183
187
  end
184
188
 
185
189
  def set_tag_list_on(context, new_list)
186
190
  add_custom_context(context)
187
191
 
188
192
  variable_name = "@#{context.to_s.singularize}_list"
189
- instance_variable_set(variable_name, TagList.from(new_list))
193
+ instance_variable_set(variable_name, ActsAsTaggableOn::TagList.from(new_list))
190
194
  end
191
195
 
192
196
  def tagging_contexts
193
197
  custom_contexts + self.class.tag_types.map(&:to_s)
194
198
  end
195
199
 
196
- def reload
200
+ def reload(*args)
197
201
  self.class.tag_types.each do |context|
198
202
  instance_variable_set("@#{context.to_s.singularize}_list", nil)
199
203
  instance_variable_set("@all_#{context.to_s.singularize}_list", nil)
200
204
  end
201
205
 
202
- super
206
+ super(*args)
203
207
  end
204
208
 
205
209
  def save_tags
@@ -209,7 +213,7 @@ module ActsAsTaggableOn::Taggable
209
213
  tag_list = tag_list_cache_on(context).uniq
210
214
 
211
215
  # Find existing tags or create non-existing tags:
212
- tag_list = Tag.find_or_create_all_with_like_by_name(tag_list)
216
+ tag_list = ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name(tag_list)
213
217
 
214
218
  current_tags = tags_on(context)
215
219
  old_tags = current_tags - tag_list
@@ -221,7 +225,7 @@ module ActsAsTaggableOn::Taggable
221
225
 
222
226
  if old_taggings.present?
223
227
  # Destroy old taggings:
224
- Tagging.destroy_all :id => old_taggings.map(&:id)
228
+ ActsAsTaggableOn::Tagging.destroy_all :id => old_taggings.map(&:id)
225
229
  end
226
230
 
227
231
  # Create new taggings:
@@ -30,9 +30,9 @@ 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
+ base_tags.where([%(#{ActsAsTaggableOn::Tagging.table_name}.context = ? AND
34
+ #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = ? AND
35
+ #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = ?), context.to_s, owner.id, owner.class.to_s]).all
36
36
  end
37
37
 
38
38
  def cached_owned_tag_list_on(context)
@@ -46,7 +46,7 @@ module ActsAsTaggableOn::Taggable
46
46
  cache = cached_owned_tag_list_on(context)
47
47
  cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
48
48
 
49
- cache[owner] ||= TagList.new(*owner_tags_on(owner, context).map(&:name))
49
+ cache[owner] ||= ActsAsTaggableOn::TagList.new(*owner_tags_on(owner, context).map(&:name))
50
50
  end
51
51
 
52
52
  def set_owner_tag_list_on(owner, context, new_list)
@@ -55,22 +55,22 @@ module ActsAsTaggableOn::Taggable
55
55
  cache = cached_owned_tag_list_on(context)
56
56
  cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
57
57
 
58
- cache[owner] = TagList.from(new_list)
58
+ cache[owner] = ActsAsTaggableOn::TagList.from(new_list)
59
59
  end
60
60
 
61
- def reload
61
+ def reload(*args)
62
62
  self.class.tag_types.each do |context|
63
63
  instance_variable_set("@owned_#{context}_list", nil)
64
64
  end
65
65
 
66
- super
66
+ super(*args)
67
67
  end
68
68
 
69
69
  def save_owned_tags
70
70
  tagging_contexts.each do |context|
71
71
  cached_owned_tag_list_on(context).each do |owner, tag_list|
72
72
  # Find existing tags or create non-existing tags:
73
- tag_list = Tag.find_or_create_all_with_like_by_name(tag_list.uniq)
73
+ tag_list = ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name(tag_list.uniq)
74
74
 
75
75
  owned_tags = owner_tags_on(owner, context)
76
76
  old_tags = owned_tags - tag_list
@@ -78,13 +78,13 @@ module ActsAsTaggableOn::Taggable
78
78
 
79
79
  # Find all taggings that belong to the taggable (self), are owned by the owner,
80
80
  # 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
81
+ old_taggings = ActsAsTaggableOn::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
84
84
 
85
85
  if old_taggings.present?
86
86
  # Destroy old taggings:
87
- Tagging.destroy_all(:id => old_taggings.map(&:id))
87
+ ActsAsTaggableOn::Tagging.destroy_all(:id => old_taggings.map(&:id))
88
88
  end
89
89
 
90
90
  # Create new taggings:
@@ -3,6 +3,7 @@ module ActsAsTaggableOn::Taggable
3
3
  def self.included(base)
4
4
  base.send :include, ActsAsTaggableOn::Taggable::Related::InstanceMethods
5
5
  base.extend ActsAsTaggableOn::Taggable::Related::ClassMethods
6
+ base.initialize_acts_as_taggable_on_related
6
7
  end
7
8
 
8
9
  module ClassMethods
@@ -41,9 +42,9 @@ module ActsAsTaggableOn::Taggable
41
42
 
42
43
  exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
43
44
 
44
- klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
45
- :from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
46
- :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],
45
+ klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{ActsAsTaggableOn::Tag.table_name}.id) AS count",
46
+ :from => "#{klass.table_name}, #{ActsAsTaggableOn::Tag.table_name}, #{ActsAsTaggableOn::Tagging.table_name}",
47
+ :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],
47
48
  :group => grouped_column_names_for(klass),
48
49
  :order => "count DESC" }.update(options))
49
50
  end
@@ -53,9 +54,9 @@ module ActsAsTaggableOn::Taggable
53
54
 
54
55
  exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
55
56
 
56
- klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
57
- :from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
58
- :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],
57
+ klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{ActsAsTaggableOn::Tag.table_name}.id) AS count",
58
+ :from => "#{klass.table_name}, #{ActsAsTaggableOn::Tag.table_name}, #{ActsAsTaggableOn::Tagging.table_name}",
59
+ :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],
59
60
  :group => grouped_column_names_for(klass),
60
61
  :order => "count DESC" }.update(options))
61
62
  end
@@ -34,8 +34,8 @@ module ActsAsTaggableOn
34
34
  class_inheritable_reader(:tag_types)
35
35
 
36
36
  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
37
+ has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "ActsAsTaggableOn::Tagging"
38
+ has_many :base_tags, :through => :taggings, :source => :tag, :class_name => "ActsAsTaggableOn::Tag"
39
39
 
40
40
  def self.taggable?
41
41
  true
@@ -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 'mysql'
8
+ gem 'pg'
@@ -0,0 +1,44 @@
1
+ module ActsAsTaggableOn
2
+ module Taggable
3
+ module PostgreSQL
4
+ def self.included(base)
5
+ base.send :include, ActsAsTaggableOn::Taggable::PostgreSQL::InstanceMethods
6
+ base.extend ActsAsTaggableOn::Taggable::PostgreSQL::ClassMethods
7
+
8
+ ActsAsTaggableOn::Tag.class_eval do
9
+ def self.named(name)
10
+ where(["name ILIKE ?", name])
11
+ end
12
+
13
+ def self.named_any(list)
14
+ where(list.map { |tag| sanitize_sql(["name ILIKE ?", tag.to_s]) }.join(" OR "))
15
+ end
16
+
17
+ def self.named_like(name)
18
+ where(["name ILIKE ?", "%#{name}%"])
19
+ end
20
+
21
+ def self.named_like_any(list)
22
+ where(list.map { |tag| sanitize_sql(["name ILIKE ?", "%#{tag.to_s}%"]) }.join(" OR "))
23
+ end
24
+ end
25
+ end
26
+
27
+ module InstanceMethods
28
+ end
29
+
30
+ module ClassMethods
31
+ # all column names are necessary for PostgreSQL group clause
32
+ def grouped_column_names_for(*objects)
33
+ object = objects.shift
34
+ columns = object.column_names.map { |column| "#{object.table_name}.#{column}" }
35
+ columns << objects.map do |object|
36
+ "#{object.table_name}.created_at"
37
+ end.flatten
38
+
39
+ columns.flatten.join(", ")
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end