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

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ == 2010-02-17
2
+ * Converted the plugin to be compatible with Rails3
3
+
1
4
  == 2009-12-02
2
5
 
3
6
  * PostgreSQL is now supported (via morgoth)
@@ -12,10 +15,10 @@
12
15
  * Removed extraneous down migration cruft (azabaj)
13
16
 
14
17
  == 2008-06-09
15
-
18
+
16
19
  * Added support for Single Table Inheritance
17
20
  * Adding gemspec and rails/init.rb for gemified plugin
18
-
21
+
19
22
  == 2007-12-12
20
23
 
21
24
  * Added ability to use dynamic tag contexts
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :gemcutter
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
@@ -31,16 +31,6 @@ 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
-
44
34
  === Post Installation (Rails)
45
35
 
46
36
  1. script/generate acts_as_taggable_on_migration
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
- require 'spec/rake/spectask'
1
+ gem 'rspec', '2.0.0.beta.1'
2
+ require 'rspec/core/rake_task'
2
3
 
3
4
  begin
4
5
  require 'jeweler'
@@ -18,12 +19,12 @@ end
18
19
 
19
20
  desc 'Default: run specs'
20
21
  task :default => :spec
21
- Spec::Rake::SpecTask.new do |t|
22
- t.spec_files = FileList["spec/**/*_spec.rb"]
22
+ Rspec::Core::RakeTask.new do |t|
23
+ t.pattern = "spec/**/*_spec.rb"
23
24
  end
24
25
 
25
- Spec::Rake::SpecTask.new('rcov') do |t|
26
- t.spec_files = FileList["spec/**/*_spec.rb"]
26
+ Rspec::Core::RakeTask.new('rcov') do |t|
27
+ t.pattern = "spec/**/*_spec.rb"
27
28
  t.rcov = true
28
29
  t.rcov_opts = ['--exclude', 'spec']
29
30
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.9
1
+ 2.0.0.pre1
@@ -1,7 +1,27 @@
1
- require 'acts_as_taggable_on/group_helper'
2
- require 'acts_as_taggable_on/acts_as_taggable_on'
3
- require 'acts_as_taggable_on/acts_as_tagger'
4
- require 'acts_as_taggable_on/tag'
5
- require 'acts_as_taggable_on/tag_list'
6
- require 'acts_as_taggable_on/tags_helper'
7
- require 'acts_as_taggable_on/tagging'
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
+ require "active_record"
14
+ require "action_view"
15
+
16
+ require "acts_as_taggable_on/group_helper"
17
+ require "acts_as_taggable_on/acts_as_taggable_on"
18
+ require "acts_as_taggable_on/acts_as_tagger"
19
+ require "acts_as_taggable_on/tag"
20
+ require "acts_as_taggable_on/tag_list"
21
+ require "acts_as_taggable_on/tags_helper"
22
+ require "acts_as_taggable_on/tagging"
23
+
24
+ ActiveRecord::Base.send :include, ActiveRecord::Acts::TaggableOn
25
+ ActiveRecord::Base.send :include, ActiveRecord::Acts::Tagger
26
+
27
+ ActionView::Base.send :include, TagsHelper
@@ -1,11 +1,13 @@
1
1
  module ActiveRecord
2
2
  module Acts
3
3
  module TaggableOn
4
+
4
5
  def self.included(base)
5
6
  base.extend(ClassMethods)
6
7
  end
7
8
 
8
9
  module ClassMethods
10
+
9
11
  def taggable?
10
12
  false
11
13
  end
@@ -17,6 +19,7 @@ module ActiveRecord
17
19
  def acts_as_taggable_on(*args)
18
20
  args.flatten! if args
19
21
  args.compact! if args
22
+
20
23
  for tag_type in args
21
24
  tag_type = tag_type.to_s
22
25
  # use aliased_join_table_name for context condition so that sphinx can join multiple
@@ -28,6 +31,7 @@ module ActiveRecord
28
31
  end
29
32
 
30
33
  class_eval <<-RUBY
34
+
31
35
  def self.taggable?
32
36
  true
33
37
  end
@@ -43,10 +47,6 @@ module ActiveRecord
43
47
  def #{tag_type.singularize}_list
44
48
  tag_list_on('#{tag_type}')
45
49
  end
46
-
47
- def all_#{tag_type}_list
48
- all_tags_list_on('#{tag_type}')
49
- end
50
50
 
51
51
  def #{tag_type.singularize}_list=(new_tags)
52
52
  set_tag_list_on('#{tag_type}',new_tags)
@@ -72,7 +72,7 @@ module ActiveRecord
72
72
  def find_matching_contexts(search_context, result_context, options = {})
73
73
  matching_contexts_for(search_context.to_s, result_context.to_s, self.class, options)
74
74
  end
75
-
75
+
76
76
  def find_matching_contexts_for(klass, search_context, result_context, options = {})
77
77
  matching_contexts_for(search_context.to_s, result_context.to_s, klass, options)
78
78
  end
@@ -84,8 +84,10 @@ module ActiveRecord
84
84
  def self.top_#{tag_type}(limit = 10)
85
85
  tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
86
86
  end
87
+
87
88
  RUBY
88
89
  end
90
+
89
91
  if respond_to?(:tag_types)
90
92
  write_inheritable_attribute( :tag_types, (tag_types + args).uniq )
91
93
  else
@@ -99,23 +101,26 @@ module ActiveRecord
99
101
  attr_writer :custom_contexts
100
102
 
101
103
  before_save :save_cached_tag_list
102
- after_save :save_tags
103
104
 
104
- if respond_to?(:named_scope)
105
- named_scope :tagged_with, lambda{ |*args|
106
- find_options_for_find_tagged_with(*args)
107
- }
108
- end
105
+ after_save:save_tags
106
+
107
+ scope :tagged_with, lambda{ |*args|
108
+ find_options_for_find_tagged_with(*args)
109
+ }
109
110
  end
110
111
 
111
112
  include ActiveRecord::Acts::TaggableOn::InstanceMethods
112
113
  extend ActiveRecord::Acts::TaggableOn::SingletonMethods
114
+ alias_method_chain :reload, :tag_list
113
115
  end
114
116
  end
117
+
115
118
  end
116
119
 
117
120
  module SingletonMethods
121
+
118
122
  include ActiveRecord::Acts::TaggableOn::GroupHelper
123
+
119
124
  # Pass either a tag string, or an array of strings or tags
120
125
  #
121
126
  # Options:
@@ -124,21 +129,20 @@ module ActiveRecord
124
129
  # :match_all - Find models that match all of the given tags, not just one
125
130
  # :conditions - A piece of SQL conditions to add to the query
126
131
  # :on - scopes the find to a context
127
- def find_tagged_with(*args)
128
- options = find_options_for_find_tagged_with(*args)
129
- options.blank? ? [] : find(:all,options)
130
- end
132
+ # def find_tagged_with(*args)
133
+ # find_options_for_find_tagged_with(*args)
134
+ # end
131
135
 
132
136
  def caching_tag_list_on?(context)
133
137
  column_names.include?("cached_#{context.to_s.singularize}_list")
134
138
  end
135
139
 
136
140
  def tag_counts_on(context, options = {})
137
- Tag.find(:all, find_options_for_tag_counts(options.merge({:on => context.to_s})))
141
+ find_for_tag_counts(options.merge({:on => context.to_s}))
138
142
  end
139
143
 
140
144
  def all_tag_counts(options = {})
141
- Tag.find(:all, find_options_for_tag_counts(options))
145
+ find_for_tag_counts(options)
142
146
  end
143
147
 
144
148
  def find_options_for_find_tagged_with(tags, options = {})
@@ -151,7 +155,6 @@ module ActiveRecord
151
155
 
152
156
  context = options.delete(:on)
153
157
 
154
-
155
158
  if options.delete(:exclude)
156
159
  tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
157
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)})"
@@ -163,7 +166,7 @@ module ActiveRecord
163
166
  else
164
167
  tags = Tag.named_any(tag_list)
165
168
  return { :conditions => "1 = 0" } unless tags.length == tag_list.length
166
-
169
+
167
170
  tags.each do |tag|
168
171
  safe_tag = tag.name.gsub(/[^a-zA-Z0-9]/, '')
169
172
  prefix = "#{safe_tag}_#{rand(1024)}"
@@ -190,10 +193,12 @@ module ActiveRecord
190
193
  group = "#{grouped_column_names_for(self)} HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
191
194
  end
192
195
 
193
- { :joins => joins.join(" "),
194
- :group => group,
195
- :conditions => conditions.join(" AND "),
196
- :readonly => false }.update(options)
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)
197
202
  end
198
203
 
199
204
  # Calculate the tag counts for all tags.
@@ -207,10 +212,9 @@ module ActiveRecord
207
212
  # :at_least - Exclude tags with a frequency less than the given value
208
213
  # :at_most - Exclude tags with a frequency greater than the given value
209
214
  # :on - Scope the find to only include a certain context
210
- def find_options_for_tag_counts(options = {})
215
+ def find_for_tag_counts(options = {})
211
216
  options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :on, :id
212
217
 
213
- scope = scope(:find)
214
218
  start_at = sanitize_sql(["#{Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
215
219
  end_at = sanitize_sql(["#{Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
216
220
 
@@ -227,58 +231,34 @@ module ActiveRecord
227
231
  ]
228
232
 
229
233
  conditions = conditions.compact.join(' AND ')
230
- conditions = merge_conditions(conditions, scope[:conditions]) if scope
231
234
 
232
235
  joins = ["LEFT OUTER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
233
236
  joins << sanitize_sql(["AND #{Tagging.table_name}.context = ?",options.delete(:on).to_s]) unless options[:on].nil?
234
237
  joins << " INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
235
-
238
+
236
239
  unless descends_from_active_record?
237
240
  # Current model is STI descendant, so add type checking to the join condition
238
241
  joins << " AND #{table_name}.#{inheritance_column} = '#{name}'"
239
242
  end
240
243
 
241
- # Based on a proposed patch by donV to ActiveRecord Base
242
- # This is needed because merge_joins and construct_join are private in ActiveRecord Base
243
- if scope && scope[:joins]
244
- case scope[:joins]
245
- when Array
246
- scope_joins = scope[:joins].flatten
247
- strings = scope_joins.select{|j| j.is_a? String}
248
- joins << strings.join(' ') + " "
249
- symbols = scope_joins - strings
250
- join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, symbols, nil)
251
- joins << " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} "
252
- joins.flatten!
253
- when Symbol, Hash
254
- join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, scope[:joins], nil)
255
- joins << " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} "
256
- when String
257
- joins << scope[:joins]
258
- end
259
- end
260
-
261
244
  at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
262
245
  at_most = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
263
246
  having = [at_least, at_most].compact.join(' AND ')
264
247
  group_by = "#{grouped_column_names_for(Tag)} HAVING COUNT(*) > 0"
265
248
  group_by << " AND #{having}" unless having.blank?
266
249
 
267
- { :select => "#{Tag.table_name}.*, COUNT(*) AS count",
268
- :joins => joins.join(" "),
269
- :conditions => conditions,
270
- :group => group_by,
271
- :limit => options[:limit],
272
- :order => options[:order]
273
- }
250
+ Tag.select("#{Tag.table_name}.*, COUNT(*) AS count").joins(joins.join(" ")).where(conditions).group(group_by).limit(options[:limit]).order(options[:order])
251
+
274
252
  end
275
253
 
276
254
  def is_taggable?
277
255
  true
278
256
  end
257
+
279
258
  end
280
259
 
281
260
  module InstanceMethods
261
+
282
262
  include ActiveRecord::Acts::TaggableOn::GroupHelper
283
263
 
284
264
  def custom_contexts
@@ -288,7 +268,7 @@ module ActiveRecord
288
268
  def is_taggable?
289
269
  self.class.is_taggable?
290
270
  end
291
-
271
+
292
272
  def add_custom_context(value)
293
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)
294
274
  end
@@ -297,40 +277,38 @@ module ActiveRecord
297
277
  add_custom_context(context)
298
278
  cache = tag_list_cache_on(context)
299
279
  return owner ? cache[owner] : cache[owner] if cache[owner]
300
-
280
+
301
281
  if !owner && self.class.caching_tag_list_on?(context) and !(cached_value = cached_tag_list_on(context)).nil?
302
282
  cache[owner] = TagList.from(cached_tag_list_on(context))
303
283
  else
304
284
  cache[owner] = TagList.new(*tags_on(context, owner).map(&:name))
305
285
  end
306
286
  end
307
-
287
+
308
288
  def all_tags_list_on(context)
309
289
  variable_name = "@all_#{context.to_s.singularize}_list"
310
290
  return instance_variable_get(variable_name) if instance_variable_get(variable_name)
311
291
  instance_variable_set(variable_name, TagList.new(all_tags_on(context).map(&:name)).freeze)
312
292
  end
313
-
293
+
314
294
  def all_tags_on(context)
315
- opts = {:conditions => ["#{Tagging.table_name}.context = ?", context.to_s]}
316
- base_tags.find(:all, opts.merge(:order => "#{Tagging.table_name}.created_at"))
295
+ opts = ["#{Tagging.table_name}.context = ?", context.to_s]
296
+ base_tags.where(opts).order("#{Tagging.table_name}.created_at")
317
297
  end
318
298
 
319
299
  def tags_on(context, owner = nil)
320
300
  if owner
321
- opts = {:conditions => ["#{Tagging.table_name}.context = ? AND #{Tagging.table_name}.tagger_id = ? AND #{Tagging.table_name}.tagger_type = ?",
322
- context.to_s, owner.id, owner.class.to_s]}
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]
323
302
  else
324
- opts = {:conditions => ["#{Tagging.table_name}.context = ? AND #{Tagging.table_name}.tagger_id IS NULL", context.to_s]}
303
+ opts = ["#{Tagging.table_name}.context = ? AND #{Tagging.table_name}.tagger_id IS NULL", context.to_s]
325
304
  end
326
-
327
- base_tags.find(:all, opts)
305
+ base_tags.where(opts)
328
306
  end
329
307
 
330
308
  def cached_tag_list_on(context)
331
309
  self["cached_#{context.to_s.singularize}_list"]
332
310
  end
333
-
311
+
334
312
  def tag_list_cache_on(context)
335
313
  variable_name = "@#{context.to_s.singularize}_list"
336
314
  cache = instance_variable_get(variable_name)
@@ -350,7 +328,7 @@ module ActiveRecord
350
328
  def related_tags_for(context, klass, options = {})
351
329
  search_conditions = related_search_options(context, klass, options)
352
330
 
353
- klass.find(:all, search_conditions)
331
+ klass.select(search_conditions[:select]).from(search_conditions[:from]).where(search_conditions[:conditions]).group(search_conditions[:group]).order(search_conditions[:order])
354
332
  end
355
333
 
356
334
  def related_search_options(context, klass, options = {})
@@ -365,13 +343,13 @@ module ActiveRecord
365
343
  :order => "count DESC"
366
344
  }.update(options)
367
345
  end
368
-
346
+
369
347
  def matching_contexts_for(search_context, result_context, klass, options = {})
370
348
  search_conditions = matching_context_search_options(search_context, result_context, klass, options)
371
349
 
372
- klass.find(:all, search_conditions)
350
+ klass.select(search_conditions[:select]).from(search_conditions[:from]).where(search_conditions[:conditions]).group(search_conditions[:group]).order(search_conditions[:order])
373
351
  end
374
-
352
+
375
353
  def matching_context_search_options(search_context, result_context, klass, options = {})
376
354
  tags_to_find = tags_on(search_context).collect { |t| t.name }
377
355
 
@@ -388,7 +366,7 @@ module ActiveRecord
388
366
  def save_cached_tag_list
389
367
  self.class.tag_types.map(&:to_s).each do |tag_type|
390
368
  if self.class.send("caching_#{tag_type.singularize}_list?")
391
- send(:"cached_#{tag_type.singularize}_list=", tag_list_cache_on(tag_type.singularize).to_a.flatten.compact.join(', '))
369
+ self["cached_#{tag_type.singularize}_list"] = tag_list_cache_on(tag_type.singularize).to_a.flatten.compact.join(', ')
392
370
  end
393
371
  end
394
372
  end
@@ -399,28 +377,22 @@ module ActiveRecord
399
377
  transaction do
400
378
  contexts.each do |context|
401
379
  cache = tag_list_cache_on(context)
402
-
380
+
403
381
  cache.each do |owner, list|
404
382
  new_tags = Tag.find_or_create_all_with_like_by_name(list.uniq)
405
- taggings = Tagging.find(:all, :conditions => { :taggable_id => self.id, :taggable_type => self.class.base_class.to_s })
383
+ taggings = Tagging.where({ :taggable_id => self.id, :taggable_type => self.class.base_class.to_s })
406
384
 
407
385
  # Destroy old taggings:
408
386
  if owner
409
387
  old_tags = tags_on(context, owner) - new_tags
410
- old_taggings = Tagging.find(:all, :conditions => { :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 })
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 })
411
389
 
412
- if old_taggings.present?
413
- Tagging.destroy_all :id => old_taggings.map(&:id)
414
- end
390
+ Tagging.destroy_all :id => old_taggings.map(&:id)
415
391
  else
416
392
  old_tags = tags_on(context) - new_tags
417
- old_taggings = Tagging.find(:all, :conditions => { :taggable_id => self.id, :taggable_type => self.class.base_class.to_s, :tag_id => old_tags, :tagger_id => nil, :tagger_type => nil, :context => context })
418
-
419
- if old_taggings.present?
420
- Tagging.destroy_all :id => old_taggings.map(&:id)
421
- end
393
+ base_tags.delete(*old_tags)
422
394
  end
423
-
395
+
424
396
  new_tags.reject! { |tag| taggings.any? { |tagging|
425
397
  tagging.tag_id == tag.id &&
426
398
  tagging.tagger_id == (owner ? owner.id : nil) &&
@@ -428,17 +400,26 @@ module ActiveRecord
428
400
  tagging.context == context
429
401
  }
430
402
  }
431
-
403
+
432
404
  # create new taggings:
433
405
  new_tags.each do |tag|
434
406
  Tagging.create!(:tag_id => tag.id, :context => context, :tagger => owner, :taggable => self)
435
407
  end
436
408
  end
437
409
  end
438
- end
410
+ end
439
411
 
440
412
  true
441
413
  end
414
+
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)
418
+ end
419
+
420
+ reload_without_tag_list(*args)
421
+ end
422
+
442
423
  end
443
424
  end
444
425
  end