acts-as-taggable-on 2.4.0 → 2.4.1

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 (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: