acts-as-taggable-on 2.0.6 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +9 -0
  4. data/CHANGELOG +10 -0
  5. data/Gemfile +2 -9
  6. data/Guardfile +5 -0
  7. data/README.rdoc +89 -66
  8. data/Rakefile +9 -55
  9. data/acts-as-taggable-on.gemspec +28 -0
  10. data/lib/acts-as-taggable-on/version.rb +4 -0
  11. data/lib/acts-as-taggable-on.rb +33 -4
  12. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +4 -4
  13. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +38 -43
  14. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +146 -38
  15. data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +37 -0
  16. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +36 -11
  17. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +23 -15
  18. data/lib/acts_as_taggable_on/tag.rb +16 -13
  19. data/lib/acts_as_taggable_on/tag_list.rb +13 -12
  20. data/lib/acts_as_taggable_on/taggable.rb +102 -0
  21. data/lib/acts_as_taggable_on/{acts_as_tagger.rb → tagger.rb} +3 -3
  22. data/lib/acts_as_taggable_on/tagging.rb +12 -2
  23. data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
  24. data/lib/acts_as_taggable_on/utils.rb +34 -0
  25. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +9 -2
  26. data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +3 -1
  27. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +300 -54
  28. data/spec/acts_as_taggable_on/tag_list_spec.rb +84 -61
  29. data/spec/acts_as_taggable_on/tag_spec.rb +51 -13
  30. data/spec/acts_as_taggable_on/taggable_spec.rb +261 -34
  31. data/spec/acts_as_taggable_on/tagger_spec.rb +36 -15
  32. data/spec/acts_as_taggable_on/tagging_spec.rb +2 -5
  33. data/spec/acts_as_taggable_on/tags_helper_spec.rb +16 -0
  34. data/spec/acts_as_taggable_on/utils_spec.rb +21 -0
  35. data/spec/database.yml.sample +4 -2
  36. data/spec/generators/acts_as_taggable_on/migration/migration_generator_spec.rb +22 -0
  37. data/spec/models.rb +19 -1
  38. data/spec/schema.rb +18 -0
  39. data/spec/spec_helper.rb +30 -7
  40. data/uninstall.rb +1 -0
  41. metadata +137 -51
  42. data/VERSION +0 -1
  43. data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +0 -7
  44. data/generators/acts_as_taggable_on_migration/templates/migration.rb +0 -29
  45. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +0 -53
  46. data/lib/acts_as_taggable_on/compatibility/Gemfile +0 -8
  47. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +0 -17
  48. data/lib/acts_as_taggable_on/compatibility/postgresql.rb +0 -44
  49. data/spec/database.yml +0 -17
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.log
2
+ *.sqlite3
3
+ /pkg/*
4
+ .bundle
5
+ .rvmrc
6
+ Gemfile.lock
7
+ spec/database.yml
8
+ tmp*.sw?
9
+ *.sw?
10
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --backtrace
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ script: "cp spec/database.yml.sample spec/database.yml && bundle install && bundle exec rake"
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ env:
7
+ - DB=sqlite3
8
+ - DB=mysql
9
+ - DB=postgresql
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ == 2011-08-21
2
+ * escape _ and % for mysql and postgres (@tilsammans)
3
+ * Now depends on mysql2 gem
4
+ * tagged_with :any is chainable now (@jeffreyiacono)
5
+ * tagged_with(nil) returns scoped object
6
+ * Case-insensitivity for TaggedModel.tagged_with for PostgreSQL database
7
+ * tagged_with(' ') returns scoped object
8
+ * remove warning for rails 3.1 about class_inheritable_attribute
9
+ * use ActiveRecord migration_number to avoid clashs (@atd)
10
+
1
11
  == 2010-02-17
2
12
  * Converted the plugin to be compatible with Rails3
3
13
 
data/Gemfile CHANGED
@@ -1,10 +1,3 @@
1
- source :gemcutter
1
+ source 'http://rubygems.org'
2
+ gemspec
2
3
 
3
- # Rails 3.0
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/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb})
3
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/README.rdoc CHANGED
@@ -1,4 +1,5 @@
1
1
  = ActsAsTaggableOn
2
+ {<img src="https://secure.travis-ci.org/mbleigh/acts-as-taggable-on.png" />}[http://travis-ci.org/mbleigh/acts-as-taggable-on]
2
3
 
3
4
  This plugin was originally based on Acts as Taggable on Steroids by Jonathan Viney.
4
5
  It has evolved substantially since that point, but all credit goes to him for the
@@ -15,37 +16,15 @@ was used.
15
16
 
16
17
  == Installation
17
18
 
18
- === Rails 2.3.x
19
+ === Rails 2.x
19
20
 
20
- Acts As Taggable On is tested to work in Rails 2.3.5.
21
+ Not supported any more! It is time for update guys.
21
22
 
22
- ==== Plugin
23
-
24
- Acts As Taggable On is available both as a gem and as a traditional plugin. For the
25
- traditional plugin you can install like so:
26
-
27
- script/plugin install git://github.com/mbleigh/acts-as-taggable-on.git
28
-
29
- Acts As Taggable On is also available as a gem plugin using Rails 2.1's gem dependencies.
30
- To install the gem, add this to your config/environment.rb:
31
-
32
- config.gem "acts-as-taggable-on", :source => "http://gemcutter.org", :version => '2.0.0.rc1'
33
-
34
- After that, you can run "rake gems:install" to install the gem if you don't already have it.
35
-
36
- ==== Post Installation
37
-
38
- 1. script/generate acts_as_taggable_on_migration
39
- 2. rake db:migrate
40
-
41
- === Rails 3.0
42
-
43
- Acts As Taggable On is now useable in Rails 3.0, thanks to the excellent work of Szymon Nowak
44
- and Jelle Vandebeeck.
23
+ === Rails 3.x
45
24
 
46
25
  To use it, add it to your Gemfile:
47
-
48
- gem 'acts-as-taggable-on'
26
+
27
+ gem 'acts-as-taggable-on', '~> 2.2.2'
49
28
 
50
29
  ==== Post Installation
51
30
 
@@ -54,20 +33,11 @@ To use it, add it to your Gemfile:
54
33
 
55
34
  == Testing
56
35
 
57
- Acts As Taggable On uses RSpec for its test coverage. Inside the plugin
58
- directory, you can run the specs for RoR 3.0.0 with:
36
+ Acts As Taggable On uses RSpec for its test coverage. Inside the gem
37
+ directory, you can run the specs for RoR 3.x with:
59
38
 
60
39
  rake spec
61
40
 
62
- If you want to test the plugin for Rails 2.3.x, use:
63
-
64
- rake rails2.3:spec
65
-
66
- If you already have RSpec on your application, the specs will run while using:
67
-
68
- rake spec:plugins
69
-
70
-
71
41
  == Usage
72
42
 
73
43
  class User < ActiveRecord::Base
@@ -89,6 +59,25 @@ rake spec:plugins
89
59
  User.skill_counts # => [<Tag name="joking" count=2>,<Tag name="clowning" count=1>...]
90
60
  @frankie.skill_counts
91
61
 
62
+ To preserve the order in which tags are created use acts_as_ordered_taggable:
63
+
64
+ class User < ActiveRecord::Base
65
+ # Alias for <tt>acts_as_ordered_taggable_on :tags</tt>:
66
+ acts_as_ordered_taggable
67
+ acts_as_ordered_taggable_on :skills, :interests
68
+ end
69
+
70
+ @user = User.new(:name => "Bobby")
71
+ @user.tag_list = "east, south"
72
+ @user.save
73
+
74
+ @user.tag_list = "north, east, south, west"
75
+ @user.save
76
+
77
+ @user.reload
78
+ @user.tag_list # => ["north", "east", "south", "west"]
79
+
80
+
92
81
  === Finding Tagged Objects
93
82
 
94
83
  Acts As Taggable On utilizes named_scopes to create an association for tags.
@@ -96,23 +85,33 @@ This way you can mix and match to filter down your results, and it also improves
96
85
  compatibility with the will_paginate gem:
97
86
 
98
87
  class User < ActiveRecord::Base
99
- acts_as_taggable_on :tags
100
- named_scope :by_join_date, :order => "created_at DESC"
88
+ acts_as_taggable_on :tags, :skills
89
+ scope :by_join_date, order("created_at DESC")
101
90
  end
102
91
 
103
92
  User.tagged_with("awesome").by_date
104
93
  User.tagged_with("awesome").by_date.paginate(:page => params[:page], :per_page => 20)
105
94
 
106
95
  # Find a user with matching all tags, not just one
107
- User.tagged_with(["awesome", "cool"], :match_all => :true)
108
-
96
+ User.tagged_with(["awesome", "cool"], :match_all => true)
97
+
109
98
  # Find a user with any of the tags:
110
99
  User.tagged_with(["awesome", "cool"], :any => true)
111
100
 
101
+ # Find a user that not tags with awesome or cool:
102
+ User.tagged_with(["awesome", "cool"], :exclude => true)
103
+
104
+ # Find a user with any of tags based on context:
105
+ User.tagged_with(['awesome, cool'], :on => :tags, :any => true).tagged_with(['smart', 'shy'], :on => :skills, :any => true)
106
+
107
+ You can also use :wild => true option along with :any or :exclude option. It will looking for %awesome% and %cool% in sql.
108
+
109
+ Tip: User.tagged_with([]) or '' will return [], but not all records.
110
+
112
111
  === Relationships
113
112
 
114
113
  You can find objects of the same type based on similar tags on certain contexts.
115
- Also, objects will be returned in descending order based on the total number of
114
+ Also, objects will be returned in descending order based on the total number of
116
115
  matched tags.
117
116
 
118
117
  @bobby = User.find_by_name("Bobby")
@@ -125,8 +124,8 @@ matched tags.
125
124
  @tom.skill_list # => ["hacking", "jogging", "diving"]
126
125
 
127
126
  @tom.find_related_skills # => [<User name="Bobby">,<User name="Frankie">]
128
- @bobby.find_related_skills # => [<User name="Tom">]
129
- @frankie.find_related_skills # => [<User name="Tom">]
127
+ @bobby.find_related_skills # => [<User name="Tom">]
128
+ @frankie.find_related_skills # => [<User name="Tom">]
130
129
 
131
130
  === Dynamic Tag Contexts
132
131
 
@@ -156,8 +155,25 @@ Tags can have owners:
156
155
  @some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations)
157
156
  @some_user.owned_taggings
158
157
  @some_user.owned_tags
159
- @some_photo.locations_from(@some_user)
160
-
158
+ @some_photo.locations_from(@some_user) # => ["paris", "normandy"]
159
+ @some_photo.owner_tags_on(@some_user, :locations) # => [#<ActsAsTaggableOn::Tag id: 1, name: "paris">...]
160
+ @some_photo.owner_tags_on(nil, :locations) # => Ownerships equivalent to saying @some_photo.locations
161
+ @some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations, :skip_save => true) #won't save @some_photo object
162
+
163
+ === Dirty objects
164
+
165
+ @bobby = User.find_by_name("Bobby")
166
+ @bobby.skill_list # => ["jogging", "diving"]
167
+
168
+ @boddy.skill_list_changed? #=> false
169
+ @boddy.changes #=> {}
170
+
171
+ @bobby.skill_list = "swimming"
172
+ @bobby.changes.should == {"skill_list"=>["jogging, diving", ["swimming"]]}
173
+ @boddy.skill_list_changed? #=> true
174
+
175
+ @bobby.skill_list_change.should == ["jogging, diving", ["swimming"]]
176
+
161
177
  === Tag cloud calculations
162
178
 
163
179
  To construct tag clouds, the frequency of each tag needs to be calculated.
@@ -198,24 +214,31 @@ CSS:
198
214
  .css3 { font-size: 1.4em; }
199
215
  .css4 { font-size: 1.6em; }
200
216
 
217
+ == Configuration
218
+
219
+ If you would like to remove unused tag objects after removing taggings, add
220
+
221
+ ActsAsTaggableOn.remove_unused_tags = true
222
+
223
+ If you want force tags to be saved downcased:
224
+
225
+ ActsAsTaggableOn.force_lowercase = true
226
+
227
+ If you want tags to be saved parametrized (you can redefine to_param as well):
228
+
229
+ ActsAsTaggableOn.force_parameterize = true
230
+
231
+
201
232
  == Contributors
202
233
 
203
- * TomEric (i76) - Maintainer
204
- * Michael Bleigh - Original Author
205
- * Szymon Nowak - Rails 3.0 compatibility
206
- * Jelle Vandebeeck - Rails 3.0 compatibility
207
- * Brendan Lim - Related Objects
208
- * Pradeep Elankumaran - Taggers
209
- * Sinclair Bain - Patch King
210
-
211
- === Patch Contributors
212
-
213
- * tristanzdunn - Related objects of other classes
214
- * azabaj - Fixed migrate down
215
- * Peter Cooper - named_scope fix
216
- * slainer68 - STI fix
217
- * harrylove - migration instructions and fix-ups
218
- * lawrencepit - cached tag work
219
- * sobrinho - fixed tag_cloud helper
220
-
221
- Copyright (c) 2007-2010 Michael Bleigh (http://mbleigh.com/) and Intridea Inc. (http://intridea.com/), released under the MIT license
234
+ We have a long list of valued contributors. {Check them all}[https://github.com/mbleigh/acts-as-taggable-on/contributors]
235
+
236
+ == Maintainers
237
+
238
+ * Artem Kramarenko (artemk)
239
+
240
+ == Author
241
+
242
+ * Michael Bleigh
243
+
244
+ Copyright (c) 2007-2011 Michael Bleigh (http://mbleigh.com/) and Intridea Inc. (http://intridea.com/), released under the MIT license
data/Rakefile CHANGED
@@ -1,59 +1,13 @@
1
- begin
2
- # Rspec 1.3.0
3
- require 'spec/rake/spectask'
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup :default, :development
4
4
 
5
- desc 'Default: run specs'
6
- task :default => :spec
7
- Spec::Rake::SpecTask.new do |t|
8
- t.spec_files = FileList["spec/**/*_spec.rb"]
9
- end
5
+ desc 'Default: run specs'
6
+ task :default => :spec
10
7
 
11
- Spec::Rake::SpecTask.new('rcov') do |t|
12
- t.spec_files = FileList["spec/**/*_spec.rb"]
13
- t.rcov = true
14
- t.rcov_opts = ['--exclude', 'spec']
15
- end
16
-
17
- rescue LoadError
18
- # Rspec 2.0
19
- require 'rspec/core/rake_task'
20
-
21
- desc 'Default: run specs'
22
- task :default => :spec
23
- Rspec::Core::RakeTask.new do |t|
24
- t.pattern = "spec/**/*_spec.rb"
25
- end
26
-
27
- Rspec::Core::RakeTask.new('rcov') do |t|
28
- t.pattern = "spec/**/*_spec.rb"
29
- t.rcov = true
30
- t.rcov_opts = ['--exclude', 'spec']
31
- end
32
-
33
- rescue LoadError
34
- puts "Rspec not available. Install it with: gem install rspec"
35
- end
36
-
37
- namespace 'rails2.3' do
38
- task :spec do
39
- gemfile = File.join(File.dirname(__FILE__), 'lib', 'acts_as_taggable_on', 'compatibility', 'Gemfile')
40
- ENV['BUNDLE_GEMFILE'] = gemfile
41
- Rake::Task['spec'].invoke
42
- end
8
+ require 'rspec/core/rake_task'
9
+ RSpec::Core::RakeTask.new do |t|
10
+ t.pattern = "spec/**/*_spec.rb"
43
11
  end
44
12
 
45
- begin
46
- require 'jeweler'
47
- Jeweler::Tasks.new do |gemspec|
48
- gemspec.name = "acts-as-taggable-on"
49
- gemspec.summary = "ActsAsTaggableOn is a tagging plugin for Rails that provides multiple tagging contexts on a single model."
50
- gemspec.description = "With ActsAsTaggableOn, you could tag a single model on several contexts, such as skills, interests, and awards. It also provides other advanced functionality."
51
- gemspec.email = "michael@intridea.com"
52
- gemspec.homepage = "http://github.com/mbleigh/acts-as-taggable-on"
53
- gemspec.authors = ["Michael Bleigh"]
54
- gemspec.files = FileList["[A-Z]*", "{generators,lib,spec,rails}/**/*"] - FileList["**/*.log"]
55
- end
56
- Jeweler::GemcutterTasks.new
57
- rescue LoadError
58
- puts "Jeweler not available. Install it with: gem install jeweler"
59
- end
13
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,28 @@
1
+ $:.push File.dirname(__FILE__) + '/lib'
2
+ require 'acts-as-taggable-on/version'
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = %q{acts-as-taggable-on}
6
+ gem.authors = ["Michael Bleigh"]
7
+ gem.date = %q{2012-01-06}
8
+ gem.description = %q{With ActsAsTaggableOn, you can tag a single model on several contexts, such as skills, interests, and awards. It also provides other advanced functionality.}
9
+ gem.summary = "Advanced tagging for Rails."
10
+ gem.email = %q{michael@intridea.com}
11
+ gem.homepage = ''
12
+
13
+ gem.add_runtime_dependency 'rails', '~> 3.0'
14
+ gem.add_development_dependency 'rspec', '~> 2.6'
15
+ gem.add_development_dependency 'ammeter', '~> 0.1.3'
16
+ gem.add_development_dependency 'sqlite3'
17
+ gem.add_development_dependency 'mysql2', '~> 0.3.7'
18
+ gem.add_development_dependency 'pg'
19
+ gem.add_development_dependency 'guard'
20
+ gem.add_development_dependency 'guard-rspec'
21
+
22
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ gem.files = `git ls-files`.split("\n")
24
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ gem.name = "acts-as-taggable-on"
26
+ gem.require_paths = ['lib']
27
+ gem.version = ActsAsTaggableOn::VERSION
28
+ end
@@ -0,0 +1,4 @@
1
+ module ActsAsTaggableOn
2
+ VERSION = '2.3.0'
3
+ end
4
+
@@ -1,18 +1,45 @@
1
1
  require "active_record"
2
+ require "active_record/version"
2
3
  require "action_view"
3
4
 
5
+ require "digest/sha1"
6
+
4
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
5
8
 
6
- require "acts_as_taggable_on/compatibility/active_record_backports" if ActiveRecord::VERSION::MAJOR < 3
9
+ module ActsAsTaggableOn
10
+ mattr_accessor :delimiter
11
+ @@delimiter = ','
12
+
13
+ mattr_accessor :force_lowercase
14
+ @@force_lowercase = false
15
+
16
+ mattr_accessor :force_parameterize
17
+ @@force_parameterize = false
18
+
19
+ mattr_accessor :remove_unused_tags
20
+ self.remove_unused_tags = false
21
+
22
+ def self.glue
23
+ @@delimiter.ends_with?(" ") ? @@delimiter : "#{@@delimiter} "
24
+ end
25
+
26
+ def self.setup
27
+ yield self
28
+ end
29
+ end
30
+
31
+
32
+ require "acts_as_taggable_on/utils"
7
33
 
8
- require "acts_as_taggable_on/acts_as_taggable_on"
34
+ require "acts_as_taggable_on/taggable"
9
35
  require "acts_as_taggable_on/acts_as_taggable_on/core"
10
36
  require "acts_as_taggable_on/acts_as_taggable_on/collection"
11
37
  require "acts_as_taggable_on/acts_as_taggable_on/cache"
12
38
  require "acts_as_taggable_on/acts_as_taggable_on/ownership"
13
39
  require "acts_as_taggable_on/acts_as_taggable_on/related"
40
+ require "acts_as_taggable_on/acts_as_taggable_on/dirty"
14
41
 
15
- require "acts_as_taggable_on/acts_as_tagger"
42
+ require "acts_as_taggable_on/tagger"
16
43
  require "acts_as_taggable_on/tag"
17
44
  require "acts_as_taggable_on/tag_list"
18
45
  require "acts_as_taggable_on/tags_helper"
@@ -20,6 +47,7 @@ require "acts_as_taggable_on/tagging"
20
47
 
21
48
  $LOAD_PATH.shift
22
49
 
50
+
23
51
  if defined?(ActiveRecord::Base)
24
52
  ActiveRecord::Base.extend ActsAsTaggableOn::Taggable
25
53
  ActiveRecord::Base.send :include, ActsAsTaggableOn::Tagger
@@ -27,4 +55,5 @@ end
27
55
 
28
56
  if defined?(ActionView::Base)
29
57
  ActionView::Base.send :include, ActsAsTaggableOn::TagsHelper
30
- end
58
+ end
59
+
@@ -11,11 +11,11 @@ module ActsAsTaggableOn::Taggable
11
11
  before_save :save_cached_tag_list
12
12
  end
13
13
 
14
- base.intialize_acts_as_taggable_on_cache
14
+ base.initialize_acts_as_taggable_on_cache
15
15
  end
16
16
 
17
17
  module ClassMethods
18
- def intialize_acts_as_taggable_on_cache
18
+ def initialize_acts_as_taggable_on_cache
19
19
  tag_types.map(&:to_s).each do |tag_type|
20
20
  class_eval %(
21
21
  def self.caching_#{tag_type.singularize}_list?
@@ -27,7 +27,7 @@ module ActsAsTaggableOn::Taggable
27
27
 
28
28
  def acts_as_taggable_on(*args)
29
29
  super(*args)
30
- intialize_acts_as_taggable_on_cache
30
+ initialize_acts_as_taggable_on_cache
31
31
  end
32
32
 
33
33
  def caching_tag_list_on?(context)
@@ -40,7 +40,7 @@ module ActsAsTaggableOn::Taggable
40
40
  tag_types.map(&:to_s).each do |tag_type|
41
41
  if self.class.send("caching_#{tag_type.singularize}_list?")
42
42
  if tag_list_cache_set_on(tag_type)
43
- list = tag_list_cache_on(tag_type.singularize).to_a.flatten.compact.join(', ')
43
+ list = tag_list_cache_on(tag_type).to_a.flatten.compact.join(', ')
44
44
  self["cached_#{tag_type.singularize}_list"] = list
45
45
  end
46
46
  end
@@ -53,73 +53,68 @@ 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
- scope = if ActiveRecord::VERSION::MAJOR >= 3
57
- {}
58
- else
59
- scope(:find) || {}
60
- end
56
+ scope = {}
61
57
 
62
58
  ## Generate conditions:
63
59
  options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
64
-
60
+
65
61
  start_at_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
66
62
  end_at_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
67
-
63
+
68
64
  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]
70
-
71
- conditions = [
65
+ taggable_conditions << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
66
+ taggable_conditions << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", options.delete(:on).to_s]) if options[:on]
67
+
68
+ tagging_conditions = [
72
69
  taggable_conditions,
73
- options[:conditions],
74
70
  scope[:conditions],
75
71
  start_at_conditions,
76
72
  end_at_conditions
77
73
  ].compact.reverse
78
74
 
75
+ tag_conditions = [
76
+ options[:conditions]
77
+ ].compact.reverse
78
+
79
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]
82
-
83
80
  taggable_join = "INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id"
84
81
  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
85
82
 
86
- joins = [
87
- tagging_join,
83
+ tagging_joins = [
88
84
  taggable_join,
89
85
  scope[:joins]
90
86
  ].compact
91
87
 
92
- joins = joins.reverse if ActiveRecord::VERSION::MAJOR < 3
93
-
88
+ tag_joins = [
89
+ ].compact
94
90
 
95
91
  ## Generate scope:
96
- scope = ActsAsTaggableOn::Tag.scoped(:select => "#{ActsAsTaggableOn::Tag.table_name}.*, COUNT(*) AS count").order(options[:order]).limit(options[:limit])
97
-
92
+ tagging_scope = ActsAsTaggableOn::Tagging.select("#{ActsAsTaggableOn::Tagging.table_name}.tag_id, COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) AS tags_count")
93
+ tag_scope = ActsAsTaggableOn::Tag.select("#{ActsAsTaggableOn::Tag.table_name}.*, #{ActsAsTaggableOn::Tagging.table_name}.tags_count AS count").order(options[:order]).limit(options[:limit])
94
+
98
95
  # Joins and conditions
99
- joins.each { |join| scope = scope.joins(join) }
100
- conditions.each { |condition| scope = scope.where(condition) }
101
-
96
+ tagging_joins.each { |join| tagging_scope = tagging_scope.joins(join) }
97
+ tagging_conditions.each { |condition| tagging_scope = tagging_scope.where(condition) }
98
+
99
+ tag_joins.each { |join| tag_scope = tag_scope.joins(join) }
100
+ tag_conditions.each { |condition| tag_scope = tag_scope.where(condition) }
101
+
102
102
  # GROUP BY and HAVING clauses:
103
- at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
104
- at_most = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
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
121
-
122
- scope
103
+ at_least = sanitize_sql(['tags_count >= ?', options.delete(:at_least)]) if options[:at_least]
104
+ at_most = sanitize_sql(['tags_count <= ?', options.delete(:at_most)]) if options[:at_most]
105
+ having = ["COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) > 0", at_least, at_most].compact.join(' AND ')
106
+
107
+ group_columns = "#{ActsAsTaggableOn::Tagging.table_name}.tag_id"
108
+
109
+ # Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore:
110
+ scoped_select = "#{table_name}.#{primary_key}"
111
+ tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{select(scoped_select).to_sql})").
112
+ group(group_columns).
113
+ having(having)
114
+
115
+
116
+ tag_scope = tag_scope.joins("JOIN (#{tagging_scope.to_sql}) AS #{ActsAsTaggableOn::Tagging.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id")
117
+ tag_scope
123
118
  end
124
119
  end
125
120