acts-as-taggable-on 3.4.1 → 3.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 281e3a40ea12a2d4bf7cd9a17548afaa79d38551
4
- data.tar.gz: 87e34ac29deab9211aea132e6f2189635d6918d5
3
+ metadata.gz: 92222646174fc38e87d9972cea2022c78e9ba337
4
+ data.tar.gz: 62d819d54ea8d538ce67d1f30c4c640e63333328
5
5
  SHA512:
6
- metadata.gz: 2168df4d62261fb9fbaacff107fb1311f9a9c8567f674b74e784dbd48a0e006825371cfa68de03595907974a1fc842383ca2ea90c2d48e51284a7398f492442a
7
- data.tar.gz: 4478cd0771d44535549f4516f9d087b94250e2087c8c75fe559752e1f033c8336167dcaede44b1223360d1e3821da8d515e5fb03398689ea3fb207dd9f15673b
6
+ metadata.gz: 6a17ce74f9d91e98037faa3348449813632f2f1ba2a4ca92a297beaafa8e544b650c2e12586c03a9aab8c2c6b0b5c1c1de21571d860be49aed971b0d20b8338e
7
+ data.tar.gz: d50ea8e8200c3eaef6f1d80b53412637604e74de62dd3a2e6385e22d1fbf579bbb372f2bbd9b6c026f3f062e4959a288a5a890360d5f98cde6df222ebc72947f
data/.travis.yml CHANGED
@@ -1,9 +1,11 @@
1
+ language: ruby
2
+
1
3
  rvm:
2
- - 1.9.3
4
+ - 2.1
5
+ - 2.2
3
6
  - 2.0.0
4
- - 2.1.1
7
+ - 1.9.3
5
8
  - rbx-2
6
- - ruby-head
7
9
 
8
10
  env:
9
11
  - DB=sqlite3
@@ -14,30 +16,25 @@ gemfile:
14
16
  - gemfiles/activerecord_3.2.gemfile
15
17
  - gemfiles/activerecord_4.0.gemfile
16
18
  - gemfiles/activerecord_4.1.gemfile
17
- - gemfiles/activerecord_edge.gemfile
18
19
 
19
- cache: bundler
20
+ sudo: false
21
+
22
+ bundler_args: '--without local_development --jobs 3 --retry 3'
23
+
20
24
  script: bundle exec rake
21
- before_install: gem install bundler
22
- bundler_args: '--without local_development'
25
+
23
26
  matrix:
24
27
  fast_finish: true
25
28
  allow_failures:
26
29
  - gemfile: gemfiles/activerecord_edge.gemfile
27
30
  - rvm: rbx-2
28
- - rvm: ruby-head
29
31
  exclude:
32
+ - rvm: 2.2
33
+ gemfile: gemfiles/activerecord_3.2.gemfile
30
34
  - rvm: 1.9.3
31
35
  gemfile: gemfiles/activerecord_4.0.gemfile
32
36
  - rvm: 1.9.3
33
37
  gemfile: gemfiles/activerecord_4.1.gemfile
34
- - rvm: 1.9.3
35
- gemfile: gemfiles/activerecord_edge.gemfile
36
38
  - rvm: rbx-2
37
39
  gemfile: gemfiles/activerecord_3.2.gemfile
38
- - rvm: ruby-head
39
- gemfile: gemfiles/activerecord_3.2.gemfile
40
- - rvm: ruby-head
41
- gemfile: gemfiles/activerecord_4.0.gemfile
42
- - rvm: ruby-head
43
- gemfile: gemfiles/activerecord_4.1.gemfile
40
+
data/Appraisals CHANGED
@@ -1,22 +1,16 @@
1
1
  appraise "activerecord-3.2" do
2
- gem "activerecord", "~> 3.2"
2
+ gem "activerecord", github: "rails/rails" , branch: '3-2-stable'
3
3
  end
4
4
 
5
5
  appraise "activerecord-4.0" do
6
- gem "activerecord", "~> 4.0.0"
6
+ gem "activerecord", github: "rails/rails" , branch: '4-0-stable'
7
7
  end
8
8
 
9
9
  appraise "activerecord-4.1" do
10
- gem "activerecord", "~> 4.1.0"
10
+ gem "activerecord", github: "rails/rails" , branch: '4-1-stable'
11
11
  end
12
12
 
13
13
  appraise "activerecord-4.2" do
14
- gem "railties", ">= 4.2.0.beta1"
15
- gem "activerecord", ">= 4.2.0.beta1"
16
- gem "rack", ">= 1.6.0.beta"
17
- end
18
-
19
- appraise "activerecord-edge" do
20
- gem "activerecord", github: "rails/rails"
21
- gem 'arel', github: 'rails/arel'
14
+ gem "railties", github: "rails/rails" , branch: '4-2-stable'
15
+ gem "activerecord", github: "rails/rails" , branch: '4-2-stable'
22
16
  end
data/CHANGELOG.md CHANGED
@@ -4,17 +4,32 @@ Each change should fall into categories that would affect whether the release is
4
4
 
5
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
6
 
7
- ### Master [changes](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.4.0...master)
7
+ ### [3.5.0 / 2015-02-11](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.4.4...v3.5.0)
8
8
 
9
- * Breaking Changes
10
- * Features
11
9
  * Fixes
12
- * Performance
13
- * Misc
10
+ * [@rikettsie Fixed collation for MySql via rake rule or config parameter](https://github.com/mbleigh/acts-as-taggable-on/pull/634)
11
+
12
+ ### [3.4.4 / 2015-02-11](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.4.3...v3.4.4)
13
+
14
+ * Fixes
15
+ * [@d4rky-pl Add context constraint to find_related_* methods](https://github.com/mbleigh/acts-as-taggable-on/pull/629)
16
+
17
+
18
+ ### [3.4.3 / 2014-09-26](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.4.2...v3.4.3)
19
+
20
+ * Fixes
21
+ * [@warp clears column cache on reset_column_information resolves](https://github.com/mbleigh/acts-as-taggable-on/pull/621)
22
+
23
+ ### [3.4.2 / 2014-09-26](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.4.1...v3.4.2)
24
+
25
+ * Fixes
26
+ * [@stiff fixed tagged_with :any in postgresql](https://github.com/mbleigh/acts-as-taggable-on/pull/570)
27
+ * [@jerefrer fixed encoding in mysql](https://github.com/mbleigh/acts-as-taggable-on/pull/588)
28
+ * [@markedmondson Ensure taggings context aliases are maintained when joining multiple taggables](https://github.com/mbleigh/acts-as-taggable-on/pull/589)
14
29
 
15
30
  ### [3.4.1 / 2014-09-01](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.4.0...v3.4.1)
16
31
  * Fixes
17
- * [@konukhov fix owned ordered taggable bug]((https://github.com/mbleigh/acts-as-taggable-on/pull/585)
32
+ * [@konukhov fix owned ordered taggable bug](https://github.com/mbleigh/acts-as-taggable-on/pull/585)
18
33
 
19
34
  ### [3.4.0 / 2014-08-29](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.3.0...v3.4.0)
20
35
 
data/Gemfile CHANGED
@@ -7,4 +7,5 @@ group :local_development do
7
7
  gem 'guard-rspec'
8
8
  gem 'appraisal'
9
9
  gem 'rake'
10
- end
10
+ gem 'byebug' , platform: :mri_21
11
+ end
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # ActsAsTaggableOn
2
+ [![Gem Version](https://badge.fury.io/rb/acts-as-taggable-on.svg)](http://badge.fury.io/rb/acts-as-taggable-on)
2
3
  [![Build Status](https://secure.travis-ci.org/mbleigh/acts-as-taggable-on.png)](http://travis-ci.org/mbleigh/acts-as-taggable-on)
3
4
  [![Code Climate](https://codeclimate.com/github/mbleigh/acts-as-taggable-on.png)](https://codeclimate.com/github/mbleigh/acts-as-taggable-on)
4
5
  [![Inline docs](http://inch-ci.org/github/mbleigh/acts-as-taggable-on.png)](http://inch-ci.org/github/mbleigh/acts-as-taggable-on)
@@ -24,14 +25,14 @@ Versions 2.4.1 and up are compatible with Rails 4 too (thanks to arabonradar and
24
25
 
25
26
  Versions >= 3.x are compatible with Ruby 1.9.3+ and Rails 3 and 4.
26
27
 
27
- For an up-to-date roadmap, see https://github.com/mbleigh/acts-as-taggable-on/issues/milestones
28
+ For an up-to-date roadmap, see https://github.com/mbleigh/acts-as-taggable-on/milestones
28
29
 
29
30
  ## Installation
30
31
 
31
32
  To use it, add it to your Gemfile:
32
33
 
33
34
  ```ruby
34
- gem 'acts-as-taggable-on'
35
+ gem 'acts-as-taggable-on', '~> 3.4'
35
36
  ```
36
37
 
37
38
  and bundle:
@@ -56,6 +57,22 @@ Review the generated migrations then migrate :
56
57
  rake db:migrate
57
58
  ```
58
59
 
60
+ #### For MySql users
61
+ You can circumvent at any time the problem of special characters [issue 623](https://github.com/mbleigh/acts-as-taggable-on/issues/623) by setting in an initializer file:
62
+
63
+ ```ruby
64
+ ActsAsTaggableOn.force_binary_collation = true
65
+ ```
66
+
67
+ Or by running this rake task:
68
+
69
+ ```shell
70
+ rake acts_as_taggable_on_engine:tag_names:collate_bin
71
+ ```
72
+
73
+ See the Configuration section for more details, and a general note valid for older
74
+ version of the gem.
75
+
59
76
  #### Upgrading
60
77
 
61
78
  see [UPGRADING](UPGRADING.md)
@@ -72,7 +89,7 @@ end
72
89
 
73
90
  class UsersController < ApplicationController
74
91
  def user_params
75
- params.require(:user).permit(:name, :tag_list) ## Rails 4 strong params usage
92
+ params.require(:user).permit(:name, :tag_list => []) ## Rails 4 strong params usage
76
93
  end
77
94
  end
78
95
 
@@ -179,7 +196,7 @@ ActsAsTaggableOn::Tag.most_used
179
196
  ActsAsTaggableOn::Tag.least_used
180
197
  ```
181
198
 
182
- You can also filter the results by passing the method a limit, however the default limit is 50.
199
+ You can also filter the results by passing the method a limit, however the default limit is 20.
183
200
 
184
201
  ```ruby
185
202
  ActsAsTaggableOn::Tag.most_used(10)
@@ -261,7 +278,7 @@ If you want to change how tags are parsed, you can define a your own implementat
261
278
  ```ruby
262
279
  class MyParser < ActsAsTaggableOn::GenericParser
263
280
  def parse
264
- TagList.new.tap do |tag_list|
281
+ ActsAsTaggableOn::TagList.new.tap do |tag_list|
265
282
  tag_list.add @tag_list.split('|')
266
283
  end
267
284
  end
@@ -406,13 +423,28 @@ If you would like tags to be case-sensitive and not use LIKE queries for creatio
406
423
  ActsAsTaggableOn.strict_case_match = true
407
424
  ```
408
425
 
426
+ If you would like to have an exact match covering special characters with MySql:
427
+
428
+ ```ruby
429
+ ActsAsTaggableOn.force_binary_collation = true
430
+ ```
431
+
409
432
  If you want to change the default delimiter (it defaults to ','). You can also pass in an array of delimiters such as ([',', '|']):
410
433
 
411
434
  ```ruby
412
435
  ActsAsTaggableOn.delimiter = ','
413
436
  ```
414
437
 
415
- *NOTE: SQLite by default can't upcase or downcase multibyte characters, resulting in unwanted behavior. Load the SQLite ICU extension for proper handle of such characters. [See docs](http://www.sqlite.org/src/artifact?ci=trunk&filename=ext/icu/README.txt)*
438
+ *NOTE 1: SQLite by default can't upcase or downcase multibyte characters, resulting in unwanted behavior. Load the SQLite ICU extension for proper handle of such characters. [See docs](http://www.sqlite.org/src/artifact?ci=trunk&filename=ext/icu/README.txt)*
439
+
440
+ *NOTE 2: the option `force_binary_collation` is strongest than `strict_case_match` and when
441
+ set to true, the `strict_case_match` is ignored.
442
+ To roughly apply the `force_binary_collation` behaviour with a version of the gem <= 3.4.4, execute the following commands in the MySql console:*
443
+
444
+ ```shell
445
+ USE my_wonderful_app_db;
446
+ ALTER TABLE tags MODIFY name VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin;
447
+ ```
416
448
 
417
449
  ## Contributors
418
450
 
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/setup'
3
3
 
4
+ import "./lib/tasks/tags_collate_utf8.rake"
5
+
4
6
  desc 'Default: run specs'
5
7
  task default: :spec
6
8
 
data/UPGRADING.md CHANGED
@@ -4,10 +4,5 @@ Re-run the migrations generator
4
4
 
5
5
  rake acts_as_taggable_on_engine:install:migrations
6
6
 
7
- It will create any new migrations and skip existing ones
8
-
9
-
10
- ##Breaking changes:
11
-
12
- - ActsAsTaggableOn::Tag is not extend with ActsAsTaggableOn::Utils anymore.
13
- Please use ActsAsTaggableOn::Utils instead
7
+ This will create any new migrations and skip existing ones
8
+ Version 3.5.0 has a migration for mysql adapter
@@ -0,0 +1,9 @@
1
+ # This migration is added to circumvent issue #623 and have special characters
2
+ # work properly
3
+ class ChangeCollationForTagNames < ActiveRecord::Migration
4
+ def up
5
+ if ActsAsTaggableOn::Utils.using_mysql?
6
+ execute("ALTER TABLE tags MODIFY name varchar(255) CHARACTER SET utf8 COLLATE utf8_bin;")
7
+ end
8
+ end
9
+ end
@@ -2,13 +2,14 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 3.2"
5
+ gem "activerecord", :github => "rails/rails", :branch => "3-2-stable"
6
6
 
7
7
  group :local_development do
8
8
  gem "guard"
9
9
  gem "guard-rspec"
10
10
  gem "appraisal"
11
11
  gem "rake"
12
+ gem "byebug", :platform => :mri_21
12
13
  end
13
14
 
14
15
  gemspec :path => "../"
@@ -2,13 +2,14 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 4.0.0"
5
+ gem "activerecord", :github => "rails/rails", :branch => "4-0-stable"
6
6
 
7
7
  group :local_development do
8
8
  gem "guard"
9
9
  gem "guard-rspec"
10
10
  gem "appraisal"
11
11
  gem "rake"
12
+ gem "byebug", :platform => :mri_21
12
13
  end
13
14
 
14
15
  gemspec :path => "../"
@@ -2,13 +2,14 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 4.1.0"
5
+ gem "activerecord", :github => "rails/rails", :branch => "4-1-stable"
6
6
 
7
7
  group :local_development do
8
8
  gem "guard"
9
9
  gem "guard-rspec"
10
10
  gem "appraisal"
11
11
  gem "rake"
12
+ gem "byebug", :platform => :mri_21
12
13
  end
13
14
 
14
15
  gemspec :path => "../"
@@ -2,15 +2,15 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "railties", ">= 4.2.0.beta1"
6
- gem "activerecord", ">= 4.2.0.beta1"
7
- gem "rack", ">= 1.6.0.beta"
5
+ gem "railties", :github => "rails/rails", :branch => "4-2-stable"
6
+ gem "activerecord", :github => "rails/rails", :branch => "4-2-stable"
8
7
 
9
8
  group :local_development do
10
9
  gem "guard"
11
10
  gem "guard-rspec"
12
11
  gem "appraisal"
13
12
  gem "rake"
13
+ gem "byebug", :platform => :mri_21
14
14
  end
15
15
 
16
16
  gemspec :path => "../"
@@ -58,7 +58,8 @@ module ActsAsTaggableOn
58
58
 
59
59
  class Configuration
60
60
  attr_accessor :delimiter, :force_lowercase, :force_parameterize,
61
- :strict_case_match, :remove_unused_tags, :default_parser
61
+ :strict_case_match, :remove_unused_tags, :default_parser,
62
+ :tags_counter
62
63
 
63
64
  def initialize
64
65
  @delimiter = ','
@@ -66,7 +67,15 @@ module ActsAsTaggableOn
66
67
  @force_parameterize = false
67
68
  @strict_case_match = false
68
69
  @remove_unused_tags = false
70
+ @tags_counter = true
69
71
  @default_parser = DefaultParser
72
+ @force_binary_collation = false
73
+ end
74
+
75
+ def strict_case_match=(force_cs)
76
+ if @force_binary_collation == false
77
+ @strict_case_match = force_cs
78
+ end
70
79
  end
71
80
 
72
81
  def delimiter=(string)
@@ -77,6 +86,30 @@ a ActsAsTaggableOn.default_parser instead
77
86
  WARNING
78
87
  @delimiter = string
79
88
  end
89
+
90
+ def force_binary_collation=(force_bin)
91
+ if Utils.using_mysql?
92
+ if force_bin == true
93
+ Configuration.apply_binary_collation(true)
94
+ @force_binary_collation = true
95
+ @strict_case_match = true
96
+ else
97
+ Configuration.apply_binary_collation(false)
98
+ @force_binary_collation = false
99
+ end
100
+ end
101
+ end
102
+
103
+ def self.apply_binary_collation(bincoll)
104
+ if Utils.using_mysql?
105
+ coll = 'utf8_general_ci'
106
+ if bincoll == true
107
+ coll = 'utf8_bin'
108
+ end
109
+ ActiveRecord::Migration.execute("ALTER TABLE tags MODIFY name varchar(255) CHARACTER SET utf8 COLLATE #{coll};")
110
+ end
111
+ end
112
+
80
113
  end
81
114
 
82
115
  setup
@@ -6,7 +6,7 @@ module ActsAsTaggableOn
6
6
 
7
7
  ### ASSOCIATIONS:
8
8
 
9
- has_many :taggings, dependent: :destroy, class_name: 'ActsAsTaggableOn::Tagging'
9
+ has_many :taggings, dependent: :destroy, class_name: '::ActsAsTaggableOn::Tagging'
10
10
 
11
11
  ### VALIDATIONS:
12
12
 
@@ -32,17 +32,10 @@ module ActsAsTaggableOn
32
32
  end
33
33
 
34
34
  def self.named_any(list)
35
- if ActsAsTaggableOn.strict_case_match
36
- clause = list.map { |tag|
37
- sanitize_sql(["name = #{binary}?", as_8bit_ascii(tag)])
38
- }.join(' OR ')
39
- where(clause)
40
- else
41
- clause = list.map { |tag|
42
- sanitize_sql(['LOWER(name) = LOWER(?)', as_8bit_ascii(unicode_downcase(tag))])
43
- }.join(' OR ')
44
- where(clause)
45
- end
35
+ clause = list.map { |tag|
36
+ sanitize_sql_for_named_any(tag).force_encoding('BINARY')
37
+ }.join(' OR ')
38
+ where(clause)
46
39
  end
47
40
 
48
41
  def self.named_like(name)
@@ -135,6 +128,14 @@ module ActsAsTaggableOn
135
128
  string.to_s.mb_chars
136
129
  end
137
130
  end
131
+
132
+ def sanitize_sql_for_named_any(tag)
133
+ if ActsAsTaggableOn.strict_case_match
134
+ sanitize_sql(["name = #{binary}?", as_8bit_ascii(tag)])
135
+ else
136
+ sanitize_sql(['LOWER(name) = LOWER(?)', as_8bit_ascii(unicode_downcase(tag))])
137
+ end
138
+ end
138
139
  end
139
140
  end
140
141
  end
@@ -40,6 +40,10 @@ module ActsAsTaggableOn::Taggable
40
40
  end
41
41
  end
42
42
 
43
+ def reset_column_information
44
+ super
45
+ @acts_as_taggable_on_cache_columns = nil
46
+ end
43
47
  end
44
48
  end
45
49
 
@@ -169,7 +169,7 @@ module ActsAsTaggableOn::Taggable
169
169
  end
170
170
 
171
171
  module CalculationMethods
172
- def count(column_name=:all)
172
+ def count(column_name=:all, options = {})
173
173
  # https://github.com/rails/rails/commit/da9b5d4a8435b744fcf278fffd6d7f1e36d4a4f2
174
174
  super
175
175
  end
@@ -27,7 +27,7 @@ module ActsAsTaggableOn::Taggable
27
27
  dependent: :destroy,
28
28
  class_name: 'ActsAsTaggableOn::Tagging',
29
29
  order: taggings_order,
30
- conditions: ["#{ActsAsTaggableOn::Tagging.table_name}.context = (?)", tags_type],
30
+ conditions: {context: tags_type},
31
31
  include: :tag
32
32
 
33
33
  has_many_with_taggable_compatibility context_tags, through: context_taggings,
@@ -100,6 +100,7 @@ module ActsAsTaggableOn::Taggable
100
100
  context = options.delete(:on)
101
101
  owned_by = options.delete(:owned_by)
102
102
  alias_base_name = undecorated_table_name.gsub('.', '_')
103
+ # FIXME use ActiveRecord's connection quote_column_name
103
104
  quote = ActsAsTaggableOn::Utils.using_postgresql? ? '"' : ''
104
105
 
105
106
  if options.delete(:exclude)
@@ -117,7 +118,7 @@ module ActsAsTaggableOn::Taggable
117
118
  " AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)}" +
118
119
  " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = #{quote_value(owned_by.id, nil)}" +
119
120
  " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = #{quote_value(owned_by.class.base_class.to_s, nil)}"
120
-
121
+
121
122
  joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
122
123
  joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
123
124
  end
@@ -130,7 +131,7 @@ module ActsAsTaggableOn::Taggable
130
131
  ActsAsTaggableOn::Tag.named_any(tag_list)
131
132
  end
132
133
 
133
- return empty_result unless tags.length > 0
134
+ return empty_result if tags.length == 0
134
135
 
135
136
  # setup taggings alias so we can chain, ex: items_locations_taggings_awesome_cool_123
136
137
  # avoid ambiguous column name
@@ -140,21 +141,22 @@ module ActsAsTaggableOn::Taggable
140
141
  "#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tags.map(&:name).join('_'))}"
141
142
  )
142
143
 
143
- tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
144
- " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
144
+ tagging_cond = "#{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
145
+ " WHERE #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
145
146
  " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
146
147
 
147
- tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
148
- tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
148
+ tagging_cond << " AND " + sanitize_sql(["#{taggings_alias}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
149
+ tagging_cond << " AND " + sanitize_sql(["#{taggings_alias}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
149
150
 
150
- tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
151
+ tagging_cond << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
151
152
 
152
153
  # don't need to sanitize sql, map all ids and join with OR logic
153
- conditions << tags.map { |t| "#{taggings_alias}.tag_id = #{quote_value(t.id, nil)}" }.join(' OR ')
154
+ tag_ids = tags.map { |t| quote_value(t.id, nil) }.join(', ')
155
+ tagging_cond << " AND #{taggings_alias}.tag_id in (#{tag_ids})"
154
156
  select_clause << " #{table_name}.*" unless context and tag_types.one?
155
157
 
156
158
  if owned_by
157
- tagging_join << ' AND ' +
159
+ tagging_cond << ' AND ' +
158
160
  sanitize_sql([
159
161
  "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
160
162
  owned_by.id,
@@ -162,10 +164,9 @@ module ActsAsTaggableOn::Taggable
162
164
  ])
163
165
  end
164
166
 
165
- joins << tagging_join
166
- unless any == 'distinct' # Fix issue #544
167
- group = "#{table_name}.#{primary_key}"
168
- select_clause << group
167
+ conditions << "EXISTS (SELECT 1 FROM #{tagging_cond})"
168
+ if options.delete(:order_by_matching_tag_count)
169
+ order_by << "(SELECT count(*) FROM #{tagging_cond}) desc"
169
170
  end
170
171
  else
171
172
  tags = ActsAsTaggableOn::Tag.named_any(tag_list)
@@ -224,11 +225,11 @@ module ActsAsTaggableOn::Taggable
224
225
  query = self
225
226
  query = self.select(select_clause.join(',')) unless select_clause.empty?
226
227
  query.joins(joins.join(' '))
227
- .where(conditions.join(' AND '))
228
- .group(group)
229
- .having(having)
230
- .order(order_by.join(', '))
231
- .readonly(false)
228
+ .where(conditions.join(' AND '))
229
+ .group(group)
230
+ .having(having)
231
+ .order(order_by.join(', '))
232
+ .readonly(false)
232
233
  end
233
234
 
234
235
  def is_taggable?
@@ -43,7 +43,7 @@ module ActsAsTaggableOn::Taggable
43
43
  def related_tags_for(context, klass, options = {})
44
44
  tags_to_ignore = Array.wrap(options[:ignore]).map(&:to_s) || []
45
45
  tags_to_find = tags_on(context).map { |t| t.name }.reject { |t| tags_to_ignore.include? t }
46
- related_where(klass, ["#{exclude_self(klass, id)} #{klass.table_name}.#{klass.primary_key} = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = '#{klass.base_class}' AND #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key} AND #{ActsAsTaggableOn::Tag.table_name}.name IN (?)", tags_to_find])
46
+ related_where(klass, ["#{exclude_self(klass, id)} #{klass.table_name}.#{klass.primary_key} = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = '#{klass.base_class}' AND #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key} AND #{ActsAsTaggableOn::Tag.table_name}.name IN (?) AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_to_find, context])
47
47
  end
48
48
 
49
49
  private
@@ -80,8 +80,8 @@ module ActsAsTaggableOn
80
80
  self.preserve_tag_order = preserve_tag_order
81
81
 
82
82
  class_eval do
83
- has_many :taggings, as: :taggable, dependent: :destroy, class_name: 'ActsAsTaggableOn::Tagging'
84
- has_many :base_tags, through: :taggings, source: :tag, class_name: 'ActsAsTaggableOn::Tag'
83
+ has_many :taggings, as: :taggable, dependent: :destroy, class_name: '::ActsAsTaggableOn::Tagging'
84
+ has_many :base_tags, through: :taggings, source: :tag, class_name: '::ActsAsTaggableOn::Tag'
85
85
 
86
86
  def self.taggable?
87
87
  true
@@ -19,13 +19,13 @@ module ActsAsTaggableOn
19
19
  opts.merge(
20
20
  as: :tagger,
21
21
  dependent: :destroy,
22
- class_name: 'ActsAsTaggableOn::Tagging'
22
+ class_name: '::ActsAsTaggableOn::Tagging'
23
23
  )
24
24
 
25
25
  has_many_with_taggable_compatibility :owned_tags,
26
26
  through: :owned_taggings,
27
27
  source: :tag,
28
- class_name: 'ActsAsTaggableOn::Tag',
28
+ class_name: '::ActsAsTaggableOn::Tag',
29
29
  uniq: true
30
30
  end
31
31
 
@@ -11,7 +11,7 @@ module ActsAsTaggableOn
11
11
  :tagger_type,
12
12
  :tagger_id if defined?(ActiveModel::MassAssignmentSecurity)
13
13
 
14
- belongs_to :tag, class_name: 'ActsAsTaggableOn::Tag' , counter_cache: true
14
+ belongs_to :tag, class_name: '::ActsAsTaggableOn::Tag', counter_cache: ActsAsTaggableOn.tags_counter
15
15
  belongs_to :taggable, polymorphic: true
16
16
  belongs_to :tagger, polymorphic: true
17
17
 
@@ -32,7 +32,11 @@ module ActsAsTaggableOn
32
32
 
33
33
  def remove_unused_tags
34
34
  if ActsAsTaggableOn.remove_unused_tags
35
- tag.destroy if tag.reload.taggings_count.zero?
35
+ if ActsAsTaggableOn.tags_counter
36
+ tag.destroy if tag.reload.taggings_count.zero?
37
+ else
38
+ tag.destroy if tag.reload.taggings.count.zero?
39
+ end
36
40
  end
37
41
  end
38
42
  end
@@ -18,7 +18,7 @@ module ActsAsTaggableOn
18
18
  end
19
19
 
20
20
  def sha_prefix(string)
21
- Digest::SHA1.hexdigest("#{string}#{rand}")[0..6]
21
+ Digest::SHA1.hexdigest(string)[0..6]
22
22
  end
23
23
 
24
24
  def active_record4?
@@ -1,4 +1,4 @@
1
1
  module ActsAsTaggableOn
2
- VERSION = '3.4.1'
2
+ VERSION = '3.5.0'
3
3
  end
4
4
 
@@ -0,0 +1,21 @@
1
+ # These rake tasks are to be run by MySql users only, they fix the management of
2
+ # binary-encoded strings for tag 'names'. Issues:
3
+ # https://github.com/mbleigh/acts-as-taggable-on/issues/623
4
+
5
+ namespace :acts_as_taggable_on_engine do
6
+
7
+ namespace :tag_names do
8
+
9
+ desc "Forcing collate of tag names to utf8_bin"
10
+ task :collate_bin => [:environment] do |t, args|
11
+ ActsAsTaggableOn::Configuration.apply_binary_collation(true)
12
+ end
13
+
14
+ desc "Forcing collate of tag names to utf8_general_ci"
15
+ task :collate_ci => [:environment] do |t, args|
16
+ ActsAsTaggableOn::Configuration.apply_binary_collation(false)
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -133,6 +133,22 @@ describe 'Acts As Taggable On' do
133
133
  expect(taggable1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(taggable1)
134
134
  end
135
135
 
136
+ it 'should ensure joins to multiple taggings maintain their contexts when aliasing' do
137
+ taggable1 = TaggableModel.create!(name: 'Taggable 1')
138
+
139
+ taggable1.offering_list = 'one'
140
+ taggable1.need_list = 'two'
141
+
142
+ taggable1.save
143
+
144
+ column = TaggableModel.connection.quote_column_name("context")
145
+ offer_alias = TaggableModel.connection.quote_table_name("taggings")
146
+ need_alias = TaggableModel.connection.quote_table_name("need_taggings_taggable_models_join")
147
+
148
+ expect(TaggableModel.joins(:offerings, :needs).to_sql).to include "#{offer_alias}.#{column}"
149
+ expect(TaggableModel.joins(:offerings, :needs).to_sql).to include "#{need_alias}.#{column}"
150
+ end
151
+
136
152
  end
137
153
 
138
154
  describe 'Tagging Contexts' do
@@ -69,6 +69,12 @@ describe 'Acts As Taggable On' do
69
69
  @taggable.save!
70
70
  expect(@taggable.tag_list.sort).to eq(%w(awesome epic).sort)
71
71
  end
72
+
73
+ it 'should clear the cache on reset_column_information' do
74
+ CachedModel.column_names
75
+ CachedModel.reset_column_information
76
+ expect(CachedModel.instance_variable_get(:@acts_as_taggable_on_cache_columns)).to eql(nil)
77
+ end
72
78
  end
73
79
 
74
80
  describe 'CachingWithArray' do
@@ -42,6 +42,15 @@ describe 'Acts As Taggable On' do
42
42
  expect(taggable1.find_related_tags_for(OtherTaggableModel)).to_not include(taggable2)
43
43
  end
44
44
 
45
+ it 'should find other related objects based on tags only from particular context' do
46
+ taggable1 = TaggableModel.create!(name: 'Taggable 1',tag_list: 'one, two')
47
+ taggable2 = TaggableModel.create!(name: 'Taggable 2',tag_list: 'three, four', skill_list: 'one, two')
48
+ taggable3 = TaggableModel.create!(name: 'Taggable 3',tag_list: 'one, four')
49
+
50
+ expect(taggable1.find_related_tags).to include(taggable3)
51
+ expect(taggable1.find_related_tags).to_not include(taggable2)
52
+ end
53
+
45
54
 
46
55
  shared_examples "a collection" do
47
56
  it do
@@ -2,6 +2,7 @@
2
2
  require 'spec_helper'
3
3
  require 'db/migrate/2_add_missing_unique_indices.rb'
4
4
 
5
+
5
6
  shared_examples_for 'without unique index' do
6
7
  prepend_before(:all) { AddMissingUniqueIndices.down }
7
8
  append_after(:all) do
@@ -46,6 +47,14 @@ describe ActsAsTaggableOn::Tag do
46
47
  end
47
48
  end
48
49
 
50
+ describe 'named any' do
51
+ context 'with some special characters combinations', if: using_mysql? do
52
+ it 'should not raise an invalid encoding exception' do
53
+ expect{ActsAsTaggableOn::Tag.named_any(["holä", "hol'ä"])}.not_to raise_error
54
+ end
55
+ end
56
+ end
57
+
49
58
  describe 'find or create by name' do
50
59
  before(:each) do
51
60
  @tag.name = 'awesome'
@@ -250,13 +259,19 @@ describe ActsAsTaggableOn::Tag do
250
259
  end
251
260
  end
252
261
 
253
- it 'should not change enconding' do
262
+ it 'should not change encoding' do
254
263
  name = "\u3042"
255
264
  original_encoding = name.encoding
256
265
  record = ActsAsTaggableOn::Tag.find_or_create_with_like_by_name(name)
257
266
  record.reload
258
267
  expect(record.name.encoding).to eq(original_encoding)
259
268
  end
269
+
270
+ context 'named any with some special characters combinations', if: using_mysql? do
271
+ it 'should not raise an invalid encoding exception' do
272
+ expect{ActsAsTaggableOn::Tag.named_any(["holä", "hol'ä"])}.not_to raise_error
273
+ end
274
+ end
260
275
  end
261
276
 
262
277
  describe 'name uniqeness validation' do
@@ -295,7 +310,7 @@ describe ActsAsTaggableOn::Tag do
295
310
  tag.save!
296
311
  end
297
312
  end
298
-
313
+
299
314
  it 'should find the most popular tags' do
300
315
  expect(ActsAsTaggableOn::Tag.most_used(3).first.name).to eq("golden_syrup")
301
316
  expect(ActsAsTaggableOn::Tag.most_used(3).length).to eq(3)
@@ -306,4 +321,5 @@ describe ActsAsTaggableOn::Tag do
306
321
  expect(ActsAsTaggableOn::Tag.least_used(3).length).to eq(3)
307
322
  end
308
323
  end
324
+
309
325
  end
@@ -261,7 +261,7 @@ describe 'Taggable' do
261
261
  @taggable.tag_list = 'bob, charlie'
262
262
  @taggable.save
263
263
 
264
- expect(TaggableModel.group(:created_at).tagged_with(['bob', 'css'], :any => true).first).to eq(@taggable)
264
+ expect(TaggableModel.tagged_with(['bob', 'css'], :any => true).to_a).to eq([@taggable])
265
265
 
266
266
  bob = TaggableModel.create(:name => 'Bob', :tag_list => 'ruby, rails, css')
267
267
  frank = TaggableModel.create(:name => 'Frank', :tag_list => 'ruby, rails')
@@ -459,6 +459,7 @@ describe 'Taggable' do
459
459
 
460
460
  expect(TaggableModel.tagged_with(%w(bob tricia), wild: true, any: true).to_a.sort_by { |o| o.id }).to eq([bob, frank, steve])
461
461
  expect(TaggableModel.tagged_with(%w(bob tricia), wild: true, exclude: true).to_a).to eq([jim])
462
+ expect(TaggableModel.tagged_with('ji', wild: true, any: true).to_a).to eq([frank, jim])
462
463
  end
463
464
  end
464
465
 
@@ -13,4 +13,11 @@ describe ActsAsTaggableOn::Utils do
13
13
  expect(ActsAsTaggableOn::Utils.like_operator).to eq('LIKE')
14
14
  end
15
15
  end
16
+
17
+ describe '#sha_prefix' do
18
+ it 'should return a consistent prefix for a given word' do
19
+ expect(ActsAsTaggableOn::Utils.sha_prefix('kittens')).to eq(ActsAsTaggableOn::Utils.sha_prefix('kittens'))
20
+ expect(ActsAsTaggableOn::Utils.sha_prefix('puppies')).not_to eq(ActsAsTaggableOn::Utils.sha_prefix('kittens'))
21
+ end
22
+ end
16
23
  end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,7 @@
1
+ begin
2
+ require 'byebug'
3
+ rescue LoadError
4
+ end
1
5
  $LOAD_PATH << '.' unless $LOAD_PATH.include?('.')
2
6
  $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
3
7
  require 'logger'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts-as-taggable-on
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.1
4
+ version: 3.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-09-01 00:00:00.000000000 Z
12
+ date: 2015-03-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -169,11 +169,11 @@ files:
169
169
  - db/migrate/2_add_missing_unique_indices.rb
170
170
  - db/migrate/3_add_taggings_counter_cache_to_tags.rb
171
171
  - db/migrate/4_add_missing_taggable_index.rb
172
+ - db/migrate/5_change_collation_for_tag_names.rb
172
173
  - gemfiles/activerecord_3.2.gemfile
173
174
  - gemfiles/activerecord_4.0.gemfile
174
175
  - gemfiles/activerecord_4.1.gemfile
175
176
  - gemfiles/activerecord_4.2.gemfile
176
- - gemfiles/activerecord_edge.gemfile
177
177
  - lib/acts-as-taggable-on.rb
178
178
  - lib/acts_as_taggable_on.rb
179
179
  - lib/acts_as_taggable_on/compatibility.rb
@@ -195,6 +195,7 @@ files:
195
195
  - lib/acts_as_taggable_on/tags_helper.rb
196
196
  - lib/acts_as_taggable_on/utils.rb
197
197
  - lib/acts_as_taggable_on/version.rb
198
+ - lib/tasks/tags_collate_utf8.rake
198
199
  - spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb
199
200
  - spec/acts_as_taggable_on/acts_as_tagger_spec.rb
200
201
  - spec/acts_as_taggable_on/caching_spec.rb
@@ -244,13 +245,8 @@ post_install_message: |-
244
245
 
245
246
  rake acts_as_taggable_on_engine:install:migrations
246
247
 
247
- It will create any new migrations and skip existing ones
248
-
249
-
250
- ##Breaking changes:
251
-
252
- - ActsAsTaggableOn::Tag is not extend with ActsAsTaggableOn::Utils anymore.
253
- Please use ActsAsTaggableOn::Utils instead
248
+ This will create any new migrations and skip existing ones
249
+ Version 3.5.0 has a migration for mysql adapter
254
250
  rdoc_options: []
255
251
  require_paths:
256
252
  - lib
@@ -266,7 +262,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
266
262
  version: '0'
267
263
  requirements: []
268
264
  rubyforge_project:
269
- rubygems_version: 2.4.1
265
+ rubygems_version: 2.4.5
270
266
  signing_key:
271
267
  specification_version: 4
272
268
  summary: Advanced tagging for Rails.
@@ -309,4 +305,3 @@ test_files:
309
305
  - spec/support/array.rb
310
306
  - spec/support/database.rb
311
307
  - spec/support/database_cleaner.rb
312
- has_rdoc:
@@ -1,15 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "activerecord", :github => "rails/rails"
6
- gem "arel", :github => "rails/arel"
7
-
8
- group :local_development do
9
- gem "guard"
10
- gem "guard-rspec"
11
- gem "appraisal"
12
- gem "rake"
13
- end
14
-
15
- gemspec :path => "../"