acts-as-taggable-on 2.0.0.pre1 → 2.0.0.pre3

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.
data/Gemfile CHANGED
@@ -1,8 +1,6 @@
1
1
  source :gemcutter
2
2
 
3
- gem 'rails', '3.0.0.beta'
4
-
5
- group :test do
6
- gem 'rspec', '2.0.0.beta.1'
7
- gem 'sqlite3-ruby', :require => 'sqlite3'
8
- end
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'
@@ -31,6 +31,16 @@ To install the gem, add this to your config/environment.rb:
31
31
 
32
32
  After that, you can run "rake gems:install" to install the gem if you don't already have it.
33
33
 
34
+ == Rails 3.0
35
+
36
+ Acts As Taggable On is now useable in Rails 3.0, thanks to the excellent work of Szymon Nowak
37
+ and Jelle Vandebeeck. Because backwards compatibility is hard to maintain, their work is available
38
+ in the feature/rails3_compatibility branch.
39
+
40
+ A Rails 3.0 compatible version of the gem is also available:
41
+
42
+ gem install acts-as-taggable-on -v=2.0.0.pre1
43
+
34
44
  === Post Installation (Rails)
35
45
 
36
46
  1. script/generate acts_as_taggable_on_migration
data/Rakefile CHANGED
@@ -1,5 +1,45 @@
1
- gem 'rspec', '2.0.0.beta.1'
2
- require 'rspec/core/rake_task'
1
+ begin
2
+ # Rspec 2.0
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Default: run specs'
6
+ task :default => :spec
7
+ Rspec::Core::RakeTask.new do |t|
8
+ t.pattern = "spec/**/*_spec.rb"
9
+ end
10
+
11
+ Rspec::Core::RakeTask.new('rcov') do |t|
12
+ t.pattern = "spec/**/*_spec.rb"
13
+ t.rcov = true
14
+ t.rcov_opts = ['--exclude', 'spec']
15
+ end
16
+
17
+ rescue LoadError
18
+ # Rspec 1.3.0
19
+ require 'spec/rake/spectask'
20
+
21
+ desc 'Default: run specs'
22
+ task :default => :spec
23
+ Spec::Rake::SpecTask.new do |t|
24
+ t.spec_files = FileList["spec/**/*_spec.rb"]
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new('rcov') do |t|
28
+ t.spec_files = FileList["spec/**/*_spec.rb"]
29
+ t.rcov = true
30
+ t.rcov_opts = ['--exclude', 'spec']
31
+ end
32
+ rescue LoadError
33
+ puts "Rspec not available. Install it with: gem install rspec"
34
+ end
35
+
36
+ namespace 'rails2.3' do
37
+ task :spec do
38
+ gemfile = File.join(File.dirname(__FILE__), 'lib', 'acts_as_taggable_on', 'compatibility', 'Gemfile')
39
+ ENV['BUNDLE_GEMFILE'] = gemfile
40
+ Rake::Task['spec'].invoke
41
+ end
42
+ end
3
43
 
4
44
  begin
5
45
  require 'jeweler'
@@ -14,17 +54,5 @@ begin
14
54
  end
15
55
  Jeweler::GemcutterTasks.new
16
56
  rescue LoadError
17
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
18
- end
19
-
20
- desc 'Default: run specs'
21
- task :default => :spec
22
- Rspec::Core::RakeTask.new do |t|
23
- t.pattern = "spec/**/*_spec.rb"
24
- end
25
-
26
- Rspec::Core::RakeTask.new('rcov') do |t|
27
- t.pattern = "spec/**/*_spec.rb"
28
- t.rcov = true
29
- t.rcov_opts = ['--exclude', 'spec']
30
- end
57
+ puts "Jeweler not available. Install it with: gem install jeweler"
58
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0.pre1
1
+ 2.0.0.pre3
@@ -1,27 +1,30 @@
1
- begin
2
- # Try to require the preresolved locked set of gems.
3
- require File.expand_path("../.bundle/environment", __FILE__)
4
- rescue LoadError
5
- # Fall back on doing an unlocked resolve at runtime.
6
- require "rubygems"
7
- require "bundler"
8
- Bundler.setup
9
- end
10
-
11
- Bundler.require
12
-
13
1
  require "active_record"
14
2
  require "action_view"
15
3
 
16
- require "acts_as_taggable_on/group_helper"
4
+ if ActiveRecord::VERSION::MAJOR < 3
5
+ require "acts_as_taggable_on/compatibility/active_record_backports"
6
+ require "acts_as_taggable_on/compatibility/tag"
7
+ require "acts_as_taggable_on/compatibility/tagging"
8
+ end
9
+
17
10
  require "acts_as_taggable_on/acts_as_taggable_on"
11
+ require "acts_as_taggable_on/acts_as_taggable_on/core"
12
+ require "acts_as_taggable_on/acts_as_taggable_on/aggregate"
13
+ require "acts_as_taggable_on/acts_as_taggable_on/cache"
14
+ require "acts_as_taggable_on/acts_as_taggable_on/ownership"
15
+ require "acts_as_taggable_on/acts_as_taggable_on/related"
16
+
18
17
  require "acts_as_taggable_on/acts_as_tagger"
19
18
  require "acts_as_taggable_on/tag"
20
19
  require "acts_as_taggable_on/tag_list"
21
20
  require "acts_as_taggable_on/tags_helper"
22
21
  require "acts_as_taggable_on/tagging"
23
22
 
24
- ActiveRecord::Base.send :include, ActiveRecord::Acts::TaggableOn
25
- ActiveRecord::Base.send :include, ActiveRecord::Acts::Tagger
23
+ if defined?(ActiveRecord::Base)
24
+ ActiveRecord::Base.extend ActsAsTaggableOn::Taggable
25
+ ActiveRecord::Base.send :include, ActsAsTaggableOn::Tagger
26
+ end
26
27
 
27
- ActionView::Base.send :include, TagsHelper
28
+ if defined?(ActionView::Base)
29
+ ActionView::Base.send :include, TagsHelper
30
+ end
@@ -1,426 +1,41 @@
1
- module ActiveRecord
2
- module Acts
3
- module TaggableOn
4
-
5
- def self.included(base)
6
- base.extend(ClassMethods)
7
- end
8
-
9
- module ClassMethods
10
-
11
- def taggable?
12
- false
13
- end
14
-
15
- def acts_as_taggable
16
- acts_as_taggable_on :tags
17
- end
18
-
19
- def acts_as_taggable_on(*args)
20
- args.flatten! if args
21
- args.compact! if args
22
-
23
- for tag_type in args
24
- tag_type = tag_type.to_s
25
- # use aliased_join_table_name for context condition so that sphinx can join multiple
26
- # tag references from same model without getting an ambiguous column error
27
- class_eval do
28
- has_many "#{tag_type.singularize}_taggings".to_sym, :as => :taggable, :dependent => :destroy,
29
- :include => :tag, :conditions => ['#{aliased_join_table_name || Tagging.table_name rescue Tagging.table_name}.context = ?',tag_type], :class_name => "Tagging"
30
- has_many "#{tag_type}".to_sym, :through => "#{tag_type.singularize}_taggings".to_sym, :source => :tag
31
- end
32
-
33
- class_eval <<-RUBY
34
-
35
- def self.taggable?
36
- true
37
- end
38
-
39
- def self.caching_#{tag_type.singularize}_list?
40
- caching_tag_list_on?("#{tag_type}")
41
- end
42
-
43
- def self.#{tag_type.singularize}_counts(options={})
44
- tag_counts_on('#{tag_type}',options)
45
- end
46
-
47
- def #{tag_type.singularize}_list
48
- tag_list_on('#{tag_type}')
49
- end
50
-
51
- def #{tag_type.singularize}_list=(new_tags)
52
- set_tag_list_on('#{tag_type}',new_tags)
53
- end
54
-
55
- def #{tag_type.singularize}_counts(options = {})
56
- tag_counts_on('#{tag_type}',options)
57
- end
58
-
59
- def #{tag_type}_from(owner)
60
- tag_list_on('#{tag_type}', owner)
61
- end
62
-
63
- def find_related_#{tag_type}(options = {})
64
- related_tags_for('#{tag_type}', self.class, options)
65
- end
66
- alias_method :find_related_on_#{tag_type}, :find_related_#{tag_type}
67
-
68
- def find_related_#{tag_type}_for(klass, options = {})
69
- related_tags_for('#{tag_type}', klass, options)
70
- end
71
-
72
- def find_matching_contexts(search_context, result_context, options = {})
73
- matching_contexts_for(search_context.to_s, result_context.to_s, self.class, options)
74
- end
75
-
76
- def find_matching_contexts_for(klass, search_context, result_context, options = {})
77
- matching_contexts_for(search_context.to_s, result_context.to_s, klass, options)
78
- end
79
-
80
- def top_#{tag_type}(limit = 10)
81
- tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
82
- end
83
-
84
- def self.top_#{tag_type}(limit = 10)
85
- tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
86
- end
87
-
88
- RUBY
89
- end
90
-
91
- if respond_to?(:tag_types)
92
- write_inheritable_attribute( :tag_types, (tag_types + args).uniq )
93
- else
94
- class_eval do
95
- write_inheritable_attribute(:tag_types, args.uniq)
96
- class_inheritable_reader :tag_types
97
-
98
- has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
99
- has_many :base_tags, :class_name => "Tag", :through => :taggings, :source => :tag
100
-
101
- attr_writer :custom_contexts
102
-
103
- before_save :save_cached_tag_list
104
-
105
- after_save:save_tags
106
-
107
- scope :tagged_with, lambda{ |*args|
108
- find_options_for_find_tagged_with(*args)
109
- }
110
- end
111
-
112
- include ActiveRecord::Acts::TaggableOn::InstanceMethods
113
- extend ActiveRecord::Acts::TaggableOn::SingletonMethods
114
- alias_method_chain :reload, :tag_list
115
- end
116
- end
117
-
118
- end
119
-
120
- module SingletonMethods
121
-
122
- include ActiveRecord::Acts::TaggableOn::GroupHelper
123
-
124
- # Pass either a tag string, or an array of strings or tags
125
- #
126
- # Options:
127
- # :any - find models that match any of the given tags
128
- # :exclude - Find models that are not tagged with the given tags
129
- # :match_all - Find models that match all of the given tags, not just one
130
- # :conditions - A piece of SQL conditions to add to the query
131
- # :on - scopes the find to a context
132
- # def find_tagged_with(*args)
133
- # find_options_for_find_tagged_with(*args)
134
- # end
135
-
136
- def caching_tag_list_on?(context)
137
- column_names.include?("cached_#{context.to_s.singularize}_list")
138
- end
139
-
140
- def tag_counts_on(context, options = {})
141
- find_for_tag_counts(options.merge({:on => context.to_s}))
142
- end
143
-
144
- def all_tag_counts(options = {})
145
- find_for_tag_counts(options)
146
- end
147
-
148
- def find_options_for_find_tagged_with(tags, options = {})
149
- tag_list = TagList.from(tags)
150
-
151
- return {} if tag_list.empty?
152
-
153
- joins = []
154
- conditions = []
155
-
156
- context = options.delete(:on)
157
-
158
- if options.delete(:exclude)
159
- tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
160
- 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)})"
161
-
162
- elsif options.delete(:any)
163
- tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
164
- 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)})"
165
-
166
- else
167
- tags = Tag.named_any(tag_list)
168
- return { :conditions => "1 = 0" } unless tags.length == tag_list.length
169
-
170
- tags.each do |tag|
171
- safe_tag = tag.name.gsub(/[^a-zA-Z0-9]/, '')
172
- prefix = "#{safe_tag}_#{rand(1024)}"
173
-
174
- taggings_alias = "#{table_name}_taggings_#{prefix}"
175
-
176
- tagging_join = "JOIN #{Tagging.table_name} #{taggings_alias}" +
177
- " ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
178
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}" +
179
- " AND #{taggings_alias}.tag_id = #{tag.id}"
180
- tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
181
-
182
- joins << tagging_join
183
- end
184
- end
185
-
186
- taggings_alias, tags_alias = "#{table_name}_taggings_group", "#{table_name}_tags_group"
187
-
188
- if options.delete(:match_all)
189
- joins << "LEFT OUTER JOIN #{Tagging.table_name} #{taggings_alias}" +
190
- " ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
191
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
192
-
193
- group = "#{grouped_column_names_for(self)} HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
194
- end
195
-
196
- Tag.joins(joins.join(" ")).group(group).where(conditions.join(" AND ")).readonly(false)
197
-
198
- # { :joins => joins.join(" "),
199
- # :group => group,
200
- # :conditions => conditions.join(" AND "),
201
- # :readonly => false }.update(options)
202
- end
203
-
204
- # Calculate the tag counts for all tags.
205
- #
206
- # Options:
207
- # :start_at - Restrict the tags to those created after a certain time
208
- # :end_at - Restrict the tags to those created before a certain time
209
- # :conditions - A piece of SQL conditions to add to the query
210
- # :limit - The maximum number of tags to return
211
- # :order - A piece of SQL to order by. Eg 'tags.count desc' or 'taggings.created_at desc'
212
- # :at_least - Exclude tags with a frequency less than the given value
213
- # :at_most - Exclude tags with a frequency greater than the given value
214
- # :on - Scope the find to only include a certain context
215
- def find_for_tag_counts(options = {})
216
- options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :on, :id
217
-
218
- start_at = sanitize_sql(["#{Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
219
- end_at = sanitize_sql(["#{Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
220
-
221
- taggable_type = sanitize_sql(["#{Tagging.table_name}.taggable_type = ?", base_class.name])
222
- taggable_id = sanitize_sql(["#{Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
223
- options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
224
-
225
- conditions = [
226
- taggable_type,
227
- taggable_id,
228
- options[:conditions],
229
- start_at,
230
- end_at
231
- ]
232
-
233
- conditions = conditions.compact.join(' AND ')
234
-
235
- joins = ["LEFT OUTER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
236
- joins << sanitize_sql(["AND #{Tagging.table_name}.context = ?",options.delete(:on).to_s]) unless options[:on].nil?
237
- joins << " INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
238
-
239
- unless descends_from_active_record?
240
- # Current model is STI descendant, so add type checking to the join condition
241
- joins << " AND #{table_name}.#{inheritance_column} = '#{name}'"
242
- end
243
-
244
- at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
245
- at_most = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
246
- having = [at_least, at_most].compact.join(' AND ')
247
- group_by = "#{grouped_column_names_for(Tag)} HAVING COUNT(*) > 0"
248
- group_by << " AND #{having}" unless having.blank?
249
-
250
- Tag.select("#{Tag.table_name}.*, COUNT(*) AS count").joins(joins.join(" ")).where(conditions).group(group_by).limit(options[:limit]).order(options[:order])
251
-
252
- end
253
-
254
- def is_taggable?
255
- true
256
- end
257
-
258
- end
259
-
260
- module InstanceMethods
261
-
262
- include ActiveRecord::Acts::TaggableOn::GroupHelper
263
-
264
- def custom_contexts
265
- @custom_contexts ||= []
266
- end
267
-
268
- def is_taggable?
269
- self.class.is_taggable?
270
- end
271
-
272
- def add_custom_context(value)
273
- custom_contexts << value.to_s unless custom_contexts.include?(value.to_s) or self.class.tag_types.map(&:to_s).include?(value.to_s)
274
- end
275
-
276
- def tag_list_on(context, owner = nil)
277
- add_custom_context(context)
278
- cache = tag_list_cache_on(context)
279
- return owner ? cache[owner] : cache[owner] if cache[owner]
280
-
281
- if !owner && self.class.caching_tag_list_on?(context) and !(cached_value = cached_tag_list_on(context)).nil?
282
- cache[owner] = TagList.from(cached_tag_list_on(context))
283
- else
284
- cache[owner] = TagList.new(*tags_on(context, owner).map(&:name))
285
- end
286
- end
287
-
288
- def all_tags_list_on(context)
289
- variable_name = "@all_#{context.to_s.singularize}_list"
290
- return instance_variable_get(variable_name) if instance_variable_get(variable_name)
291
- instance_variable_set(variable_name, TagList.new(all_tags_on(context).map(&:name)).freeze)
292
- end
293
-
294
- def all_tags_on(context)
295
- opts = ["#{Tagging.table_name}.context = ?", context.to_s]
296
- base_tags.where(opts).order("#{Tagging.table_name}.created_at")
297
- end
298
-
299
- def tags_on(context, owner = nil)
300
- if owner
301
- opts = ["#{Tagging.table_name}.context = ? AND #{Tagging.table_name}.tagger_id = ? AND #{Tagging.table_name}.tagger_type = ?", context.to_s, owner.id, owner.class.to_s]
302
- else
303
- opts = ["#{Tagging.table_name}.context = ? AND #{Tagging.table_name}.tagger_id IS NULL", context.to_s]
304
- end
305
- base_tags.where(opts)
306
- end
307
-
308
- def cached_tag_list_on(context)
309
- self["cached_#{context.to_s.singularize}_list"]
310
- end
311
-
312
- def tag_list_cache_on(context)
313
- variable_name = "@#{context.to_s.singularize}_list"
314
- cache = instance_variable_get(variable_name)
315
- instance_variable_set(variable_name, cache = {}) unless cache
316
- cache
317
- end
318
-
319
- def set_tag_list_on(context, new_list, tagger = nil)
320
- tag_list_cache_on(context)[tagger] = TagList.from(new_list)
321
- add_custom_context(context)
322
- end
323
-
324
- def tag_counts_on(context, options={})
325
- self.class.tag_counts_on(context, options.merge(:id => id))
326
- end
327
-
328
- def related_tags_for(context, klass, options = {})
329
- search_conditions = related_search_options(context, klass, options)
330
-
331
- klass.select(search_conditions[:select]).from(search_conditions[:from]).where(search_conditions[:conditions]).group(search_conditions[:group]).order(search_conditions[:order])
332
- end
333
-
334
- def related_search_options(context, klass, options = {})
335
- tags_to_find = tags_on(context).collect { |t| t.name }
336
-
337
- exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
338
-
339
- { :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
340
- :from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
341
- :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],
342
- :group => grouped_column_names_for(klass),
343
- :order => "count DESC"
344
- }.update(options)
345
- end
346
-
347
- def matching_contexts_for(search_context, result_context, klass, options = {})
348
- search_conditions = matching_context_search_options(search_context, result_context, klass, options)
349
-
350
- klass.select(search_conditions[:select]).from(search_conditions[:from]).where(search_conditions[:conditions]).group(search_conditions[:group]).order(search_conditions[:order])
351
- end
352
-
353
- def matching_context_search_options(search_context, result_context, klass, options = {})
354
- tags_to_find = tags_on(search_context).collect { |t| t.name }
355
-
356
- exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
357
-
358
- { :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
359
- :from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
360
- :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],
361
- :group => grouped_column_names_for(klass),
362
- :order => "count DESC"
363
- }.update(options)
364
- end
365
-
366
- def save_cached_tag_list
367
- self.class.tag_types.map(&:to_s).each do |tag_type|
368
- if self.class.send("caching_#{tag_type.singularize}_list?")
369
- self["cached_#{tag_type.singularize}_list"] = tag_list_cache_on(tag_type.singularize).to_a.flatten.compact.join(', ')
370
- end
371
- end
372
- end
373
-
374
- def save_tags
375
- contexts = custom_contexts + self.class.tag_types.map(&:to_s)
376
-
377
- transaction do
378
- contexts.each do |context|
379
- cache = tag_list_cache_on(context)
380
-
381
- cache.each do |owner, list|
382
- new_tags = Tag.find_or_create_all_with_like_by_name(list.uniq)
383
- taggings = Tagging.where({ :taggable_id => self.id, :taggable_type => self.class.base_class.to_s })
384
-
385
- # Destroy old taggings:
386
- if owner
387
- old_tags = tags_on(context, owner) - new_tags
388
- old_taggings = Tagging.where({ :taggable_id => self.id, :taggable_type => self.class.base_class.to_s, :tag_id => old_tags, :tagger_id => owner.id, :tagger_type => owner.class.to_s, :context => context })
389
-
390
- Tagging.destroy_all :id => old_taggings.map(&:id)
391
- else
392
- old_tags = tags_on(context) - new_tags
393
- base_tags.delete(*old_tags)
394
- end
1
+ module ActsAsTaggableOn
2
+ module Taggable
3
+ def taggable?
4
+ false
5
+ end
395
6
 
396
- new_tags.reject! { |tag| taggings.any? { |tagging|
397
- tagging.tag_id == tag.id &&
398
- tagging.tagger_id == (owner ? owner.id : nil) &&
399
- tagging.tagger_type == (owner ? owner.class.to_s : nil) &&
400
- tagging.context == context
401
- }
402
- }
7
+ def acts_as_taggable
8
+ acts_as_taggable_on :tags
9
+ end
403
10
 
404
- # create new taggings:
405
- new_tags.each do |tag|
406
- Tagging.create!(:tag_id => tag.id, :context => context, :tagger => owner, :taggable => self)
407
- end
408
- end
409
- end
410
- end
11
+ def acts_as_taggable_on(*tag_types)
12
+ tag_types = tag_types.to_a.flatten.compact.map(&:to_sym)
411
13
 
412
- true
14
+ if taggable?
15
+ write_inheritable_attribute(:tag_types, (self.tag_types + tag_types).uniq)
16
+ else
17
+ if ::ActiveRecord::VERSION::MAJOR < 3
18
+ include ActsAsTaggableOn::ActiveRecord::Backports
413
19
  end
20
+
21
+ write_inheritable_attribute(:tag_types, tag_types)
22
+ class_inheritable_reader(:tag_types)
23
+
24
+ class_eval do
25
+ has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
26
+ has_many :base_tags, :class_name => "Tag", :through => :taggings, :source => :tag
414
27
 
415
- def reload_with_tag_list(*args)
416
- self.class.tag_types.each do |tag_type|
417
- instance_variable_set("@#{tag_type.to_s.singularize}_list", nil)
28
+ def self.taggable?
29
+ true
418
30
  end
419
-
420
- reload_without_tag_list(*args)
421
31
  end
422
-
423
32
  end
33
+
34
+ include Core
35
+ include Aggregate
36
+ include Cache
37
+ include Ownership
38
+ include Related
424
39
  end
425
40
  end
426
41
  end