acts-as-taggable-on 2.4.0 → 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/Appraisals +7 -0
  4. data/Gemfile +3 -1
  5. data/{MIT-LICENSE.md → LICENSE.md} +0 -0
  6. data/README.md +22 -16
  7. data/Rakefile +2 -2
  8. data/acts-as-taggable-on.gemspec +22 -19
  9. data/gemfiles/rails_3.gemfile +8 -0
  10. data/gemfiles/rails_4.gemfile +8 -0
  11. data/lib/acts-as-taggable-on.rb +2 -0
  12. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +5 -7
  13. data/lib/acts_as_taggable_on/acts_as_taggable_on/compatibility.rb +34 -0
  14. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +75 -50
  15. data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +1 -1
  16. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +21 -12
  17. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +27 -18
  18. data/lib/acts_as_taggable_on/tag.rb +8 -8
  19. data/lib/acts_as_taggable_on/taggable.rb +10 -7
  20. data/lib/acts_as_taggable_on/tagger.rb +12 -3
  21. data/lib/acts_as_taggable_on/tagging.rb +2 -2
  22. data/lib/acts_as_taggable_on/tags_helper.rb +0 -2
  23. data/lib/{acts-as-taggable-on → acts_as_taggable_on}/version.rb +1 -1
  24. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +1 -216
  25. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +8 -8
  26. data/spec/acts_as_taggable_on/related_spec.rb +143 -0
  27. data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +187 -0
  28. data/spec/acts_as_taggable_on/tag_list_spec.rb +2 -2
  29. data/spec/acts_as_taggable_on/tag_spec.rb +3 -4
  30. data/spec/acts_as_taggable_on/taggable_spec.rb +127 -116
  31. data/spec/acts_as_taggable_on/tagger_spec.rb +32 -33
  32. data/spec/acts_as_taggable_on/tagging_spec.rb +1 -1
  33. data/spec/acts_as_taggable_on/tags_helper_spec.rb +2 -2
  34. data/spec/acts_as_taggable_on/utils_spec.rb +2 -2
  35. data/spec/models.rb +2 -2
  36. data/spec/schema.rb +1 -1
  37. data/spec/spec_helper.rb +7 -4
  38. metadata +48 -34
  39. data/CHANGELOG.md +0 -35
  40. data/UPGRADING +0 -14
  41. data/rails/init.rb +0 -1
  42. data/uninstall.rb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fd04dd7817f5d465548706b2bb39ed8923bef126
4
- data.tar.gz: 371cb9a485f0370ff51074edc74296219d6f1342
3
+ metadata.gz: d0b7cd2b4e279238a0fb8464721b40c3dbedaacd
4
+ data.tar.gz: edd07332dcbd884be1daab9e65d9be28b6f3f695
5
5
  SHA512:
6
- metadata.gz: f6dd2e281ec95af2bf1b3779a96522a99c81aa06d09b6e59337805c94060419c9860ef707d378029214b58fae76a9d23ac67d9b94a978c91385fffb2f605d64b
7
- data.tar.gz: 843b679f0dd4212e1b814323a3b253d8f8d62942acd30d0f696a64301064c6bc5d6f4b65cfb33d300929b3c2f6f7910c844e98fa9b9fabbd47c5fc03f968cb44
6
+ metadata.gz: 65f468a535ce620033df88d152bd5e1bff722c01a197d233794da097e682b29ad5f4ebf46b4eb46a18957d2ecda9f6699aad27b42789a2c8ee8d7c4f68e985ba
7
+ data.tar.gz: 2fd5249aa31f826fd56666dd3d07c4a63df1803722fc7e29c02b39fdbff6f0459316f30a1bb9345c53e885fa5a0edfd30495e211e8e6e90630600d9d60cd69a5
data/.gitignore CHANGED
@@ -3,9 +3,9 @@
3
3
  /pkg/*
4
4
  .bundle
5
5
  .ruby-version
6
- Gemfile.lock
7
6
  spec/database.yml
8
7
  tmp*.sw?
9
8
  *.sw?
10
9
  tmp
11
10
  *.gem
11
+ *.lock
@@ -0,0 +1,7 @@
1
+ appraise "rails-3" do
2
+ gem "rails", "3.2.13"
3
+ end
4
+
5
+ appraise "rails-4" do
6
+ gem "rails", "4.0.0.beta1"
7
+ end
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
- source 'http://rubygems.org'
1
+ source 'https://rubygems.org'
2
+
2
3
  gemspec
3
4
 
5
+ gem 'appraisal'
File without changes
data/README.md CHANGED
@@ -14,18 +14,28 @@ Enter Acts as Taggable On. Rather than tying functionality to a specific keyword
14
14
  tag "contexts" that can be used locally or in combination in the same way steroids
15
15
  was used.
16
16
 
17
- ## Installation
17
+ ## Compatibility
18
+
19
+ Versions 2.x are compatible with Ruby 1.8.7+ and Rails 3.
20
+
21
+ Versions 2.4.1 and up are compatible with Rails 4 too (thanks to arabonradar and cwoodcox).
18
22
 
19
- ### Rails 2.x
23
+ Versions 3.x (currently unreleased) are compatible with Ruby 1.9.3+ and Rails 3 and 4.
20
24
 
21
- Not supported any more! It is time for update guys.
25
+ For an up-to-date roadmap, see https://github.com/mbleigh/acts-as-taggable-on/issues/milestones
22
26
 
23
- ### Rails 3.x
27
+ ## Installation
24
28
 
25
29
  To use it, add it to your Gemfile:
26
30
 
27
31
  ```ruby
28
- gem 'acts-as-taggable-on', '~> 2.3.1'
32
+ gem 'acts-as-taggable-on'
33
+ ```
34
+
35
+ and bundle:
36
+
37
+ ```ruby
38
+ bundle
29
39
  ```
30
40
 
31
41
  #### Post Installation
@@ -38,12 +48,15 @@ rake db:migrate
38
48
  ## Testing
39
49
 
40
50
  Acts As Taggable On uses RSpec for its test coverage. Inside the gem
41
- directory, you can run the specs for RoR 3.x with:
51
+ directory, you can run the specs with:
42
52
 
43
53
  ```shell
54
+ bundle
44
55
  rake spec
45
56
  ```
46
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.
59
+
47
60
  ## Usage
48
61
 
49
62
  ```ruby
@@ -277,21 +290,14 @@ If you want to change the default delimiter (it defaults to ','). You can also p
277
290
  ActsAsTaggableOn.delimiter = ','
278
291
  ```
279
292
 
280
- ## Changelog
281
-
282
- See [CHANGELOG](https://github.com/mbleigh/acts-as-taggable-on/blob/master/CHANGELOG.md).
283
-
284
293
  ## Contributors
285
294
 
286
295
  We have a long list of valued contributors. [Check them all](https://github.com/mbleigh/acts-as-taggable-on/contributors)
287
296
 
288
- ## Maintainers
297
+ ## Maintainer
289
298
 
290
- * [Artem Kramarenko](https://github.com/artemk) (artemk)
291
299
  * [Joost Baaij](https://github.com/tilsammans)
292
300
 
293
- ## Author
294
-
295
- * [Michael Bleigh](https://github.com/mbleigh)
301
+ ## License
296
302
 
297
- Copyright (c) 2007-2011 Michael Bleigh (http://mbleigh.com/) and Intridea Inc. (http://intridea.com/), released under the [MIT license](https://github.com/mbleigh/acts-as-taggable-on/blob/master/MIT-LICENSE.md)
303
+ See [LICENSE](https://github.com/mbleigh/acts-as-taggable-on/blob/master/LICENSE.md)
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'rubygems'
2
- require 'bundler'
3
- Bundler.setup :default, :development
2
+ require 'bundler/setup'
3
+ require 'appraisal'
4
4
 
5
5
  desc 'Default: run specs'
6
6
  task :default => :spec
@@ -1,32 +1,35 @@
1
- $:.push File.dirname(__FILE__) + '/lib'
2
- require 'acts-as-taggable-on/version'
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'acts_as_taggable_on/version'
3
5
 
4
6
  Gem::Specification.new do |gem|
5
- gem.name = %q{acts-as-taggable-on}
6
- gem.authors = ["Michael Bleigh"]
7
- gem.date = %q{2012-07-16}
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 = ''
7
+ gem.name = "acts-as-taggable-on"
8
+ gem.version = ActsAsTaggableOn::VERSION
9
+ gem.authors = ["Michael Bleigh", "Joost Baaij"]
10
+ gem.email = ["michael@intridea.com", "joost@spacebabies.nl"]
11
+ 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.}
12
+ gem.summary = "Advanced tagging for Rails."
13
+ gem.homepage = 'https://github.com/mbleigh/acts-as-taggable-on'
14
+ gem.license = "MIT"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
12
20
 
13
21
  if File.exists?('UPGRADING')
14
- gem.post_install_message = File.read("UPGRADING")
22
+ gem.post_install_message = File.read('UPGRADING')
15
23
  end
16
24
 
17
- gem.add_runtime_dependency 'rails', '~> 3.0'
25
+ gem.add_runtime_dependency 'rails', ['>= 3', '< 5']
26
+
27
+ gem.add_development_dependency 'rspec-rails', '2.13.0' # 2.13.1 is broken
18
28
  gem.add_development_dependency 'rspec', '~> 2.6'
19
- gem.add_development_dependency 'ammeter', '~> 0.1.3'
29
+ gem.add_development_dependency 'ammeter'
20
30
  gem.add_development_dependency 'sqlite3'
21
31
  gem.add_development_dependency 'mysql2', '~> 0.3.7'
22
32
  gem.add_development_dependency 'pg'
23
33
  gem.add_development_dependency 'guard'
24
34
  gem.add_development_dependency 'guard-rspec'
25
-
26
- gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
- gem.files = `git ls-files`.split("\n")
28
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
29
- gem.name = "acts-as-taggable-on"
30
- gem.require_paths = ['lib']
31
- gem.version = ActsAsTaggableOn::VERSION
32
35
  end
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "rails", "3.2.13"
7
+
8
+ gemspec :path=>"../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "rails", :github => 'rails/rails'
7
+
8
+ gemspec :path=>"../"
@@ -36,6 +36,7 @@ end
36
36
  require "acts_as_taggable_on/utils"
37
37
 
38
38
  require "acts_as_taggable_on/taggable"
39
+ require "acts_as_taggable_on/acts_as_taggable_on/compatibility"
39
40
  require "acts_as_taggable_on/acts_as_taggable_on/core"
40
41
  require "acts_as_taggable_on/acts_as_taggable_on/collection"
41
42
  require "acts_as_taggable_on/acts_as_taggable_on/cache"
@@ -53,6 +54,7 @@ $LOAD_PATH.shift
53
54
 
54
55
 
55
56
  if defined?(ActiveRecord::Base)
57
+ ActiveRecord::Base.extend ActsAsTaggableOn::Compatibility
56
58
  ActiveRecord::Base.extend ActsAsTaggableOn::Taggable
57
59
  ActiveRecord::Base.send :include, ActsAsTaggableOn::Tagger
58
60
  end
@@ -86,12 +86,10 @@ 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
- scoped_select = "#{table_name}.#{primary_key}"
90
- tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{select(scoped_select).to_sql})").
91
- group(group_columns)
92
-
93
- 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")
94
- tag_scope
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")
95
93
  end
96
94
 
97
95
  ##
@@ -166,7 +164,7 @@ module ActsAsTaggableOn::Taggable
166
164
  scoped_select = "#{table_name}.#{primary_key}"
167
165
  select_query = "#{select(scoped_select).to_sql}"
168
166
 
169
- res = ActiveRecord::Base.connection.select_all(select_query).map { |item| item.values }.flatten.join(",")
167
+ res = ActiveRecord::Base.connection.select_all(select_query).map { |item| item.values }.flatten.compact.join(",")
170
168
  res = "NULL" if res.blank?
171
169
 
172
170
  tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{res})")
@@ -0,0 +1,34 @@
1
+ module ActsAsTaggableOn::Compatibility
2
+ def has_many_with_compatibility(name, options = {}, &extention)
3
+ if ActiveRecord::VERSION::MAJOR >= 4
4
+ scope, opts = build_scope_and_options(options)
5
+ has_many(name, scope, opts, &extention)
6
+ else
7
+ has_many(name, options, &extention)
8
+ end
9
+ end
10
+
11
+ def build_scope_and_options(opts)
12
+ scope_opts, opts = parse_options(opts)
13
+
14
+ unless scope_opts.empty?
15
+ scope = lambda do
16
+ scope_opts.inject(self) { |result, hash| result.send *hash }
17
+ end
18
+ end
19
+
20
+ [defined?(scope) ? scope : nil, opts]
21
+ end
22
+
23
+ def parse_options(opts)
24
+ scope_opts = {}
25
+ [:order, :having, :select, :group, :limit, :offset, :readonly].each do |o|
26
+ scope_opts[o] = opts.delete o if opts[o]
27
+ end
28
+ scope_opts[:where] = opts.delete :conditions if opts[:conditions]
29
+ scope_opts[:joins] = opts.delete :include if opts [:include]
30
+ scope_opts[:distinct] = opts.delete :uniq if opts[:uniq]
31
+
32
+ [scope_opts, opts]
33
+ end
34
+ end
@@ -13,28 +13,30 @@ module ActsAsTaggableOn::Taggable
13
13
  end
14
14
 
15
15
  module ClassMethods
16
+
16
17
  def initialize_acts_as_taggable_on_core
17
18
  include taggable_mixin
18
19
  tag_types.map(&:to_s).each do |tags_type|
19
20
  tag_type = tags_type.to_s.singularize
20
21
  context_taggings = "#{tag_type}_taggings".to_sym
21
22
  context_tags = tags_type.to_sym
22
- taggings_order = (preserve_tag_order? ? "#{ActsAsTaggableOn::Tagging.table_name}.id" : nil)
23
-
23
+ taggings_order = (preserve_tag_order? ? "#{ActsAsTaggableOn::Tagging.table_name}.id" : [])
24
+
24
25
  class_eval do
25
26
  # when preserving tag order, include order option so that for a 'tags' context
26
27
  # the associations tag_taggings & tags are always returned in created order
27
- has_many context_taggings, :as => :taggable,
28
- :dependent => :destroy,
29
- :include => :tag,
30
- :class_name => "ActsAsTaggableOn::Tagging",
31
- :conditions => ["#{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_type],
32
- :order => taggings_order
33
-
34
- has_many context_tags, :through => context_taggings,
35
- :source => :tag,
36
- :class_name => "ActsAsTaggableOn::Tag",
37
- :order => taggings_order
28
+ has_many_with_compatibility context_taggings, :as => :taggable,
29
+ :dependent => :destroy,
30
+ :class_name => "ActsAsTaggableOn::Tagging",
31
+ :order => taggings_order,
32
+ :conditions => ["#{ActsAsTaggableOn::Tagging.table_name}.context = (?)", tags_type],
33
+ :include => :tag
34
+
35
+ has_many_with_compatibility context_tags, :through => context_taggings,
36
+ :source => :tag,
37
+ :class_name => "ActsAsTaggableOn::Tag",
38
+ :order => taggings_order
39
+
38
40
  end
39
41
 
40
42
  taggable_mixin.class_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -57,7 +59,7 @@ module ActsAsTaggableOn::Taggable
57
59
  super(preserve_tag_order, *tag_types)
58
60
  initialize_acts_as_taggable_on_core
59
61
  end
60
-
62
+
61
63
  # all column names are necessary for PostgreSQL group clause
62
64
  def grouped_column_names_for(object)
63
65
  object.column_names.map { |column| "#{object.table_name}.#{column}" }.join(", ")
@@ -81,12 +83,14 @@ module ActsAsTaggableOn::Taggable
81
83
  # User.tagged_with("awesome", "cool", :owned_by => foo ) # Users that are tagged with just awesome and cool by 'foo'
82
84
  def tagged_with(tags, options = {})
83
85
  tag_list = ActsAsTaggableOn::TagList.from(tags)
84
- empty_result = scoped(:conditions => "1 = 0")
86
+ empty_result = where("1 = 0")
85
87
 
86
88
  return empty_result if tag_list.empty?
87
89
 
88
90
  joins = []
89
91
  conditions = []
92
+ having = []
93
+ select_clause = []
90
94
 
91
95
  context = options.delete(:on)
92
96
  owned_by = options.delete(:owned_by)
@@ -102,15 +106,23 @@ module ActsAsTaggableOn::Taggable
102
106
 
103
107
  conditions << "#{table_name}.#{primary_key} NOT IN (SELECT #{ActsAsTaggableOn::Tagging.table_name}.taggable_id FROM #{ActsAsTaggableOn::Tagging.table_name} JOIN #{ActsAsTaggableOn::Tag.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key} AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
104
108
 
109
+ if owned_by
110
+ joins << "JOIN #{ActsAsTaggableOn::Tagging.table_name}" +
111
+ " ON #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
112
+ " AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name)}" +
113
+ " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = #{owned_by.id}" +
114
+ " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = #{quote_value(owned_by.class.base_class.to_s)}"
115
+ end
116
+
105
117
  elsif options.delete(:any)
106
118
  # get tags, drop out if nothing returned (we need at least one)
107
- if options.delete(:wild)
108
- tags = ActsAsTaggableOn::Tag.named_like_any(tag_list)
119
+ tags = if options.delete(:wild)
120
+ ActsAsTaggableOn::Tag.named_like_any(tag_list)
109
121
  else
110
- tags = ActsAsTaggableOn::Tag.named_any(tag_list)
122
+ ActsAsTaggableOn::Tag.named_any(tag_list)
111
123
  end
112
124
 
113
- return scoped(:conditions => "1 = 0") unless tags.length > 0
125
+ return empty_result unless tags.length > 0
114
126
 
115
127
  # setup taggings alias so we can chain, ex: items_locations_taggings_awesome_cool_123
116
128
  # avoid ambiguous column name
@@ -129,29 +141,37 @@ module ActsAsTaggableOn::Taggable
129
141
  conditions << tags.map { |t| "#{taggings_alias}.tag_id = #{t.id}" }.join(" OR ")
130
142
  select_clause = "DISTINCT #{table_name}.*" unless context and tag_types.one?
131
143
 
132
- joins << tagging_join
144
+ if owned_by
145
+ tagging_join << " AND " +
146
+ sanitize_sql([
147
+ "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
148
+ owned_by.id,
149
+ owned_by.class.base_class.to_s
150
+ ])
151
+ end
133
152
 
153
+ joins << tagging_join
134
154
  else
135
155
  tags = ActsAsTaggableOn::Tag.named_any(tag_list)
156
+
136
157
  return empty_result unless tags.length == tag_list.length
137
158
 
138
159
  tags.each do |tag|
139
-
140
160
  taggings_alias = adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{sha_prefix(tag.name)}")
141
-
142
161
  tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
143
162
  " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
144
163
  " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}" +
145
164
  " AND #{taggings_alias}.tag_id = #{tag.id}"
165
+
146
166
  tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
147
167
 
148
168
  if owned_by
149
169
  tagging_join << " AND " +
150
- sanitize_sql([
151
- "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
152
- owned_by.id,
153
- owned_by.class.base_class.to_s
154
- ])
170
+ sanitize_sql([
171
+ "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
172
+ owned_by.id,
173
+ owned_by.class.base_class.to_s
174
+ ])
155
175
  end
156
176
 
157
177
  joins << tagging_join
@@ -171,13 +191,13 @@ module ActsAsTaggableOn::Taggable
171
191
  having = "COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
172
192
  end
173
193
 
174
- scoped(:select => select_clause,
175
- :joins => joins.join(" "),
176
- :group => group,
177
- :having => having,
178
- :conditions => conditions.join(" AND "),
179
- :order => options[:order],
180
- :readonly => false)
194
+ select(select_clause) \
195
+ .joins(joins.join(" ")) \
196
+ .where(conditions.join(" AND ")) \
197
+ .group(group) \
198
+ .having(having) \
199
+ .order(options[:order]) \
200
+ .readonly(false)
181
201
  end
182
202
 
183
203
  def is_taggable?
@@ -252,12 +272,10 @@ module ActsAsTaggableOn::Taggable
252
272
 
253
273
  if ActsAsTaggableOn::Tag.using_postgresql?
254
274
  group_columns = grouped_column_names_for(ActsAsTaggableOn::Tag)
255
- scope = scope.order("max(#{tagging_table_name}.created_at)").group(group_columns)
275
+ scope.order("max(#{tagging_table_name}.created_at)").group(group_columns)
256
276
  else
257
- scope = scope.group("#{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key}")
258
- end
259
-
260
- scope.all
277
+ scope.group("#{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key}")
278
+ end.to_a
261
279
  end
262
280
 
263
281
  ##
@@ -267,7 +285,7 @@ module ActsAsTaggableOn::Taggable
267
285
  # when preserving tag order, return tags in created order
268
286
  # if we added the order to the association this would always apply
269
287
  scope = scope.order("#{ActsAsTaggableOn::Tagging.table_name}.id") if self.class.preserve_tag_order?
270
- scope.all
288
+ scope
271
289
  end
272
290
 
273
291
  def set_tag_list_on(context, new_list)
@@ -309,7 +327,6 @@ module ActsAsTaggableOn::Taggable
309
327
  def save_tags
310
328
  tagging_contexts.each do |context|
311
329
  next unless tag_list_cache_set_on(context)
312
-
313
330
  # List of currently assigned tag names
314
331
  tag_list = tag_list_cache_on(context).uniq
315
332
 
@@ -321,13 +338,22 @@ module ActsAsTaggableOn::Taggable
321
338
 
322
339
  # Tag maintenance based on whether preserving the created order of tags
323
340
  if self.class.preserve_tag_order?
324
- # First off order the array of tag objects to match the tag list
325
- # rather than existing tags followed by new tags
326
- tags = tag_list.map{|l| tags.detect{|t| t.name.downcase == l.downcase}}
327
- # To preserve tags in the order in which they were added
328
- # delete all current tags and create new tags if the content or order has changed
329
- old_tags = (tags == current_tags ? [] : current_tags)
330
- new_tags = (tags == current_tags ? [] : tags)
341
+ old_tags, new_tags = current_tags - tags, tags - current_tags
342
+
343
+ shared_tags = current_tags & tags
344
+
345
+ if shared_tags.any? && tags[0...shared_tags.size] != shared_tags
346
+ index = shared_tags.each_with_index { |_, i| break i unless shared_tags[i] == tags[i] }
347
+
348
+ # Update arrays of tag objects
349
+ old_tags |= current_tags[index...current_tags.size]
350
+ new_tags |= current_tags[index...current_tags.size] & shared_tags
351
+
352
+ # Order the array of tag objects to match the tag list
353
+ new_tags = tags.map do |t|
354
+ new_tags.find { |n| n.name.downcase == t.name.downcase }
355
+ end.compact
356
+ end
331
357
  else
332
358
  # Delete discarded tags and create new tags
333
359
  old_tags = current_tags - tags
@@ -336,8 +362,7 @@ module ActsAsTaggableOn::Taggable
336
362
 
337
363
  # Find taggings to remove:
338
364
  if old_tags.present?
339
- old_taggings = taggings.where(:tagger_type => nil, :tagger_id => nil,
340
- :context => context.to_s, :tag_id => old_tags).all
365
+ old_taggings = taggings.where(:tagger_type => nil, :tagger_id => nil, :context => context.to_s, :tag_id => old_tags)
341
366
  end
342
367
 
343
368
  # Destroy old taggings: