acts-as-taggable-on 3.5.0 → 4.0.0.pre
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 +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +14 -18
- data/Appraisals +9 -8
- data/CHANGELOG.md +3 -1
- data/Gemfile +1 -1
- data/README.md +41 -3
- data/acts-as-taggable-on.gemspec +3 -3
- data/db/migrate/2_add_missing_unique_indices.rb +3 -2
- data/db/migrate/6_add_missing_indexes.rb +12 -0
- data/gemfiles/activerecord_4.0.gemfile +3 -2
- data/gemfiles/activerecord_4.1.gemfile +3 -2
- data/gemfiles/activerecord_4.2.gemfile +2 -3
- data/gemfiles/{activerecord_3.2.gemfile → activerecord_5.0.gemfile} +2 -2
- data/lib/acts-as-taggable-on.rb +16 -12
- data/lib/acts_as_taggable_on/engine.rb +0 -1
- data/lib/acts_as_taggable_on/tag.rb +6 -2
- data/lib/acts_as_taggable_on/tag_list.rb +2 -13
- data/lib/acts_as_taggable_on/taggable/cache.rb +1 -1
- data/lib/acts_as_taggable_on/taggable/collection.rb +6 -3
- data/lib/acts_as_taggable_on/taggable/core.rb +21 -16
- data/lib/acts_as_taggable_on/taggable/ownership.rb +3 -3
- data/lib/acts_as_taggable_on/tagger.rb +12 -11
- data/lib/acts_as_taggable_on/tagging.rb +4 -14
- data/lib/acts_as_taggable_on/utils.rb +2 -3
- data/lib/acts_as_taggable_on/version.rb +1 -1
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +10 -1
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +1 -1
- data/spec/acts_as_taggable_on/caching_spec.rb +22 -0
- data/spec/acts_as_taggable_on/tag_list_spec.rb +27 -1
- data/spec/acts_as_taggable_on/tag_spec.rb +15 -0
- data/spec/acts_as_taggable_on/taggable_spec.rb +19 -5
- data/spec/acts_as_taggable_on/tagging_spec.rb +64 -10
- data/spec/internal/db/schema.rb +7 -3
- data/spec/support/database.rb +1 -7
- metadata +12 -21
- data/lib/acts_as_taggable_on/compatibility.rb +0 -35
- data/lib/acts_as_taggable_on/tag_list_parser.rb +0 -21
- data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +0 -46
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8c10b1bdc1cfdce1f5faa7db0cb813257657bcda
|
|
4
|
+
data.tar.gz: fdc07fbeaf0a0fe45e1410b2f6a52078a168ff8d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1542dd8c7ebafeed4794f6f734da1e98f9641d7ddda18fa93c7e49ec4d5b14867353e065a6f3598050d0606f3e2809c5034a07c9f91385f22c90159dad202c50
|
|
7
|
+
data.tar.gz: aba26b02cbc655676003bfb6d73092ab49dae44013985a6838b187b3f4920507e684e73fab8a53c7d8b11e036cbc58a608943e3116ebee4ff10bf7df8fea6e80
|
data/.travis.yml
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
language: ruby
|
|
2
|
+
cache: bundler
|
|
2
3
|
|
|
3
4
|
rvm:
|
|
5
|
+
- 2.3.1
|
|
6
|
+
- 2.2.5
|
|
4
7
|
- 2.1
|
|
5
|
-
- 2.2
|
|
6
8
|
- 2.0.0
|
|
7
|
-
- 1.9.3
|
|
8
|
-
- rbx-2
|
|
9
9
|
|
|
10
10
|
env:
|
|
11
11
|
- DB=sqlite3
|
|
@@ -13,28 +13,24 @@ env:
|
|
|
13
13
|
- DB=postgresql
|
|
14
14
|
|
|
15
15
|
gemfile:
|
|
16
|
-
- gemfiles/
|
|
17
|
-
- gemfiles/activerecord_4.
|
|
16
|
+
- gemfiles/activerecord_5.0.gemfile
|
|
17
|
+
- gemfiles/activerecord_4.2.gemfile
|
|
18
18
|
- gemfiles/activerecord_4.1.gemfile
|
|
19
|
+
- gemfiles/activerecord_4.0.gemfile
|
|
19
20
|
|
|
20
21
|
sudo: false
|
|
21
22
|
|
|
22
23
|
bundler_args: '--without local_development --jobs 3 --retry 3'
|
|
23
24
|
|
|
25
|
+
before_install:
|
|
26
|
+
- gem install bundler
|
|
27
|
+
|
|
24
28
|
script: bundle exec rake
|
|
25
29
|
|
|
26
30
|
matrix:
|
|
27
|
-
fast_finish: true
|
|
28
|
-
allow_failures:
|
|
29
|
-
- gemfile: gemfiles/activerecord_edge.gemfile
|
|
30
|
-
- rvm: rbx-2
|
|
31
31
|
exclude:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
gemfile: gemfiles/activerecord_4.1.gemfile
|
|
38
|
-
- rvm: rbx-2
|
|
39
|
-
gemfile: gemfiles/activerecord_3.2.gemfile
|
|
40
|
-
|
|
32
|
+
- rvm: 2.0.0
|
|
33
|
+
gemfile: gemfiles/activerecord_5.0.gemfile
|
|
34
|
+
- rvm: 2.1
|
|
35
|
+
gemfile: gemfiles/activerecord_5.0.gemfile
|
|
36
|
+
fast_finish: true
|
data/Appraisals
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
appraise
|
|
2
|
-
gem
|
|
1
|
+
appraise 'activerecord-5.0' do
|
|
2
|
+
gem 'activerecord', "~> 5.0.0"
|
|
3
3
|
end
|
|
4
4
|
|
|
5
|
-
appraise "activerecord-4.
|
|
6
|
-
gem "activerecord",
|
|
5
|
+
appraise "activerecord-4.2" do
|
|
6
|
+
gem "activerecord", "~> 4.2.0"
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
appraise "activerecord-4.1" do
|
|
10
|
-
gem "activerecord",
|
|
10
|
+
gem "activerecord", "~> 4.1.0"
|
|
11
|
+
gem 'mysql2', '~> 0.3.21'
|
|
11
12
|
end
|
|
12
13
|
|
|
13
|
-
appraise "activerecord-4.
|
|
14
|
-
gem "
|
|
15
|
-
gem
|
|
14
|
+
appraise "activerecord-4.0" do
|
|
15
|
+
gem "activerecord", "~> 4.0.0"
|
|
16
|
+
gem 'mysql2', '~> 0.3.21'
|
|
16
17
|
end
|
data/CHANGELOG.md
CHANGED
|
@@ -4,10 +4,12 @@ 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
|
-
### [3.5.0 / 2015-
|
|
7
|
+
### [3.5.0 / 2015-03-03](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.4.4...v3.5.0)
|
|
8
8
|
|
|
9
9
|
* Fixes
|
|
10
10
|
* [@rikettsie Fixed collation for MySql via rake rule or config parameter](https://github.com/mbleigh/acts-as-taggable-on/pull/634)
|
|
11
|
+
*Misc
|
|
12
|
+
* [@pcupueran Add rspec test for tagging_spec completeness]()
|
|
11
13
|
|
|
12
14
|
### [3.4.4 / 2015-02-11](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.4.3...v3.4.4)
|
|
13
15
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# ActsAsTaggableOn
|
|
2
|
+
|
|
3
|
+
[](https://gitter.im/mbleigh/acts-as-taggable-on?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
2
4
|
[](http://badge.fury.io/rb/acts-as-taggable-on)
|
|
3
5
|
[](http://travis-ci.org/mbleigh/acts-as-taggable-on)
|
|
4
6
|
[](https://codeclimate.com/github/mbleigh/acts-as-taggable-on)
|
|
@@ -89,7 +91,7 @@ end
|
|
|
89
91
|
|
|
90
92
|
class UsersController < ApplicationController
|
|
91
93
|
def user_params
|
|
92
|
-
params.require(:user).permit(:name, :tag_list
|
|
94
|
+
params.require(:user).permit(:name, :tag_list) ## Rails 4 strong params usage
|
|
93
95
|
end
|
|
94
96
|
end
|
|
95
97
|
|
|
@@ -273,7 +275,7 @@ User.tagged_with("same", :on => :customs) # => [@user]
|
|
|
273
275
|
|
|
274
276
|
### Tag Parsers
|
|
275
277
|
|
|
276
|
-
If you want to change how tags are parsed, you can define
|
|
278
|
+
If you want to change how tags are parsed, you can define your own implementation:
|
|
277
279
|
|
|
278
280
|
```ruby
|
|
279
281
|
class MyParser < ActsAsTaggableOn::GenericParser
|
|
@@ -302,7 +304,7 @@ Now you can use this parser, passing it as parameter:
|
|
|
302
304
|
Or change it globally:
|
|
303
305
|
|
|
304
306
|
```ruby
|
|
305
|
-
|
|
307
|
+
ActsAsTaggableOn.default_parser = MyParser
|
|
306
308
|
@user = User.new(:name => "Bobby")
|
|
307
309
|
@user.tag_list = "east|south"
|
|
308
310
|
@user.tag_list # => ["east", "south"]
|
|
@@ -331,6 +333,42 @@ Photo.tagged_with("paris", :on => :locations, :owned_by => @some_user)
|
|
|
331
333
|
@some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations, :skip_save => true) #won't save @some_photo object
|
|
332
334
|
```
|
|
333
335
|
|
|
336
|
+
#### Working with Owned Tags
|
|
337
|
+
Note that `tag_list` only returns tags whose taggings do not have an owner. Continuing from the above example:
|
|
338
|
+
```ruby
|
|
339
|
+
@some_photo.tag_list # => []
|
|
340
|
+
```
|
|
341
|
+
To retrieve all tags of an object (regardless of ownership) or if only one owner can tag the object, use `all_tags_list`.
|
|
342
|
+
|
|
343
|
+
##### Adding owned tags
|
|
344
|
+
Note that **owned tags** are added all at once, in the form of ***comma seperated tags*** in string.
|
|
345
|
+
Also, when you try to add **owned tags** again, it simply overwrites the previous set of **owned tags**.
|
|
346
|
+
So to append tags in previously existing **owned tags** list, go as follows:
|
|
347
|
+
```ruby
|
|
348
|
+
def add_owned_tag
|
|
349
|
+
@some_item = Item.find(params[:id])
|
|
350
|
+
owned_tag_list = @some_item.all_tag_list - @some_item.tag_list
|
|
351
|
+
owned_tag_list += [(params[:tag])]
|
|
352
|
+
@tag_owner.tag(@some_item, :with => stringify(owned_tag_list), :on => :tags)
|
|
353
|
+
@some_item.save
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def stringify(tag_list)
|
|
357
|
+
tag_list.inject('') { |memo, tag| memo += (tag + ',') }[0..-1]
|
|
358
|
+
end
|
|
359
|
+
```
|
|
360
|
+
##### Removing owned tags
|
|
361
|
+
Similarly as above, removing will be as follows:
|
|
362
|
+
```ruby
|
|
363
|
+
def remove_owned_tag
|
|
364
|
+
@some_item = Item.find(params[:id])
|
|
365
|
+
owned_tag_list = @some_item.all_tag_list - @some_item.tag_list
|
|
366
|
+
owned_tag_list -= [(params[:tag])]
|
|
367
|
+
@tag_owner.tag(@some_item, :with => stringify(owned_tag_list), :on => :tags)
|
|
368
|
+
@some_item.save
|
|
369
|
+
end
|
|
370
|
+
```
|
|
371
|
+
|
|
334
372
|
### Dirty objects
|
|
335
373
|
|
|
336
374
|
```ruby
|
data/acts-as-taggable-on.gemspec
CHANGED
|
@@ -16,16 +16,16 @@ Gem::Specification.new do |gem|
|
|
|
16
16
|
gem.files = `git ls-files`.split($/)
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^spec/})
|
|
18
18
|
gem.require_paths = ['lib']
|
|
19
|
-
gem.required_ruby_version = '>=
|
|
19
|
+
gem.required_ruby_version = '>= 2.0.0'
|
|
20
20
|
|
|
21
21
|
if File.exist?('UPGRADING.md')
|
|
22
22
|
gem.post_install_message = File.read('UPGRADING.md')
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
gem.add_runtime_dependency 'activerecord',
|
|
25
|
+
gem.add_runtime_dependency 'activerecord', ['>= 4.0']
|
|
26
26
|
|
|
27
27
|
gem.add_development_dependency 'sqlite3'
|
|
28
|
-
gem.add_development_dependency 'mysql2', '~> 0.3
|
|
28
|
+
gem.add_development_dependency 'mysql2', '~> 0.3'
|
|
29
29
|
gem.add_development_dependency 'pg'
|
|
30
30
|
|
|
31
31
|
gem.add_development_dependency 'rspec-rails'
|
|
@@ -2,7 +2,7 @@ class AddMissingUniqueIndices < ActiveRecord::Migration
|
|
|
2
2
|
def self.up
|
|
3
3
|
add_index :tags, :name, unique: true
|
|
4
4
|
|
|
5
|
-
remove_index :taggings, :tag_id
|
|
5
|
+
remove_index :taggings, :tag_id if index_exists?(:taggings, :tag_id)
|
|
6
6
|
remove_index :taggings, [:taggable_id, :taggable_type, :context]
|
|
7
7
|
add_index :taggings,
|
|
8
8
|
[:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type],
|
|
@@ -13,7 +13,8 @@ class AddMissingUniqueIndices < ActiveRecord::Migration
|
|
|
13
13
|
remove_index :tags, :name
|
|
14
14
|
|
|
15
15
|
remove_index :taggings, name: 'taggings_idx'
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
add_index :taggings, :tag_id unless index_exists?(:taggings, :tag_id)
|
|
17
18
|
add_index :taggings, [:taggable_id, :taggable_type, :context]
|
|
18
19
|
end
|
|
19
20
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class AddMissingIndexes < ActiveRecord::Migration
|
|
2
|
+
def change
|
|
3
|
+
add_index :taggings, :tag_id
|
|
4
|
+
add_index :taggings, :taggable_id
|
|
5
|
+
add_index :taggings, :taggable_type
|
|
6
|
+
add_index :taggings, :tagger_id
|
|
7
|
+
add_index :taggings, :context
|
|
8
|
+
|
|
9
|
+
add_index :taggings, [:tagger_id, :tagger_type]
|
|
10
|
+
add_index :taggings, [:taggable_id, :taggable_type, :tagger_id, :context], name: 'taggings_idy'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
source "https://rubygems.org"
|
|
4
4
|
|
|
5
|
-
gem "activerecord",
|
|
5
|
+
gem "activerecord", "~> 4.0.0"
|
|
6
|
+
gem "mysql2", "~> 0.3.21"
|
|
6
7
|
|
|
7
8
|
group :local_development do
|
|
8
9
|
gem "guard"
|
|
9
10
|
gem "guard-rspec"
|
|
10
11
|
gem "appraisal"
|
|
11
12
|
gem "rake"
|
|
12
|
-
gem "byebug", :
|
|
13
|
+
gem "byebug", :platforms => [:mri_21, :mri_22, :mri_23]
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
gemspec :path => "../"
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
source "https://rubygems.org"
|
|
4
4
|
|
|
5
|
-
gem "activerecord",
|
|
5
|
+
gem "activerecord", "~> 4.1.0"
|
|
6
|
+
gem "mysql2", "~> 0.3.21"
|
|
6
7
|
|
|
7
8
|
group :local_development do
|
|
8
9
|
gem "guard"
|
|
9
10
|
gem "guard-rspec"
|
|
10
11
|
gem "appraisal"
|
|
11
12
|
gem "rake"
|
|
12
|
-
gem "byebug", :
|
|
13
|
+
gem "byebug", :platforms => [:mri_21, :mri_22, :mri_23]
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
gemspec :path => "../"
|
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
source "https://rubygems.org"
|
|
4
4
|
|
|
5
|
-
gem "
|
|
6
|
-
gem "activerecord", :github => "rails/rails", :branch => "4-2-stable"
|
|
5
|
+
gem "activerecord", "~> 4.2.0"
|
|
7
6
|
|
|
8
7
|
group :local_development do
|
|
9
8
|
gem "guard"
|
|
10
9
|
gem "guard-rspec"
|
|
11
10
|
gem "appraisal"
|
|
12
11
|
gem "rake"
|
|
13
|
-
gem "byebug", :
|
|
12
|
+
gem "byebug", :platforms => [:mri_21, :mri_22, :mri_23]
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
gemspec :path => "../"
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
source "https://rubygems.org"
|
|
4
4
|
|
|
5
|
-
gem "activerecord",
|
|
5
|
+
gem "activerecord", "~> 5.0.0"
|
|
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", :
|
|
12
|
+
gem "byebug", :platforms => [:mri_21, :mri_22, :mri_23]
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
gemspec :path => "../"
|
data/lib/acts-as-taggable-on.rb
CHANGED
|
@@ -2,7 +2,12 @@ require 'active_record'
|
|
|
2
2
|
require 'active_record/version'
|
|
3
3
|
require 'active_support/core_ext/module'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
begin
|
|
6
|
+
require 'rails/engine'
|
|
7
|
+
require 'acts_as_taggable_on/engine'
|
|
8
|
+
rescue LoadError
|
|
9
|
+
|
|
10
|
+
end
|
|
6
11
|
|
|
7
12
|
require 'digest/sha1'
|
|
8
13
|
|
|
@@ -13,7 +18,6 @@ module ActsAsTaggableOn
|
|
|
13
18
|
autoload :TagList
|
|
14
19
|
autoload :GenericParser
|
|
15
20
|
autoload :DefaultParser
|
|
16
|
-
autoload :TagListParser
|
|
17
21
|
autoload :Taggable
|
|
18
22
|
autoload :Tagger
|
|
19
23
|
autoload :Tagging
|
|
@@ -57,9 +61,10 @@ module ActsAsTaggableOn
|
|
|
57
61
|
end
|
|
58
62
|
|
|
59
63
|
class Configuration
|
|
60
|
-
attr_accessor :
|
|
61
|
-
:
|
|
64
|
+
attr_accessor :force_lowercase, :force_parameterize,
|
|
65
|
+
:remove_unused_tags, :default_parser,
|
|
62
66
|
:tags_counter
|
|
67
|
+
attr_reader :delimiter, :strict_case_match
|
|
63
68
|
|
|
64
69
|
def initialize
|
|
65
70
|
@delimiter = ','
|
|
@@ -73,9 +78,7 @@ module ActsAsTaggableOn
|
|
|
73
78
|
end
|
|
74
79
|
|
|
75
80
|
def strict_case_match=(force_cs)
|
|
76
|
-
|
|
77
|
-
@strict_case_match = force_cs
|
|
78
|
-
end
|
|
81
|
+
@strict_case_match = force_cs unless @force_binary_collation
|
|
79
82
|
end
|
|
80
83
|
|
|
81
84
|
def delimiter=(string)
|
|
@@ -89,7 +92,7 @@ WARNING
|
|
|
89
92
|
|
|
90
93
|
def force_binary_collation=(force_bin)
|
|
91
94
|
if Utils.using_mysql?
|
|
92
|
-
if force_bin
|
|
95
|
+
if force_bin
|
|
93
96
|
Configuration.apply_binary_collation(true)
|
|
94
97
|
@force_binary_collation = true
|
|
95
98
|
@strict_case_match = true
|
|
@@ -103,10 +106,12 @@ WARNING
|
|
|
103
106
|
def self.apply_binary_collation(bincoll)
|
|
104
107
|
if Utils.using_mysql?
|
|
105
108
|
coll = 'utf8_general_ci'
|
|
106
|
-
if bincoll
|
|
107
|
-
|
|
109
|
+
coll = 'utf8_bin' if bincoll
|
|
110
|
+
begin
|
|
111
|
+
ActiveRecord::Migration.execute("ALTER TABLE #{Tag.table_name} MODIFY name varchar(255) CHARACTER SET utf8 COLLATE #{coll};")
|
|
112
|
+
rescue Exception => e
|
|
113
|
+
puts "Trapping #{e.class}: collation parameter ignored while migrating for the first time."
|
|
108
114
|
end
|
|
109
|
-
ActiveRecord::Migration.execute("ALTER TABLE tags MODIFY name varchar(255) CHARACTER SET utf8 COLLATE #{coll};")
|
|
110
115
|
end
|
|
111
116
|
end
|
|
112
117
|
|
|
@@ -116,7 +121,6 @@ WARNING
|
|
|
116
121
|
end
|
|
117
122
|
|
|
118
123
|
ActiveSupport.on_load(:active_record) do
|
|
119
|
-
extend ActsAsTaggableOn::Compatibility
|
|
120
124
|
extend ActsAsTaggableOn::Taggable
|
|
121
125
|
include ActsAsTaggableOn::Tagger
|
|
122
126
|
end
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
module ActsAsTaggableOn
|
|
3
3
|
class Tag < ::ActiveRecord::Base
|
|
4
4
|
|
|
5
|
-
attr_accessible :name if defined?(ActiveModel::MassAssignmentSecurity)
|
|
6
|
-
|
|
7
5
|
### ASSOCIATIONS:
|
|
8
6
|
|
|
9
7
|
has_many :taggings, dependent: :destroy, class_name: '::ActsAsTaggableOn::Tagging'
|
|
@@ -50,6 +48,12 @@ module ActsAsTaggableOn
|
|
|
50
48
|
where(clause)
|
|
51
49
|
end
|
|
52
50
|
|
|
51
|
+
def self.for_context(context)
|
|
52
|
+
joins(:taggings).
|
|
53
|
+
where(["taggings.context = ?", context]).
|
|
54
|
+
select("DISTINCT tags.*")
|
|
55
|
+
end
|
|
56
|
+
|
|
53
57
|
### CLASS METHODS:
|
|
54
58
|
|
|
55
59
|
def self.find_or_create_with_like_by_name(name)
|
|
@@ -84,7 +84,8 @@ module ActsAsTaggableOn
|
|
|
84
84
|
map! { |tag| tag.mb_chars.downcase.to_s } if ActsAsTaggableOn.force_lowercase
|
|
85
85
|
map!(&:parameterize) if ActsAsTaggableOn.force_parameterize
|
|
86
86
|
|
|
87
|
-
uniq!
|
|
87
|
+
ActsAsTaggableOn.strict_case_match ? uniq! : uniq!{ |tag| tag.downcase }
|
|
88
|
+
self
|
|
88
89
|
end
|
|
89
90
|
|
|
90
91
|
|
|
@@ -99,18 +100,6 @@ module ActsAsTaggableOn
|
|
|
99
100
|
args.flatten!
|
|
100
101
|
end
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
## DEPRECATED
|
|
104
|
-
def self.from(string)
|
|
105
|
-
ActiveRecord::Base.logger.warn <<WARNING
|
|
106
|
-
ActsAsTaggableOn::TagList.from is deprecated \
|
|
107
|
-
and will be removed from v4.0+, use \
|
|
108
|
-
ActsAsTaggableOn::DefaultParser.new instead
|
|
109
|
-
WARNING
|
|
110
|
-
@parser.new(string).parse
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
|
|
114
103
|
end
|
|
115
104
|
end
|
|
116
105
|
|
|
@@ -73,7 +73,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
73
73
|
tag_types.map(&:to_s).each do |tag_type|
|
|
74
74
|
if self.class.send("caching_#{tag_type.singularize}_list?")
|
|
75
75
|
if tag_list_cache_set_on(tag_type)
|
|
76
|
-
list = tag_list_cache_on(tag_type).to_a.flatten.compact.join(
|
|
76
|
+
list = tag_list_cache_on(tag_type).to_a.flatten.compact.join("#{ActsAsTaggableOn.delimiter} ")
|
|
77
77
|
self["cached_#{tag_type.singularize}_list"] = list
|
|
78
78
|
end
|
|
79
79
|
end
|
|
@@ -138,7 +138,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
138
138
|
scoped_ids = pluck(table_name_pkey)
|
|
139
139
|
tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN (?)", scoped_ids)
|
|
140
140
|
else
|
|
141
|
-
tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{safe_to_sql(select(table_name_pkey))})")
|
|
141
|
+
tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{safe_to_sql(except(:select).select(table_name_pkey))})")
|
|
142
142
|
end
|
|
143
143
|
|
|
144
144
|
tagging_scope
|
|
@@ -169,9 +169,12 @@ module ActsAsTaggableOn::Taggable
|
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
module CalculationMethods
|
|
172
|
-
|
|
172
|
+
# Rails 5 TODO: Remove options argument as soon we remove support to
|
|
173
|
+
# activerecord-deprecated_finders.
|
|
174
|
+
# See https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/calculations.rb#L38
|
|
175
|
+
def count(column_name = :all, options = {})
|
|
173
176
|
# https://github.com/rails/rails/commit/da9b5d4a8435b744fcf278fffd6d7f1e36d4a4f2
|
|
174
|
-
super
|
|
177
|
+
ActsAsTaggableOn::Utils.active_record5? ? super(column_name) : super(column_name, options)
|
|
175
178
|
end
|
|
176
179
|
end
|
|
177
180
|
end
|
|
@@ -23,18 +23,15 @@ module ActsAsTaggableOn::Taggable
|
|
|
23
23
|
class_eval do
|
|
24
24
|
# when preserving tag order, include order option so that for a 'tags' context
|
|
25
25
|
# the associations tag_taggings & tags are always returned in created order
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class_name: 'ActsAsTaggableOn::Tag',
|
|
36
|
-
order: taggings_order
|
|
37
|
-
|
|
26
|
+
has_many context_taggings, -> { includes(:tag).order(taggings_order).where(context: tags_type) },
|
|
27
|
+
as: :taggable,
|
|
28
|
+
class_name: ActsAsTaggableOn::Tagging,
|
|
29
|
+
dependent: :destroy
|
|
30
|
+
|
|
31
|
+
has_many context_tags, -> { order(taggings_order) },
|
|
32
|
+
class_name: ActsAsTaggableOn::Tag,
|
|
33
|
+
through: context_taggings,
|
|
34
|
+
source: :tag
|
|
38
35
|
end
|
|
39
36
|
|
|
40
37
|
taggable_mixin.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
@@ -246,6 +243,14 @@ module ActsAsTaggableOn::Taggable
|
|
|
246
243
|
def taggable_mixin
|
|
247
244
|
@taggable_mixin ||= Module.new
|
|
248
245
|
end
|
|
246
|
+
|
|
247
|
+
private
|
|
248
|
+
|
|
249
|
+
# Rails 5 has merged sanitize and quote_value
|
|
250
|
+
# See https://github.com/rails/rails/blob/master/activerecord/lib/active_record/sanitization.rb#L10
|
|
251
|
+
def quote_value(value, column = nil)
|
|
252
|
+
ActsAsTaggableOn::Utils.active_record5? ? super(value) : super(value, column)
|
|
253
|
+
end
|
|
249
254
|
end
|
|
250
255
|
|
|
251
256
|
# all column names are necessary for PostgreSQL group clause
|
|
@@ -254,7 +259,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
254
259
|
end
|
|
255
260
|
|
|
256
261
|
def custom_contexts
|
|
257
|
-
@custom_contexts ||=
|
|
262
|
+
@custom_contexts ||= taggings.map(&:context).uniq
|
|
258
263
|
end
|
|
259
264
|
|
|
260
265
|
def is_taggable?
|
|
@@ -333,7 +338,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
333
338
|
end
|
|
334
339
|
|
|
335
340
|
def tagging_contexts
|
|
336
|
-
|
|
341
|
+
self.class.tag_types.map(&:to_s) + custom_contexts
|
|
337
342
|
end
|
|
338
343
|
|
|
339
344
|
def process_dirty_object(context, new_list)
|
|
@@ -407,7 +412,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
407
412
|
|
|
408
413
|
# Destroy old taggings:
|
|
409
414
|
if old_tags.present?
|
|
410
|
-
taggings.not_owned.by_context(context).
|
|
415
|
+
taggings.not_owned.by_context(context).where(tag_id: old_tags).destroy_all
|
|
411
416
|
end
|
|
412
417
|
|
|
413
418
|
# Create new taggings:
|
|
@@ -432,7 +437,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
432
437
|
tag_lists = tag_types.map {|tags_type| "#{tags_type.to_s.singularize}_list"}
|
|
433
438
|
super.delete_if {|attr| tag_lists.include? attr }
|
|
434
439
|
end
|
|
435
|
-
|
|
440
|
+
|
|
436
441
|
##
|
|
437
442
|
# Override this hook if you wish to subclass {ActsAsTaggableOn::Tag} --
|
|
438
443
|
# context is provided so that you may conditionally use a Tag subclass
|
|
@@ -108,9 +108,9 @@ module ActsAsTaggableOn::Taggable
|
|
|
108
108
|
|
|
109
109
|
# Find all taggings that belong to the taggable (self), are owned by the owner,
|
|
110
110
|
# have the correct context, and are removed from the list.
|
|
111
|
-
ActsAsTaggableOn::Tagging.
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
ActsAsTaggableOn::Tagging.where(taggable_id: id, taggable_type: self.class.base_class.to_s,
|
|
112
|
+
tagger_type: owner.class.base_class.to_s, tagger_id: owner.id,
|
|
113
|
+
tag_id: old_tags, context: context).destroy_all if old_tags.present?
|
|
114
114
|
|
|
115
115
|
# Create new taggings:
|
|
116
116
|
new_tags.each do |tag|
|
|
@@ -15,18 +15,19 @@ module ActsAsTaggableOn
|
|
|
15
15
|
# end
|
|
16
16
|
def acts_as_tagger(opts={})
|
|
17
17
|
class_eval do
|
|
18
|
-
|
|
19
|
-
opts.merge(
|
|
20
|
-
as: :tagger,
|
|
21
|
-
dependent: :destroy,
|
|
22
|
-
class_name: '::ActsAsTaggableOn::Tagging'
|
|
23
|
-
)
|
|
18
|
+
owned_taggings_scope = opts.delete(:scope)
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
has_many :owned_taggings, owned_taggings_scope,
|
|
21
|
+
opts.merge(
|
|
22
|
+
as: :tagger,
|
|
23
|
+
class_name: ::ActsAsTaggableOn::Tagging,
|
|
24
|
+
dependent: :destroy
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
has_many :owned_tags, -> { distinct },
|
|
28
|
+
class_name: ::ActsAsTaggableOn::Tag,
|
|
29
|
+
source: :tag,
|
|
30
|
+
through: :owned_taggings
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
include ActsAsTaggableOn::Tagger::InstanceMethods
|
|
@@ -1,25 +1,15 @@
|
|
|
1
1
|
module ActsAsTaggableOn
|
|
2
2
|
class Tagging < ::ActiveRecord::Base #:nodoc:
|
|
3
|
-
#TODO, remove from 4.0.0
|
|
4
|
-
attr_accessible :tag,
|
|
5
|
-
:tag_id,
|
|
6
|
-
:context,
|
|
7
|
-
:taggable,
|
|
8
|
-
:taggable_type,
|
|
9
|
-
:taggable_id,
|
|
10
|
-
:tagger,
|
|
11
|
-
:tagger_type,
|
|
12
|
-
:tagger_id if defined?(ActiveModel::MassAssignmentSecurity)
|
|
13
|
-
|
|
14
3
|
belongs_to :tag, class_name: '::ActsAsTaggableOn::Tag', counter_cache: ActsAsTaggableOn.tags_counter
|
|
15
4
|
belongs_to :taggable, polymorphic: true
|
|
16
|
-
|
|
5
|
+
|
|
6
|
+
belongs_to :tagger, {polymorphic: true}.tap {|o| o.merge!(optional: true) if ActsAsTaggableOn::Utils.active_record5? }
|
|
17
7
|
|
|
18
8
|
scope :owned_by, ->(owner) { where(tagger: owner) }
|
|
19
9
|
scope :not_owned, -> { where(tagger_id: nil, tagger_type: nil) }
|
|
20
10
|
|
|
21
|
-
scope :by_contexts, ->(contexts
|
|
22
|
-
scope :by_context, ->(context= 'tags') { by_contexts(context.to_s) }
|
|
11
|
+
scope :by_contexts, ->(contexts) { where(context: (contexts || 'tags')) }
|
|
12
|
+
scope :by_context, ->(context = 'tags') { by_contexts(context.to_s) }
|
|
23
13
|
|
|
24
14
|
validates_presence_of :context
|
|
25
15
|
validates_presence_of :tag_id
|
|
@@ -13,7 +13,6 @@ module ActsAsTaggableOn
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def using_mysql?
|
|
16
|
-
#We should probably use regex for mysql to support prehistoric adapters
|
|
17
16
|
connection && connection.adapter_name == 'Mysql2'
|
|
18
17
|
end
|
|
19
18
|
|
|
@@ -21,8 +20,8 @@ module ActsAsTaggableOn
|
|
|
21
20
|
Digest::SHA1.hexdigest(string)[0..6]
|
|
22
21
|
end
|
|
23
22
|
|
|
24
|
-
def
|
|
25
|
-
::ActiveRecord::VERSION::MAJOR ==
|
|
23
|
+
def active_record5?
|
|
24
|
+
::ActiveRecord::VERSION::MAJOR == 5
|
|
26
25
|
end
|
|
27
26
|
|
|
28
27
|
def like_operator
|
|
@@ -154,7 +154,7 @@ describe 'Acts As Taggable On' do
|
|
|
154
154
|
describe 'Tagging Contexts' do
|
|
155
155
|
it 'should eliminate duplicate tagging contexts ' do
|
|
156
156
|
TaggableModel.acts_as_taggable_on(:skills, :skills)
|
|
157
|
-
expect(TaggableModel.tag_types.freq[:skills]).
|
|
157
|
+
expect(TaggableModel.tag_types.freq[:skills]).to eq(1)
|
|
158
158
|
end
|
|
159
159
|
|
|
160
160
|
it 'should not contain embedded/nested arrays' do
|
|
@@ -178,6 +178,15 @@ describe 'Acts As Taggable On' do
|
|
|
178
178
|
TaggableModel.acts_as_taggable_on([nil])
|
|
179
179
|
}).to_not raise_error
|
|
180
180
|
end
|
|
181
|
+
|
|
182
|
+
it 'should include dynamic contexts in tagging_contexts' do
|
|
183
|
+
taggable = TaggableModel.create!(name: 'Dynamic Taggable')
|
|
184
|
+
taggable.set_tag_list_on :colors, 'tag1, tag2, tag3'
|
|
185
|
+
expect(taggable.tagging_contexts).to eq(%w(tags languages skills needs offerings array colors))
|
|
186
|
+
taggable.save
|
|
187
|
+
taggable = TaggableModel.where(name: 'Dynamic Taggable').first
|
|
188
|
+
expect(taggable.tagging_contexts).to eq(%w(tags languages skills needs offerings array colors))
|
|
189
|
+
end
|
|
181
190
|
end
|
|
182
191
|
|
|
183
192
|
context 'when tagging context ends in an "s" when singular (ex. "status", "glass", etc.)' do
|
|
@@ -72,7 +72,7 @@ describe 'acts_as_tagger' do
|
|
|
72
72
|
expect(@taggable.tag_list_on(:foo_boo)).to be_empty
|
|
73
73
|
expect(-> {
|
|
74
74
|
@tagger.tag(@taggable, with: 'this, and, that', on: :foo_boo, force: false)
|
|
75
|
-
}).to raise_error
|
|
75
|
+
}).to raise_error(RuntimeError)
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
it 'should not create the tag context on-the-fly when the default is over-ridden' do
|
|
@@ -77,6 +77,28 @@ describe 'Acts As Taggable On' do
|
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
describe 'with a custom delimiter' do
|
|
81
|
+
before(:each) do
|
|
82
|
+
@taggable = CachedModel.new(name: 'Bob Jones')
|
|
83
|
+
@another_taggable = OtherCachedModel.new(name: 'John Smith')
|
|
84
|
+
ActsAsTaggableOn.delimiter = ';'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
after(:all) do
|
|
88
|
+
ActsAsTaggableOn.delimiter = ','
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'should cache tags with custom delimiter' do
|
|
92
|
+
@taggable.update_attributes(tag_list: 'awesome; epic')
|
|
93
|
+
expect(@taggable.tag_list).to eq(['awesome', 'epic'])
|
|
94
|
+
expect(@taggable.cached_tag_list).to eq('awesome; epic')
|
|
95
|
+
|
|
96
|
+
@taggable = CachedModel.find_by_name('Bob Jones')
|
|
97
|
+
expect(@taggable.tag_list).to eq(['awesome', 'epic'])
|
|
98
|
+
expect(@taggable.cached_tag_list).to eq('awesome; epic')
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
80
102
|
describe 'CachingWithArray' do
|
|
81
103
|
pending '#TODO'
|
|
82
104
|
end
|
|
@@ -83,6 +83,18 @@ describe ActsAsTaggableOn::TagList do
|
|
|
83
83
|
new_tag_list = tag_list.concat(another_tag_list)
|
|
84
84
|
expect(new_tag_list.class).to eq(ActsAsTaggableOn::TagList)
|
|
85
85
|
end
|
|
86
|
+
|
|
87
|
+
context 'without duplicates' do
|
|
88
|
+
let(:arr) { ['crazy', 'alien'] }
|
|
89
|
+
let(:another_tag_list) { ActsAsTaggableOn::TagList.new(*arr) }
|
|
90
|
+
it 'adds other list' do
|
|
91
|
+
expect(tag_list.concat(another_tag_list)).to eq(%w[awesome radical crazy alien])
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'adds other array' do
|
|
95
|
+
expect(tag_list.concat(arr)).to eq(%w[awesome radical crazy alien])
|
|
96
|
+
end
|
|
97
|
+
end
|
|
86
98
|
end
|
|
87
99
|
|
|
88
100
|
describe '#to_s' do
|
|
@@ -92,7 +104,7 @@ describe ActsAsTaggableOn::TagList do
|
|
|
92
104
|
|
|
93
105
|
it 'should be able to call to_s on a frozen tag list' do
|
|
94
106
|
tag_list.freeze
|
|
95
|
-
expect(-> { tag_list.add('cool', 'rad,bodacious') }).to raise_error
|
|
107
|
+
expect(-> { tag_list.add('cool', 'rad,bodacious') }).to raise_error(RuntimeError)
|
|
96
108
|
expect(-> { tag_list.to_s }).to_not raise_error
|
|
97
109
|
end
|
|
98
110
|
end
|
|
@@ -114,6 +126,20 @@ describe ActsAsTaggableOn::TagList do
|
|
|
114
126
|
|
|
115
127
|
ActsAsTaggableOn.force_lowercase = false
|
|
116
128
|
end
|
|
129
|
+
|
|
130
|
+
it 'should ignore case when removing duplicates if strict_case_match is false' do
|
|
131
|
+
tag_list = ActsAsTaggableOn::TagList.new('Junglist', 'JUNGLIST', 'Junglist', 'Massive', 'MASSIVE', 'MASSIVE')
|
|
132
|
+
|
|
133
|
+
expect(tag_list.to_s).to eq('Junglist, Massive')
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'should not ignore case when removing duplicates if strict_case_match is true' do
|
|
137
|
+
ActsAsTaggableOn.strict_case_match = true
|
|
138
|
+
tag_list = ActsAsTaggableOn::TagList.new('Junglist', 'JUNGLIST', 'Junglist', 'Massive', 'MASSIVE', 'MASSIVE')
|
|
139
|
+
|
|
140
|
+
expect(tag_list.to_s).to eq('Junglist, JUNGLIST, Massive, MASSIVE')
|
|
141
|
+
ActsAsTaggableOn.strict_case_match = false
|
|
142
|
+
end
|
|
117
143
|
end
|
|
118
144
|
|
|
119
145
|
describe 'custom parser' do
|
|
@@ -55,6 +55,21 @@ describe ActsAsTaggableOn::Tag do
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
describe 'for context' do
|
|
59
|
+
before(:each) do
|
|
60
|
+
@user.skill_list.add('ruby')
|
|
61
|
+
@user.save
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'should return tags that have been used in the given context' do
|
|
65
|
+
expect(ActsAsTaggableOn::Tag.for_context('skills').pluck(:name)).to include('ruby')
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'should not return tags that have been used in other contexts' do
|
|
69
|
+
expect(ActsAsTaggableOn::Tag.for_context('needs').pluck(:name)).to_not include('ruby')
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
58
73
|
describe 'find or create by name' do
|
|
59
74
|
before(:each) do
|
|
60
75
|
@tag.name = 'awesome'
|
|
@@ -119,6 +119,21 @@ describe 'Taggable' do
|
|
|
119
119
|
expect(@taggable.tag_counts_on(:tags).length).to eq(2)
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
+
context 'tag_counts on a collection' do
|
|
123
|
+
context 'a select clause is specified on the collection' do
|
|
124
|
+
it 'should return tag counts without raising an error' do
|
|
125
|
+
expect(TaggableModel.tag_counts_on(:tags)).to be_empty
|
|
126
|
+
|
|
127
|
+
@taggable.tag_list = %w(awesome epic)
|
|
128
|
+
@taggable.save
|
|
129
|
+
|
|
130
|
+
expect {
|
|
131
|
+
expect(TaggableModel.select(:name).tag_counts_on(:tags).length).to eq(2)
|
|
132
|
+
}.not_to raise_error
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
122
137
|
it 'should have tags_on' do
|
|
123
138
|
expect(TaggableModel.tags_on(:tags)).to be_empty
|
|
124
139
|
|
|
@@ -226,8 +241,10 @@ describe 'Taggable' do
|
|
|
226
241
|
it "should be able to find a tag using dates" do
|
|
227
242
|
@taggable.skill_list = "ruby"
|
|
228
243
|
@taggable.save
|
|
244
|
+
today = Date.today.to_time.utc
|
|
245
|
+
tomorrow = Date.tomorrow.to_time.utc
|
|
229
246
|
|
|
230
|
-
expect(TaggableModel.tagged_with("ruby", :start_at =>
|
|
247
|
+
expect(TaggableModel.tagged_with("ruby", :start_at => today, :end_at => tomorrow).count).to eq(1)
|
|
231
248
|
end
|
|
232
249
|
|
|
233
250
|
it "shouldn't be able to find a tag outside date range" do
|
|
@@ -380,7 +397,6 @@ describe 'Taggable' do
|
|
|
380
397
|
# Test specific join syntaxes:
|
|
381
398
|
frank.untaggable_models.create!
|
|
382
399
|
expect(TaggableModel.tagged_with('rails').joins(:untaggable_models).all_tag_counts.size).to eq(2)
|
|
383
|
-
expect(TaggableModel.tagged_with('rails').joins(untaggable_models: :taggable_model).all_tag_counts.size).to eq(2)
|
|
384
400
|
expect(TaggableModel.tagged_with('rails').joins([:untaggable_models]).all_tag_counts.size).to eq(2)
|
|
385
401
|
end
|
|
386
402
|
|
|
@@ -395,7 +411,6 @@ describe 'Taggable' do
|
|
|
395
411
|
# Test specific join syntaxes:
|
|
396
412
|
frank.untaggable_models.create!
|
|
397
413
|
expect(TaggableModel.tagged_with('rails').joins(:untaggable_models).all_tags.size).to eq(2)
|
|
398
|
-
expect(TaggableModel.tagged_with('rails').joins(untaggable_models: :taggable_model).all_tags.size).to eq(2)
|
|
399
414
|
expect(TaggableModel.tagged_with('rails').joins([:untaggable_models]).all_tags.size).to eq(2)
|
|
400
415
|
end
|
|
401
416
|
|
|
@@ -459,7 +474,7 @@ describe 'Taggable' do
|
|
|
459
474
|
|
|
460
475
|
expect(TaggableModel.tagged_with(%w(bob tricia), wild: true, any: true).to_a.sort_by { |o| o.id }).to eq([bob, frank, steve])
|
|
461
476
|
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
|
|
477
|
+
expect(TaggableModel.tagged_with('ji', wild: true, any: true).to_a =~ [frank, jim])
|
|
463
478
|
end
|
|
464
479
|
end
|
|
465
480
|
|
|
@@ -743,7 +758,6 @@ describe 'Taggable' do
|
|
|
743
758
|
|
|
744
759
|
context 'Model.limit(x).tag_counts.sum(:tags_count)' do
|
|
745
760
|
it 'should not break on Mysql' do
|
|
746
|
-
# Activerecord 3.2 return a string
|
|
747
761
|
expect(TaggableModel.limit(2).tag_counts.sum('tags_count').to_i).to eq(5)
|
|
748
762
|
end
|
|
749
763
|
end
|
|
@@ -49,15 +49,69 @@ describe ActsAsTaggableOn::Tagging do
|
|
|
49
49
|
ActsAsTaggableOn.remove_unused_tags = previous_setting
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
describe 'context scopes' do
|
|
53
|
+
before do
|
|
54
|
+
@tagging_2 = ActsAsTaggableOn::Tagging.new
|
|
55
|
+
@tagging_3 = ActsAsTaggableOn::Tagging.new
|
|
56
|
+
|
|
57
|
+
@tagger = User.new
|
|
58
|
+
@tagger_2 = User.new
|
|
59
|
+
|
|
60
|
+
@tagging.taggable = TaggableModel.create(name: "Black holes")
|
|
61
|
+
@tagging.tag = ActsAsTaggableOn::Tag.create(name: "Physics")
|
|
62
|
+
@tagging.tagger = @tagger
|
|
63
|
+
@tagging.context = 'Science'
|
|
64
|
+
@tagging.save
|
|
65
|
+
|
|
66
|
+
@tagging_2.taggable = TaggableModel.create(name: "Satellites")
|
|
67
|
+
@tagging_2.tag = ActsAsTaggableOn::Tag.create(name: "Technology")
|
|
68
|
+
@tagging_2.tagger = @tagger_2
|
|
69
|
+
@tagging_2.context = 'Science'
|
|
70
|
+
@tagging_2.save
|
|
71
|
+
|
|
72
|
+
@tagging_3.taggable = TaggableModel.create(name: "Satellites")
|
|
73
|
+
@tagging_3.tag = ActsAsTaggableOn::Tag.create(name: "Engineering")
|
|
74
|
+
@tagging_3.tagger = @tagger_2
|
|
75
|
+
@tagging_3.context = 'Astronomy'
|
|
76
|
+
@tagging_3.save
|
|
77
|
+
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe '.owned_by' do
|
|
81
|
+
it "should belong to a specific user" do
|
|
82
|
+
expect(ActsAsTaggableOn::Tagging.owned_by(@tagger).first).to eq(@tagging)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe '.by_context' do
|
|
87
|
+
it "should be found by context" do
|
|
88
|
+
expect(ActsAsTaggableOn::Tagging.by_context('Science').length).to eq(2);
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe '.by_contexts' do
|
|
93
|
+
it "should find taggings by contexts" do
|
|
94
|
+
expect(ActsAsTaggableOn::Tagging.by_contexts(['Science', 'Astronomy']).first).to eq(@tagging);
|
|
95
|
+
expect(ActsAsTaggableOn::Tagging.by_contexts(['Science', 'Astronomy']).second).to eq(@tagging_2);
|
|
96
|
+
expect(ActsAsTaggableOn::Tagging.by_contexts(['Science', 'Astronomy']).third).to eq(@tagging_3);
|
|
97
|
+
expect(ActsAsTaggableOn::Tagging.by_contexts(['Science', 'Astronomy']).length).to eq(3);
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe '.not_owned' do
|
|
102
|
+
before do
|
|
103
|
+
@tagging_4 = ActsAsTaggableOn::Tagging.new
|
|
104
|
+
@tagging_4.taggable = TaggableModel.create(name: "Gravity")
|
|
105
|
+
@tagging_4.tag = ActsAsTaggableOn::Tag.create(name: "Space")
|
|
106
|
+
@tagging_4.context = "Science"
|
|
107
|
+
@tagging_4.save
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "should found the taggings that do not have owner" do
|
|
111
|
+
expect(ActsAsTaggableOn::Tagging.all.length).to eq(4)
|
|
112
|
+
expect(ActsAsTaggableOn::Tagging.not_owned.length).to eq(1)
|
|
113
|
+
expect(ActsAsTaggableOn::Tagging.not_owned.first).to eq(@tagging_4)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
61
116
|
end
|
|
62
|
-
|
|
63
117
|
end
|
data/spec/internal/db/schema.rb
CHANGED
|
@@ -7,12 +7,15 @@ ActiveRecord::Schema.define version: 0 do
|
|
|
7
7
|
add_index 'tags', ['name'], name: 'index_tags_on_name', unique: true
|
|
8
8
|
|
|
9
9
|
create_table :taggings, force: true do |t|
|
|
10
|
-
t.
|
|
10
|
+
t.integer :tag_id
|
|
11
11
|
|
|
12
12
|
# You should make sure that the column created is
|
|
13
13
|
# long enough to store the required class names.
|
|
14
|
-
t.
|
|
15
|
-
t.
|
|
14
|
+
t.string :taggable_type
|
|
15
|
+
t.integer :taggable_id
|
|
16
|
+
|
|
17
|
+
t.string :tagger_type
|
|
18
|
+
t.integer :tagger_id
|
|
16
19
|
|
|
17
20
|
# Limit is created to prevent MySQL error on index
|
|
18
21
|
# length for MyISAM table type: http://bit.ly/vgW2Ql
|
|
@@ -23,6 +26,7 @@ ActiveRecord::Schema.define version: 0 do
|
|
|
23
26
|
add_index 'taggings',
|
|
24
27
|
['tag_id', 'taggable_id', 'taggable_type', 'context', 'tagger_id', 'tagger_type'],
|
|
25
28
|
unique: true, name: 'taggings_idx'
|
|
29
|
+
add_index 'taggings', :tag_id , name: 'index_taggings_on_tag_id'
|
|
26
30
|
|
|
27
31
|
# above copied from
|
|
28
32
|
# generators/acts_as_taggable_on/migration/migration_generator
|
data/spec/support/database.rb
CHANGED
|
@@ -13,13 +13,7 @@ if File.exist?(database_yml)
|
|
|
13
13
|
config = ActiveRecord::Base.configurations[db_name]
|
|
14
14
|
|
|
15
15
|
begin
|
|
16
|
-
|
|
17
|
-
#TODO, remove when activerecord 3 support is dropped
|
|
18
|
-
if ActsAsTaggableOn::Utils.active_record4?
|
|
19
|
-
ActiveRecord::Base.establish_connection(db_name.to_sym)
|
|
20
|
-
else
|
|
21
|
-
ActiveRecord::Base.establish_connection(db_name)
|
|
22
|
-
end
|
|
16
|
+
ActiveRecord::Base.establish_connection(db_name.to_sym)
|
|
23
17
|
ActiveRecord::Base.connection
|
|
24
18
|
rescue
|
|
25
19
|
case db_name
|
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:
|
|
4
|
+
version: 4.0.0.pre
|
|
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:
|
|
12
|
+
date: 2016-07-17 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activerecord
|
|
@@ -17,20 +17,14 @@ dependencies:
|
|
|
17
17
|
requirements:
|
|
18
18
|
- - ">="
|
|
19
19
|
- !ruby/object:Gem::Version
|
|
20
|
-
version: '
|
|
21
|
-
- - "<"
|
|
22
|
-
- !ruby/object:Gem::Version
|
|
23
|
-
version: '5'
|
|
20
|
+
version: '4.0'
|
|
24
21
|
type: :runtime
|
|
25
22
|
prerelease: false
|
|
26
23
|
version_requirements: !ruby/object:Gem::Requirement
|
|
27
24
|
requirements:
|
|
28
25
|
- - ">="
|
|
29
26
|
- !ruby/object:Gem::Version
|
|
30
|
-
version: '
|
|
31
|
-
- - "<"
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '5'
|
|
27
|
+
version: '4.0'
|
|
34
28
|
- !ruby/object:Gem::Dependency
|
|
35
29
|
name: sqlite3
|
|
36
30
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -51,14 +45,14 @@ dependencies:
|
|
|
51
45
|
requirements:
|
|
52
46
|
- - "~>"
|
|
53
47
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: 0.3
|
|
48
|
+
version: '0.3'
|
|
55
49
|
type: :development
|
|
56
50
|
prerelease: false
|
|
57
51
|
version_requirements: !ruby/object:Gem::Requirement
|
|
58
52
|
requirements:
|
|
59
53
|
- - "~>"
|
|
60
54
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: 0.3
|
|
55
|
+
version: '0.3'
|
|
62
56
|
- !ruby/object:Gem::Dependency
|
|
63
57
|
name: pg
|
|
64
58
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -170,19 +164,18 @@ files:
|
|
|
170
164
|
- db/migrate/3_add_taggings_counter_cache_to_tags.rb
|
|
171
165
|
- db/migrate/4_add_missing_taggable_index.rb
|
|
172
166
|
- db/migrate/5_change_collation_for_tag_names.rb
|
|
173
|
-
-
|
|
167
|
+
- db/migrate/6_add_missing_indexes.rb
|
|
174
168
|
- gemfiles/activerecord_4.0.gemfile
|
|
175
169
|
- gemfiles/activerecord_4.1.gemfile
|
|
176
170
|
- gemfiles/activerecord_4.2.gemfile
|
|
171
|
+
- gemfiles/activerecord_5.0.gemfile
|
|
177
172
|
- lib/acts-as-taggable-on.rb
|
|
178
173
|
- lib/acts_as_taggable_on.rb
|
|
179
|
-
- lib/acts_as_taggable_on/compatibility.rb
|
|
180
174
|
- lib/acts_as_taggable_on/default_parser.rb
|
|
181
175
|
- lib/acts_as_taggable_on/engine.rb
|
|
182
176
|
- lib/acts_as_taggable_on/generic_parser.rb
|
|
183
177
|
- lib/acts_as_taggable_on/tag.rb
|
|
184
178
|
- lib/acts_as_taggable_on/tag_list.rb
|
|
185
|
-
- lib/acts_as_taggable_on/tag_list_parser.rb
|
|
186
179
|
- lib/acts_as_taggable_on/taggable.rb
|
|
187
180
|
- lib/acts_as_taggable_on/taggable/cache.rb
|
|
188
181
|
- lib/acts_as_taggable_on/taggable/collection.rb
|
|
@@ -203,7 +196,6 @@ files:
|
|
|
203
196
|
- spec/acts_as_taggable_on/generic_parser_spec.rb
|
|
204
197
|
- spec/acts_as_taggable_on/related_spec.rb
|
|
205
198
|
- spec/acts_as_taggable_on/single_table_inheritance_spec.rb
|
|
206
|
-
- spec/acts_as_taggable_on/tag_list_parser_spec.rb
|
|
207
199
|
- spec/acts_as_taggable_on/tag_list_spec.rb
|
|
208
200
|
- spec/acts_as_taggable_on/tag_spec.rb
|
|
209
201
|
- spec/acts_as_taggable_on/taggable/dirty_spec.rb
|
|
@@ -254,15 +246,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
254
246
|
requirements:
|
|
255
247
|
- - ">="
|
|
256
248
|
- !ruby/object:Gem::Version
|
|
257
|
-
version:
|
|
249
|
+
version: 2.0.0
|
|
258
250
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
259
251
|
requirements:
|
|
260
|
-
- - "
|
|
252
|
+
- - ">"
|
|
261
253
|
- !ruby/object:Gem::Version
|
|
262
|
-
version:
|
|
254
|
+
version: 1.3.1
|
|
263
255
|
requirements: []
|
|
264
256
|
rubyforge_project:
|
|
265
|
-
rubygems_version: 2.4.5
|
|
257
|
+
rubygems_version: 2.4.5.1
|
|
266
258
|
signing_key:
|
|
267
259
|
specification_version: 4
|
|
268
260
|
summary: Advanced tagging for Rails.
|
|
@@ -274,7 +266,6 @@ test_files:
|
|
|
274
266
|
- spec/acts_as_taggable_on/generic_parser_spec.rb
|
|
275
267
|
- spec/acts_as_taggable_on/related_spec.rb
|
|
276
268
|
- spec/acts_as_taggable_on/single_table_inheritance_spec.rb
|
|
277
|
-
- spec/acts_as_taggable_on/tag_list_parser_spec.rb
|
|
278
269
|
- spec/acts_as_taggable_on/tag_list_spec.rb
|
|
279
270
|
- spec/acts_as_taggable_on/tag_spec.rb
|
|
280
271
|
- spec/acts_as_taggable_on/taggable/dirty_spec.rb
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
module ActsAsTaggableOn::Compatibility
|
|
2
|
-
def has_many_with_taggable_compatibility(name, options = {}, &extention)
|
|
3
|
-
if ActsAsTaggableOn::Utils.active_record4?
|
|
4
|
-
scope, opts = build_taggable_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_taggable_scope_and_options(opts)
|
|
12
|
-
scope_opts, opts = parse_taggable_options(opts)
|
|
13
|
-
|
|
14
|
-
unless scope_opts.empty?
|
|
15
|
-
scope = -> {
|
|
16
|
-
scope_opts.inject(self) { |result, hash| result.send(*hash) }
|
|
17
|
-
}
|
|
18
|
-
return [scope, opts]
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
[nil, opts]
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def parse_taggable_options(opts)
|
|
25
|
-
scope_opts = {}
|
|
26
|
-
[:order, :having, :select, :group, :limit, :offset, :readonly].each do |o|
|
|
27
|
-
scope_opts[o] = opts.delete o if opts[o]
|
|
28
|
-
end
|
|
29
|
-
scope_opts[:where] = opts.delete :conditions if opts[:conditions]
|
|
30
|
-
scope_opts[:joins] = opts.delete :include if opts [:include]
|
|
31
|
-
scope_opts[:distinct] = opts.delete :uniq if opts[:uniq]
|
|
32
|
-
|
|
33
|
-
[scope_opts, opts]
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
module ActsAsTaggableOn
|
|
2
|
-
##
|
|
3
|
-
# Returns a new TagList using the given tag string.
|
|
4
|
-
#
|
|
5
|
-
# Example:
|
|
6
|
-
# tag_list = ActsAsTaggableOn::TagListParser.parse("One , Two, Three")
|
|
7
|
-
# tag_list # ["One", "Two", "Three"]
|
|
8
|
-
module TagListParser
|
|
9
|
-
class << self
|
|
10
|
-
## DEPRECATED
|
|
11
|
-
def parse(string)
|
|
12
|
-
ActiveRecord::Base.logger.warn <<WARNING
|
|
13
|
-
ActsAsTaggableOn::TagListParser.parse is deprecated \
|
|
14
|
-
and will be removed from v4.0+, use \
|
|
15
|
-
ActsAsTaggableOn::TagListParser.new instead
|
|
16
|
-
WARNING
|
|
17
|
-
DefaultParser.new(string).parse
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# -*- encoding : utf-8 -*-
|
|
2
|
-
require 'spec_helper'
|
|
3
|
-
|
|
4
|
-
describe ActsAsTaggableOn::TagListParser do
|
|
5
|
-
it '#parse should return empty array if empty array is passed' do
|
|
6
|
-
expect(ActsAsTaggableOn::TagListParser.parse([])).to be_empty
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
describe 'Multiple Delimiter' do
|
|
10
|
-
before do
|
|
11
|
-
@old_delimiter = ActsAsTaggableOn.delimiter
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
after do
|
|
15
|
-
ActsAsTaggableOn.delimiter = @old_delimiter
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it 'should separate tags by delimiters' do
|
|
19
|
-
ActsAsTaggableOn.delimiter = [',', ' ', '\|']
|
|
20
|
-
tag_list = ActsAsTaggableOn::TagListParser.parse('cool, data|I have')
|
|
21
|
-
expect(tag_list.to_s).to eq('cool, data, I, have')
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
it 'should escape quote' do
|
|
25
|
-
ActsAsTaggableOn.delimiter = [',', ' ', '\|']
|
|
26
|
-
tag_list = ActsAsTaggableOn::TagListParser.parse "'I have'|cool, data"
|
|
27
|
-
expect(tag_list.to_s).to eq('"I have", cool, data')
|
|
28
|
-
|
|
29
|
-
tag_list = ActsAsTaggableOn::TagListParser.parse '"I, have"|cool, data'
|
|
30
|
-
expect(tag_list.to_s).to eq('"I, have", cool, data')
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it 'should work for utf8 delimiter and long delimiter' do
|
|
34
|
-
ActsAsTaggableOn.delimiter = [',', '的', '可能是']
|
|
35
|
-
tag_list = ActsAsTaggableOn::TagListParser.parse('我的东西可能是不见了,还好有备份')
|
|
36
|
-
expect(tag_list.to_s).to eq('我, 东西, 不见了, 还好有备份')
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it 'should work for multiple quoted tags' do
|
|
40
|
-
ActsAsTaggableOn.delimiter = [',']
|
|
41
|
-
tag_list = ActsAsTaggableOn::TagListParser.parse('"Ruby Monsters","eat Katzenzungen"')
|
|
42
|
-
expect(tag_list.to_s).to eq('Ruby Monsters, eat Katzenzungen')
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
end
|