acts-as-taggable-on 2.4.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +13 -4
  3. data/Appraisals +8 -4
  4. data/CHANGELOG.md +47 -0
  5. data/Gemfile +7 -1
  6. data/README.md +21 -11
  7. data/Rakefile +21 -3
  8. data/UPGRADING +7 -0
  9. data/acts-as-taggable-on.gemspec +5 -5
  10. data/{lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb → db/migrate/1_acts_as_taggable_on_migration.rb} +0 -0
  11. data/db/migrate/2_add_missing_unique_indices.rb +21 -0
  12. data/gemfiles/{rails_3.gemfile → rails_3.2.gemfile} +1 -2
  13. data/gemfiles/rails_4.0.gemfile +7 -0
  14. data/gemfiles/rails_4.1.gemfile +7 -0
  15. data/lib/acts-as-taggable-on.rb +9 -13
  16. data/lib/acts_as_taggable_on.rb +6 -0
  17. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +49 -20
  18. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +42 -32
  19. data/lib/acts_as_taggable_on/acts_as_taggable_on/compatibility.rb +1 -1
  20. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +24 -13
  21. data/lib/acts_as_taggable_on/engine.rb +6 -0
  22. data/lib/acts_as_taggable_on/tag.rb +27 -7
  23. data/lib/acts_as_taggable_on/taggable.rb +6 -6
  24. data/lib/acts_as_taggable_on/tagger.rb +6 -6
  25. data/lib/acts_as_taggable_on/tags_helper.rb +1 -1
  26. data/lib/acts_as_taggable_on/version.rb +1 -1
  27. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +4 -71
  28. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +19 -19
  29. data/spec/acts_as_taggable_on/caching_spec.rb +77 -0
  30. data/spec/acts_as_taggable_on/tags_helper_spec.rb +10 -10
  31. data/spec/schema.rb +25 -21
  32. data/spec/spec_helper.rb +9 -25
  33. metadata +40 -56
  34. data/gemfiles/rails_4.gemfile +0 -8
  35. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +0 -39
  36. data/spec/generators/acts_as_taggable_on/migration/migration_generator_spec.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d0b7cd2b4e279238a0fb8464721b40c3dbedaacd
4
- data.tar.gz: edd07332dcbd884be1daab9e65d9be28b6f3f695
3
+ metadata.gz: 5b46e80f19a5c7de71ec448bd0700ef672ea9639
4
+ data.tar.gz: d7c19bc3aec73a2b5d1ff08ad18cb9c85e61225c
5
5
  SHA512:
6
- metadata.gz: 65f468a535ce620033df88d152bd5e1bff722c01a197d233794da097e682b29ad5f4ebf46b4eb46a18957d2ecda9f6699aad27b42789a2c8ee8d7c4f68e985ba
7
- data.tar.gz: 2fd5249aa31f826fd56666dd3d07c4a63df1803722fc7e29c02b39fdbff6f0459316f30a1bb9345c53e885fa5a0edfd30495e211e8e6e90630600d9d60cd69a5
6
+ metadata.gz: 4ac4317c1fa00caa6f048d4958d830da83bae5a48d8fd2f4018918319069e9e0311b99318cfe337fcd8d23f9c37055484e1c40c1ab0ecda2c256d361315b0848
7
+ data.tar.gz: 7e29cf71dfce65f2ac65d0a10972c6b26344398278c53b088b9069ec728955e697389f496b25a8f57fd521b6349aaa2ef827caab78bf8a00c7416c9be43c21e5
@@ -1,9 +1,18 @@
1
- script: "cp spec/database.yml.sample spec/database.yml && bundle install && bundle exec rake"
2
1
  rvm:
3
- - 1.8.7
4
- - 1.9.2
5
2
  - 1.9.3
3
+ - 2.0.0
6
4
  env:
7
5
  - DB=sqlite3
8
6
  - DB=mysql
9
- - DB=postgresql
7
+ - DB=postgresql
8
+ gemfile:
9
+ - gemfiles/rails_3.2.gemfile
10
+ - gemfiles/rails_4.0.gemfile
11
+ - gemfiles/rails_4.1.gemfile
12
+ cache: bundler
13
+ script: bundle exec rake
14
+ before_install:
15
+ - gem install bundler
16
+ bundler_args: '--without local_development'
17
+ matrix:
18
+ fast_finish: true
data/Appraisals CHANGED
@@ -1,7 +1,11 @@
1
- appraise "rails-3" do
2
- gem "rails", "3.2.13"
1
+ appraise "rails-3.2" do
2
+ gem "rails", "~> 3.2"
3
3
  end
4
4
 
5
- appraise "rails-4" do
6
- gem "rails", "4.0.0.beta1"
5
+ appraise "rails-4.0" do
6
+ gem "rails", "~> 4.0"
7
+ end
8
+
9
+ appraise "rails-4.1" do
10
+ gem "rails", "~> 4.1.0.beta1"
7
11
  end
@@ -0,0 +1,47 @@
1
+ Changes are below categorized as `Features, Fixes, or Misc`.
2
+
3
+ Each change should fall into categories that would affect whether the release is major (breaking changes), minor (new behavior), or patch (bug fix). See [semver](http://semver.org/) and [pessimistic versioning](http://guides.rubygems.org/patterns/#pessimistic_version_constraint)
4
+
5
+ As such, a _Feature_ would map to either major or minor. A _bug fix_ to a patch. And _misc_ is either minor or patch, the difference being kind of fuzzy for the purposes of history. Adding tests would be patch level.
6
+
7
+ ### Master [changes](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.0.0...master)
8
+
9
+ * Breaking Changes
10
+ * Features
11
+ * Fixes
12
+ * Misc
13
+
14
+ ### [3.0.0 / 2014-01-01](https://github.com/mbleigh/acts-as-taggable-on/compare/v2.4.1...v3.0.0)
15
+
16
+ * Breaking Changes
17
+ * No longer supports Ruby 1.8.
18
+ * Features
19
+ * Supports Rails 4.1.
20
+ * Misc (TODO: expand)
21
+ * [zquest #359](https://github.com/mbleigh/acts-as-taggable-on/pull/359)
22
+ * [rsl #367](https://github.com/mbleigh/acts-as-taggable-on/pull/367)
23
+ * [ktdreyer #383](https://github.com/mbleigh/acts-as-taggable-on/pull/383)
24
+ * [cwoodcox #346](https://github.com/mbleigh/acts-as-taggable-on/pull/346)
25
+ * [mrb #421](https://github.com/mbleigh/acts-as-taggable-on/pull/421)
26
+ * [bf4 #430](https://github.com/mbleigh/acts-as-taggable-on/pull/430)
27
+ * [sanemat #368](https://github.com/mbleigh/acts-as-taggable-on/pull/368)
28
+ * [bf4 #343](https://github.com/mbleigh/acts-as-taggable-on/pull/343)
29
+ * [marclennox #429](https://github.com/mbleigh/acts-as-taggable-on/pull/429)
30
+ * [shekibobo #403](https://github.com/mbleigh/acts-as-taggable-on/pull/403)
31
+ * [ches ktdreyer #410](https://github.com/mbleigh/acts-as-taggable-on/pull/410)
32
+ * [makaroni4 #371](https://github.com/mbleigh/acts-as-taggable-on/pull/371)
33
+ * [kenzai dstosik awt #431](https://github.com/mbleigh/acts-as-taggable-on/pull/431)
34
+ * [bf4 joelcogen shekibobo aaronchi #438](https://github.com/mbleigh/acts-as-taggable-on/pull/438)
35
+ * [seuros #442](https://github.com/mbleigh/acts-as-taggable-on/pull/442)
36
+ * [bf4 #445](https://github.com/mbleigh/acts-as-taggable-on/pull/445)
37
+ * [eaglemt #446](https://github.com/mbleigh/acts-as-taggable-on/pull/446)
38
+
39
+ ### 3.0.0.rc2 [changes](https://github.com/mbleigh/acts-as-taggable-on/compare/fork-v3.0.0.rc1...fork-v3.0.0.rc2)
40
+
41
+ ### 3.0.0.rc1 [changes](https://github.com/mbleigh/acts-as-taggable-on/compare/v2.4.1...fork-v3.0.0.rc1)
42
+
43
+ ### [2.4.1 / 2013-05-07](https://github.com/mbleigh/acts-as-taggable-on/compare/v2.4.0...v2.4.1)
44
+
45
+ * Features
46
+ * Fixes
47
+ * Misc
data/Gemfile CHANGED
@@ -2,4 +2,10 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'appraisal'
5
+ group :local_development do
6
+ gem 'guard'
7
+ gem 'guard-rspec'
8
+ gem 'appraisal'
9
+ gem 'rake'
10
+ end
11
+
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # ActsAsTaggableOn
2
2
  [![Build Status](https://secure.travis-ci.org/mbleigh/acts-as-taggable-on.png)](http://travis-ci.org/mbleigh/acts-as-taggable-on)
3
+ [![Code Climate](https://codeclimate.com/github/mbleigh/acts-as-taggable-on.png)](https://codeclimate.com/github/mbleigh/acts-as-taggable-on)
3
4
 
4
5
  This plugin was originally based on Acts as Taggable on Steroids by Jonathan Viney.
5
6
  It has evolved substantially since that point, but all credit goes to him for the
@@ -20,7 +21,7 @@ Versions 2.x are compatible with Ruby 1.8.7+ and Rails 3.
20
21
 
21
22
  Versions 2.4.1 and up are compatible with Rails 4 too (thanks to arabonradar and cwoodcox).
22
23
 
23
- Versions 3.x (currently unreleased) are compatible with Ruby 1.9.3+ and Rails 3 and 4.
24
+ Versions 3.x (currently unreleased) are compatible with Ruby 1.9.3+ and Rails 3 and 4. There is a [release candidate you may try](http://rubygems.org/gems/acts_as_taggable_on).
24
25
 
25
26
  For an up-to-date roadmap, see https://github.com/mbleigh/acts-as-taggable-on/issues/milestones
26
27
 
@@ -41,21 +42,17 @@ bundle
41
42
  #### Post Installation
42
43
 
43
44
  ```shell
45
+ # For the latest versions
46
+ rake railties:install:migrations FROM=acts_as_taggable_on_engine db:migrate
47
+
48
+ # For versions 2.4.1 and earlier
44
49
  rails generate acts_as_taggable_on:migration
45
50
  rake db:migrate
46
51
  ```
47
52
 
48
- ## Testing
53
+ #### Upgrading
49
54
 
50
- Acts As Taggable On uses RSpec for its test coverage. Inside the gem
51
- directory, you can run the specs with:
52
-
53
- ```shell
54
- bundle
55
- rake spec
56
- ```
57
-
58
- If you want, add a `.ruby-version` file in the project root (and use rbenv or RVM) to work on a specific version of Ruby.
55
+ see [UPGRADING](UPGRADING)
59
56
 
60
57
  ## Usage
61
58
 
@@ -298,6 +295,19 @@ We have a long list of valued contributors. [Check them all](https://github.com/
298
295
 
299
296
  * [Joost Baaij](https://github.com/tilsammans)
300
297
 
298
+ ## Testing
299
+
300
+ Acts As Taggable On uses RSpec for its test coverage. Inside the gem
301
+ directory, you can run the specs with:
302
+
303
+ ```shell
304
+ bundle
305
+ rake spec
306
+ ```
307
+
308
+ You can run all the tests across all the Rails versions by running `rake appraise`. If you'd also like to [run the tests across all rubies and databases as configured for Travis CI, install and run `wwtd`](https://github.com/grosser/wwtd).
309
+
310
+
301
311
  ## License
302
312
 
303
313
  See [LICENSE](https://github.com/mbleigh/acts-as-taggable-on/blob/master/LICENSE.md)
data/Rakefile CHANGED
@@ -1,9 +1,27 @@
1
1
  require 'rubygems'
2
- require 'bundler/setup'
3
- require 'appraisal'
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ STDERR.puts "Bundler not loaded"
6
+ end
7
+
8
+ desc 'Copy sample spec database.yml over if not exists'
9
+ task :copy_db_config do
10
+ cp 'spec/database.yml.sample', 'spec/database.yml'
11
+ end
12
+
13
+ task :spec => [:copy_db_config]
4
14
 
5
15
  desc 'Default: run specs'
6
- task :default => :spec
16
+ task :default => :spec
17
+
18
+ begin
19
+ require 'appraisal'
20
+ desc 'Run tests across gemfiles specified in Appraisals'
21
+ task :appraise => ['appraisal:cleanup', 'appraisal:install', 'appraisal']
22
+ rescue LoadError
23
+ puts "appraisal tasks not available"
24
+ end
7
25
 
8
26
  require 'rspec/core/rake_task'
9
27
  RSpec::Core::RakeTask.new do |t|
@@ -0,0 +1,7 @@
1
+ When upgrading
2
+
3
+ Re-run the migrations generator
4
+
5
+ rake railties:install:migrations FROM=acts_as_taggable_on_engine db:migrate
6
+
7
+ It will create any new migrations and skip existing ones
@@ -24,12 +24,12 @@ Gem::Specification.new do |gem|
24
24
 
25
25
  gem.add_runtime_dependency 'rails', ['>= 3', '< 5']
26
26
 
27
- gem.add_development_dependency 'rspec-rails', '2.13.0' # 2.13.1 is broken
28
- gem.add_development_dependency 'rspec', '~> 2.6'
29
- gem.add_development_dependency 'ammeter'
30
27
  gem.add_development_dependency 'sqlite3'
31
28
  gem.add_development_dependency 'mysql2', '~> 0.3.7'
32
29
  gem.add_development_dependency 'pg'
33
- gem.add_development_dependency 'guard'
34
- gem.add_development_dependency 'guard-rspec'
30
+
31
+ gem.add_development_dependency 'rspec-rails', '2.13.0' # 2.13.1 is broken
32
+ gem.add_development_dependency 'rspec', '~> 2.6'
33
+ gem.add_development_dependency 'ammeter'
34
+
35
35
  end
@@ -0,0 +1,21 @@
1
+ class AddMissingUniqueIndices < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ add_index :tags, :name, unique: true
5
+
6
+ remove_index :taggings, :tag_id
7
+ remove_index :taggings, [:taggable_id, :taggable_type, :context]
8
+ add_index :taggings,
9
+ [:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type],
10
+ unique: true, name: 'taggings_idx'
11
+ end
12
+
13
+ def self.down
14
+ remove_index :tags, :name
15
+
16
+ remove_index :taggings, name: 'tagging_idx'
17
+ add_index :taggings, :tag_id
18
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
19
+ end
20
+
21
+ end
@@ -2,7 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal"
6
- gem "rails", "3.2.13"
5
+ gem "rails", "~> 3.2"
7
6
 
8
7
  gemspec :path=>"../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.0"
6
+
7
+ gemspec :path=>"../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.1.0.beta1"
6
+
7
+ gemspec :path=>"../"
@@ -1,11 +1,11 @@
1
1
  require "active_record"
2
2
  require "active_record/version"
3
+ require "active_support/core_ext/module"
3
4
  require "action_view"
5
+ require 'active_support/all'
4
6
 
5
7
  require "digest/sha1"
6
8
 
7
- $LOAD_PATH.unshift(File.dirname(__FILE__))
8
-
9
9
  module ActsAsTaggableOn
10
10
  mattr_accessor :delimiter
11
11
  @@delimiter = ','
@@ -49,17 +49,13 @@ require "acts_as_taggable_on/tag"
49
49
  require "acts_as_taggable_on/tag_list"
50
50
  require "acts_as_taggable_on/tags_helper"
51
51
  require "acts_as_taggable_on/tagging"
52
+ require 'acts_as_taggable_on/engine'
52
53
 
53
- $LOAD_PATH.shift
54
-
55
-
56
- if defined?(ActiveRecord::Base)
57
- ActiveRecord::Base.extend ActsAsTaggableOn::Compatibility
58
- ActiveRecord::Base.extend ActsAsTaggableOn::Taggable
59
- ActiveRecord::Base.send :include, ActsAsTaggableOn::Tagger
54
+ ActiveSupport.on_load(:active_record) do
55
+ extend ActsAsTaggableOn::Compatibility
56
+ extend ActsAsTaggableOn::Taggable
57
+ include ActsAsTaggableOn::Tagger
60
58
  end
61
-
62
- if defined?(ActionView::Base)
63
- ActionView::Base.send :include, ActsAsTaggableOn::TagsHelper
59
+ ActiveSupport.on_load(:action_view) do
60
+ include ActsAsTaggableOn::TagsHelper
64
61
  end
65
-
@@ -0,0 +1,6 @@
1
+ $stderr.puts <<EOF
2
+ You are requiring 'acts_as_taggable_on' in the 'acts-as-taggable-on' gem.
3
+
4
+ Please change your require to 'acts-as-taggable-on'.
5
+ EOF
6
+ require_relative 'acts-as-taggable-on'
@@ -1,41 +1,69 @@
1
1
  module ActsAsTaggableOn::Taggable
2
2
  module Cache
3
3
  def self.included(base)
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
-
7
- base.send :include, ActsAsTaggableOn::Taggable::Cache::InstanceMethods
8
- base.extend ActsAsTaggableOn::Taggable::Cache::ClassMethods
9
-
10
- base.class_eval do
11
- before_save :save_cached_tag_list
4
+ # When included, conditionally adds tag caching methods when the model
5
+ # has any "cached_#{tag_type}_list" column
6
+ base.instance_eval do
7
+ # @private
8
+ def _has_tags_cache_columns?(db_columns)
9
+ db_column_names = db_columns.map(&:name)
10
+ tag_types.any? {|context|
11
+ db_column_names.include?("cached_#{context.to_s.singularize}_list")
12
+ }
13
+ end
14
+
15
+ # @private
16
+ def _add_tags_caching_methods
17
+ send :include, ActsAsTaggableOn::Taggable::Cache::InstanceMethods
18
+ extend ActsAsTaggableOn::Taggable::Cache::ClassMethods
19
+
20
+ before_save :save_cached_tag_list
21
+
22
+ initialize_tags_cache
23
+ end
24
+
25
+ # ActiveRecord::Base.columns makes a database connection and caches the calculated
26
+ # columns hash for the record as @columns. Since we don't want to add caching
27
+ # methods until we confirm the presence of a caching column, and we don't
28
+ # want to force opening a database connection when the class is loaded,
29
+ # here we intercept and cache the call to :columns as @acts_as_taggable_on_columns
30
+ # to mimic the underlying behavior. While processing this first call to columns,
31
+ # we do the caching column check and dynamically add the class and instance methods
32
+ def columns
33
+ @acts_as_taggable_on_columns ||= begin
34
+ db_columns = super
35
+ if _has_tags_cache_columns?(db_columns)
36
+ _add_tags_caching_methods
37
+ end
38
+ db_columns
39
+ end
40
+ end
41
+
12
42
  end
13
-
14
- base.initialize_acts_as_taggable_on_cache
15
43
  end
16
-
44
+
17
45
  module ClassMethods
18
- def initialize_acts_as_taggable_on_cache
46
+ def initialize_tags_cache
19
47
  tag_types.map(&:to_s).each do |tag_type|
20
48
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
21
49
  def self.caching_#{tag_type.singularize}_list?
22
50
  caching_tag_list_on?("#{tag_type}")
23
- end
51
+ end
24
52
  RUBY
25
- end
53
+ end
26
54
  end
27
-
55
+
28
56
  def acts_as_taggable_on(*args)
29
57
  super(*args)
30
- initialize_acts_as_taggable_on_cache
58
+ initialize_tags_cache
31
59
  end
32
-
60
+
33
61
  def caching_tag_list_on?(context)
34
62
  column_names.include?("cached_#{context.to_s.singularize}_list")
35
63
  end
36
64
  end
37
-
38
- module InstanceMethods
65
+
66
+ module InstanceMethods
39
67
  def save_cached_tag_list
40
68
  tag_types.map(&:to_s).each do |tag_type|
41
69
  if self.class.send("caching_#{tag_type.singularize}_list?")
@@ -45,9 +73,10 @@ module ActsAsTaggableOn::Taggable
45
73
  end
46
74
  end
47
75
  end
48
-
76
+
49
77
  true
50
78
  end
51
79
  end
80
+
52
81
  end
53
82
  end
@@ -5,7 +5,7 @@ module ActsAsTaggableOn::Taggable
5
5
  base.extend ActsAsTaggableOn::Taggable::Collection::ClassMethods
6
6
  base.initialize_acts_as_taggable_on_collection
7
7
  end
8
-
8
+
9
9
  module ClassMethods
10
10
  def initialize_acts_as_taggable_on_collection
11
11
  tag_types.map(&:to_s).each do |tag_type|
@@ -24,16 +24,16 @@ module ActsAsTaggableOn::Taggable
24
24
 
25
25
  def self.top_#{tag_type}(limit = 10)
26
26
  tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
27
- end
27
+ end
28
28
  RUBY
29
- end
29
+ end
30
30
  end
31
-
31
+
32
32
  def acts_as_taggable_on(*args)
33
33
  super(*args)
34
34
  initialize_acts_as_taggable_on_collection
35
35
  end
36
-
36
+
37
37
  def tag_counts_on(context, options = {})
38
38
  all_tag_counts(options.merge({:on => context.to_s}))
39
39
  end
@@ -86,12 +86,13 @@ module ActsAsTaggableOn::Taggable
86
86
  group_columns = "#{ActsAsTaggableOn::Tagging.table_name}.tag_id"
87
87
 
88
88
  # Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore:
89
- ids = select("#{table_name}.#{primary_key}").map(&:id)
90
- tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(?)", ids).group(group_columns)
91
-
92
- 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")
89
+ scoped_select = "#{table_name}.#{primary_key}"
90
+ tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{safe_to_sql(select(scoped_select))})").group(group_columns)
91
+
92
+ tag_scope = tag_scope.joins("JOIN (#{safe_to_sql(tagging_scope)}) AS #{ActsAsTaggableOn::Tagging.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id")
93
+ tag_scope.extending(CalculationMethods)
93
94
  end
94
-
95
+
95
96
  ##
96
97
  # Calculate the tag counts for all tags.
97
98
  #
@@ -110,29 +111,29 @@ module ActsAsTaggableOn::Taggable
110
111
  scope = {}
111
112
 
112
113
  ## Generate conditions:
113
- options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
114
+ options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
114
115
 
115
116
  start_at_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
116
117
  end_at_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
117
-
118
+
118
119
  taggable_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.taggable_type = ?", base_class.name])
119
- taggable_conditions << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
120
+ taggable_conditions << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = ?", options[:id]]) if options[:id]
120
121
  taggable_conditions << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", options.delete(:on).to_s]) if options[:on]
121
-
122
+
122
123
  tagging_conditions = [
123
124
  taggable_conditions,
124
125
  scope[:conditions],
125
126
  start_at_conditions,
126
127
  end_at_conditions
127
128
  ].compact.reverse
128
-
129
+
129
130
  tag_conditions = [
130
- options[:conditions]
131
+ options[:conditions]
131
132
  ].compact.reverse
132
-
133
+
133
134
  ## Generate joins:
134
135
  taggable_join = "INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id"
135
- 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
136
+ 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
136
137
 
137
138
  tagging_joins = [
138
139
  taggable_join,
@@ -144,10 +145,10 @@ module ActsAsTaggableOn::Taggable
144
145
 
145
146
  ## Generate scope:
146
147
  tagging_scope = ActsAsTaggableOn::Tagging.select("#{ActsAsTaggableOn::Tagging.table_name}.tag_id, COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) AS tags_count")
147
- tag_scope = ActsAsTaggableOn::Tag.select("#{ActsAsTaggableOn::Tag.table_name}.*, #{ActsAsTaggableOn::Tagging.table_name}.tags_count AS count").order(options[:order]).limit(options[:limit])
148
+ tag_scope = ActsAsTaggableOn::Tag.select("#{ActsAsTaggableOn::Tag.table_name}.*, #{ActsAsTaggableOn::Tagging.table_name}.tags_count AS count").order(options[:order]).limit(options[:limit])
148
149
 
149
150
  # Joins and conditions
150
- tagging_joins.each { |join| tagging_scope = tagging_scope.joins(join) }
151
+ tagging_joins.each { |join| tagging_scope = tagging_scope.joins(join) }
151
152
  tagging_conditions.each { |condition| tagging_scope = tagging_scope.where(condition) }
152
153
 
153
154
  tag_joins.each { |join| tag_scope = tag_scope.joins(join) }
@@ -156,29 +157,38 @@ module ActsAsTaggableOn::Taggable
156
157
  # GROUP BY and HAVING clauses:
157
158
  at_least = sanitize_sql(["COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) >= ?", options.delete(:at_least)]) if options[:at_least]
158
159
  at_most = sanitize_sql(["COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) <= ?", options.delete(:at_most)]) if options[:at_most]
159
- having = ["COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) > 0", at_least, at_most].compact.join(' AND ')
160
+ having = ["COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) > 0", at_least, at_most].compact.join(' AND ')
160
161
 
161
162
  group_columns = "#{ActsAsTaggableOn::Tagging.table_name}.tag_id"
162
163
 
163
- # Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore:
164
- scoped_select = "#{table_name}.#{primary_key}"
165
- select_query = "#{select(scoped_select).to_sql}"
166
-
167
- res = ActiveRecord::Base.connection.select_all(select_query).map { |item| item.values }.flatten.compact.join(",")
168
- res = "NULL" if res.blank?
164
+ unless options[:id]
165
+ # Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore:
166
+ scoped_select = "#{table_name}.#{primary_key}"
167
+ tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{safe_to_sql(select(scoped_select))})")
168
+ end
169
169
 
170
- tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{res})")
171
170
  tagging_scope = tagging_scope.group(group_columns).having(having)
172
171
 
173
- 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")
174
- tag_scope
172
+ tag_scope = tag_scope.joins("JOIN (#{safe_to_sql(tagging_scope)}) AS #{ActsAsTaggableOn::Tagging.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id")
173
+ tag_scope.extending(CalculationMethods)
174
+ end
175
+
176
+ def safe_to_sql(relation)
177
+ connection.respond_to?(:unprepared_statement) ? connection.unprepared_statement{relation.to_sql} : relation.to_sql
175
178
  end
176
179
  end
177
-
180
+
178
181
  module InstanceMethods
179
182
  def tag_counts_on(context, options={})
180
183
  self.class.tag_counts_on(context, options.merge(:id => id))
181
184
  end
182
185
  end
186
+
187
+ module CalculationMethods
188
+ def count
189
+ # https://github.com/rails/rails/commit/da9b5d4a8435b744fcf278fffd6d7f1e36d4a4f2
190
+ super(:all)
191
+ end
192
+ end
183
193
  end
184
- end
194
+ end