acts-as-taggable-on 3.2.1 → 3.3.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 +4 -4
- data/.travis.yml +21 -1
- data/Appraisals +0 -4
- data/CHANGELOG.md +27 -15
- data/README.md +18 -11
- data/acts-as-taggable-on.gemspec +0 -2
- data/db/migrate/4_add_missing_taggable_index.rb +9 -0
- data/gemfiles/activerecord_3.2.gemfile +0 -1
- data/gemfiles/activerecord_4.0.gemfile +0 -1
- data/gemfiles/activerecord_4.1.gemfile +0 -1
- data/gemfiles/activerecord_edge.gemfile +0 -1
- data/lib/acts-as-taggable-on.rb +28 -20
- data/lib/acts_as_taggable_on/tag.rb +1 -1
- data/lib/acts_as_taggable_on/tag_list.rb +15 -78
- data/lib/acts_as_taggable_on/tag_list_parser.rb +78 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/cache.rb +1 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/collection.rb +2 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/core.rb +68 -44
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/ownership.rb +1 -1
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/related.rb +6 -6
- data/lib/acts_as_taggable_on/taggable.rb +7 -8
- data/lib/acts_as_taggable_on/tagging.rb +7 -1
- data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
- data/lib/acts_as_taggable_on/utils.rb +26 -50
- data/lib/acts_as_taggable_on/version.rb +1 -1
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +1 -1
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +1 -0
- data/spec/acts_as_taggable_on/caching_spec.rb +1 -0
- data/spec/acts_as_taggable_on/related_spec.rb +1 -0
- data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +1 -0
- data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +46 -0
- data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -40
- data/spec/acts_as_taggable_on/tag_spec.rb +34 -38
- data/spec/acts_as_taggable_on/taggable/dirty_spec.rb +127 -0
- data/spec/acts_as_taggable_on/taggable_spec.rb +84 -164
- data/spec/acts_as_taggable_on/tagger_spec.rb +1 -1
- data/spec/acts_as_taggable_on/tagging_spec.rb +37 -1
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -0
- data/spec/acts_as_taggable_on/utils_spec.rb +6 -11
- data/spec/internal/app/models/cached_model_with_array.rb +1 -1
- data/spec/internal/app/models/models.rb +2 -2
- data/spec/internal/db/schema.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/support/0-helpers.rb +32 -0
- data/spec/support/database_cleaner.rb +4 -0
- metadata +17 -47
- data/spec/bm.rb +0 -52
- data/spec/schema.rb +0 -82
- /data/lib/acts_as_taggable_on/{acts_as_taggable_on/compatibility.rb → compatibility.rb} +0 -0
- /data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/dirty.rb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5c20928991764b079db87d3d1ba32846d0a3e01f
|
|
4
|
+
data.tar.gz: 94cae20632e6d61d21cd55921cb7ee284cb6f766
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 29cfdac7542aed8bb4f8f55a0c7d33f592739ae1f887a5399e0bd34fd686a5824b3e5335f5037a0ada5928001b4a8b3ca124d21771d5af648fc6f55735bf31da
|
|
7
|
+
data.tar.gz: 535aa2433b00889491d6aaf7f488b372ebc1ac2e60f8d9cd2337ae07a19ce2ce1bce3438a1f2db48c77b52a3a57c56648c8c31ddf1b4f0c2447bebae27d67fe0
|
data/.travis.yml
CHANGED
|
@@ -3,15 +3,19 @@ rvm:
|
|
|
3
3
|
- 2.0.0
|
|
4
4
|
- 2.1.1
|
|
5
5
|
- rbx-2
|
|
6
|
+
- ruby-head
|
|
7
|
+
|
|
6
8
|
env:
|
|
7
9
|
- DB=sqlite3
|
|
8
10
|
- DB=mysql
|
|
9
11
|
- DB=postgresql
|
|
12
|
+
|
|
10
13
|
gemfile:
|
|
11
14
|
- gemfiles/activerecord_3.2.gemfile
|
|
12
15
|
- gemfiles/activerecord_4.0.gemfile
|
|
13
16
|
- gemfiles/activerecord_4.1.gemfile
|
|
14
17
|
- gemfiles/activerecord_edge.gemfile
|
|
18
|
+
|
|
15
19
|
cache: bundler
|
|
16
20
|
script: bundle exec rake
|
|
17
21
|
before_install: gem install bundler
|
|
@@ -20,4 +24,20 @@ matrix:
|
|
|
20
24
|
fast_finish: true
|
|
21
25
|
allow_failures:
|
|
22
26
|
- gemfile: gemfiles/activerecord_edge.gemfile
|
|
23
|
-
- rvm: rbx-2
|
|
27
|
+
- rvm: rbx-2
|
|
28
|
+
- rvm: ruby-head
|
|
29
|
+
exclude:
|
|
30
|
+
- rvm: 1.9.3
|
|
31
|
+
gemfile: gemfiles/activerecord_4.0.gemfile
|
|
32
|
+
- rvm: 1.9.3
|
|
33
|
+
gemfile: gemfiles/activerecord_4.1.gemfile
|
|
34
|
+
- rvm: 1.9.3
|
|
35
|
+
gemfile: gemfiles/activerecord_edge.gemfile
|
|
36
|
+
- rvm: rbx-2
|
|
37
|
+
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
|
data/Appraisals
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
appraise "activerecord-3.2" do
|
|
2
2
|
gem "activerecord", "~> 3.2"
|
|
3
|
-
gem "actionpack", "~> 3.2"
|
|
4
3
|
end
|
|
5
4
|
|
|
6
5
|
appraise "activerecord-4.0" do
|
|
7
6
|
gem "activerecord", "~> 4.0"
|
|
8
|
-
gem "actionpack", "~> 4.0"
|
|
9
7
|
end
|
|
10
8
|
|
|
11
9
|
appraise "activerecord-4.1" do
|
|
12
10
|
gem "activerecord", "~> 4.1"
|
|
13
|
-
gem "actionpack", "~> 4.1"
|
|
14
11
|
end
|
|
15
12
|
|
|
16
13
|
appraise "activerecord-edge" do
|
|
17
14
|
gem "activerecord", github: "rails/rails"
|
|
18
|
-
gem "actionpack", github: "rails/rails"
|
|
19
15
|
gem 'arel', github: 'rails/arel'
|
|
20
16
|
end
|
data/CHANGELOG.md
CHANGED
|
@@ -4,21 +4,33 @@ 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.
|
|
8
|
-
|
|
9
|
-
* Breaking Changes
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Fixes
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
*
|
|
21
|
-
|
|
7
|
+
### Master [changes](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.0...master)
|
|
8
|
+
|
|
9
|
+
* Breaking Changes
|
|
10
|
+
* Taggable models are not extend with ActsAsTaggableOn::Utils anymore
|
|
11
|
+
* Features
|
|
12
|
+
* Fixes
|
|
13
|
+
* Performance
|
|
14
|
+
* Misc
|
|
15
|
+
* Deleted outdated benchmark script
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### [3.2.0 / 2014-05-1](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.1.1...v3.2.0)
|
|
19
|
+
|
|
20
|
+
* Breaking Changes
|
|
21
|
+
* ActsAsTaggableOn::Tag is not extend with ActsAsTaggableOn::Utils anymore
|
|
22
|
+
* Features
|
|
23
|
+
* [@chess #413 Hook to support STI subclasses of Tag in save_tags](https://github.com/mbleigh/acts-as-taggable-on/pull/413)
|
|
24
|
+
* Fixes
|
|
25
|
+
* [@jdelStrother #515 Rename Compatibility methods to reduce chance of conflicts ](https://github.com/mbleigh/acts-as-taggable-on/pull/515)
|
|
26
|
+
* [@seuros #512 fix for << method](https://github.com/mbleigh/acts-as-taggable-on/pull/512)
|
|
27
|
+
* [@sonots #510 fix IN subquery error for mysql](https://github.com/mbleigh/acts-as-taggable-on/pull/510)
|
|
28
|
+
* [@jonseaberg #499 fix for race condition when multiple processes try to add the same tag](https://github.com/mbleigh/acts-as-taggable-on/pull/499)
|
|
29
|
+
* [@leklund #496 Fix for distinct and postgresql json columns errors](https://github.com/mbleigh/acts-as-taggable-on/pull/496)
|
|
30
|
+
* [@thatbettina & @plexus #394 Multiple quoted tags](https://github.com/mbleigh/acts-as-taggable-on/pull/496)
|
|
31
|
+
* Performance
|
|
32
|
+
* Misc
|
|
33
|
+
* [@seuros #511 Rspec 3](https://github.com/mbleigh/acts-as-taggable-on/pull/511)
|
|
22
34
|
|
|
23
35
|
### [3.1.0 / 2014-03-31](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.0.1...v3.1.0)
|
|
24
36
|
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# ActsAsTaggableOn
|
|
2
2
|
[](http://travis-ci.org/mbleigh/acts-as-taggable-on)
|
|
3
3
|
[](https://codeclimate.com/github/mbleigh/acts-as-taggable-on)
|
|
4
|
-
[](http://inch-ci.org/github/mbleigh/acts-as-taggable-on)
|
|
5
5
|
|
|
6
6
|
This plugin was originally based on Acts as Taggable on Steroids by Jonathan Viney.
|
|
7
7
|
It has evolved substantially since that point, but all credit goes to him for the
|
|
@@ -135,11 +135,13 @@ these methods are added to the model: `skill_list`(and `skill_list.add`, `skill_
|
|
|
135
135
|
# => ["joking", "clowning", "boxing", "coding"]
|
|
136
136
|
|
|
137
137
|
@another_user = User.new(:name => "Alice")
|
|
138
|
-
@
|
|
139
|
-
@
|
|
138
|
+
@another_user.skill_list.add("clowning")
|
|
139
|
+
@another_user.save
|
|
140
140
|
|
|
141
141
|
User.skill_counts
|
|
142
|
-
=> [#<ActsAsTaggableOn::Tag id: 1, name: "joking", taggings_count:
|
|
142
|
+
=> [#<ActsAsTaggableOn::Tag id: 1, name: "joking", taggings_count: 1>,
|
|
143
|
+
#<ActsAsTaggableOn::Tag id: 2, name: "clowning", taggings_count: 2>,
|
|
144
|
+
#<ActsAsTaggableOn::Tag id: 3, name: "boxing", taggings_count: 1>]
|
|
143
145
|
```
|
|
144
146
|
|
|
145
147
|
To preserve the order in which tags are created use `acts_as_ordered_taggable`:
|
|
@@ -176,22 +178,22 @@ end
|
|
|
176
178
|
User.tagged_with("awesome").by_join_date
|
|
177
179
|
User.tagged_with("awesome").by_join_date.paginate(:page => params[:page], :per_page => 20)
|
|
178
180
|
|
|
179
|
-
# Find
|
|
181
|
+
# Find users that matches all given tags:
|
|
180
182
|
User.tagged_with(["awesome", "cool"], :match_all => true)
|
|
181
183
|
|
|
182
|
-
# Find
|
|
184
|
+
# Find users with any of the specified tags:
|
|
183
185
|
User.tagged_with(["awesome", "cool"], :any => true)
|
|
184
186
|
|
|
185
|
-
# Find
|
|
187
|
+
# Find users that has not been tagged with awesome or cool:
|
|
186
188
|
User.tagged_with(["awesome", "cool"], :exclude => true)
|
|
187
189
|
|
|
188
|
-
# Find
|
|
190
|
+
# Find users with any of the tags based on context:
|
|
189
191
|
User.tagged_with(['awesome', 'cool'], :on => :tags, :any => true).tagged_with(['smart', 'shy'], :on => :skills, :any => true)
|
|
190
192
|
```
|
|
191
193
|
|
|
192
|
-
You can also use `:wild => true` option along with `:any` or `:exclude` option. It will looking for `%awesome%` and `%cool%` in
|
|
194
|
+
You can also use `:wild => true` option along with `:any` or `:exclude` option. It will be looking for `%awesome%` and `%cool%` in SQL.
|
|
193
195
|
|
|
194
|
-
__Tip:__ `User.tagged_with([])` or `User.tagged_with('')` will return `[]`,
|
|
196
|
+
__Tip:__ `User.tagged_with([])` or `User.tagged_with('')` will return `[]`, an empty set of records.
|
|
195
197
|
|
|
196
198
|
### Relationships
|
|
197
199
|
|
|
@@ -273,7 +275,7 @@ Photo.tagged_with("paris", :on => :locations, :owned_by => @some_user)
|
|
|
273
275
|
To construct tag clouds, the frequency of each tag needs to be calculated.
|
|
274
276
|
Because we specified `acts_as_taggable_on` on the `User` class, we can
|
|
275
277
|
get a calculation of all the tag counts by using `User.tag_counts_on(:customs)`. But what if we wanted a tag count for
|
|
276
|
-
|
|
278
|
+
a single user's posts? To achieve this we call tag_counts on the association:
|
|
277
279
|
|
|
278
280
|
```ruby
|
|
279
281
|
User.find(:first).posts.tag_counts_on(:tags)
|
|
@@ -360,6 +362,11 @@ We have a long list of valued contributors. [Check them all](https://github.com/
|
|
|
360
362
|
|
|
361
363
|
* [Joost Baaij](https://github.com/tilsammans)
|
|
362
364
|
|
|
365
|
+
## TODO
|
|
366
|
+
|
|
367
|
+
- Write benchmark script
|
|
368
|
+
- Resolve concurrency issues
|
|
369
|
+
|
|
363
370
|
## Testing
|
|
364
371
|
|
|
365
372
|
Acts As Taggable On uses RSpec for its test coverage. Inside the gem
|
data/acts-as-taggable-on.gemspec
CHANGED
|
@@ -23,7 +23,6 @@ Gem::Specification.new do |gem|
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
gem.add_runtime_dependency 'activerecord', ['>= 3', '< 5']
|
|
26
|
-
gem.add_runtime_dependency 'actionpack', ['>= 3', '< 5']
|
|
27
26
|
|
|
28
27
|
gem.add_development_dependency 'sqlite3'
|
|
29
28
|
gem.add_development_dependency 'mysql2', '~> 0.3.7'
|
|
@@ -32,7 +31,6 @@ Gem::Specification.new do |gem|
|
|
|
32
31
|
gem.add_development_dependency 'rspec-rails' , '~> 3.0.0.beta'
|
|
33
32
|
gem.add_development_dependency 'rspec-its'
|
|
34
33
|
gem.add_development_dependency 'rspec'
|
|
35
|
-
gem.add_development_dependency 'ammeter'
|
|
36
34
|
gem.add_development_dependency 'barrier'
|
|
37
35
|
gem.add_development_dependency 'database_cleaner'
|
|
38
36
|
end
|
data/lib/acts-as-taggable-on.rb
CHANGED
|
@@ -3,9 +3,35 @@ require 'active_record/version'
|
|
|
3
3
|
require 'active_support/core_ext/module'
|
|
4
4
|
require 'action_view'
|
|
5
5
|
|
|
6
|
+
require_relative 'acts_as_taggable_on/engine' if defined?(Rails)
|
|
7
|
+
|
|
6
8
|
require 'digest/sha1'
|
|
7
9
|
|
|
8
10
|
module ActsAsTaggableOn
|
|
11
|
+
extend ActiveSupport::Autoload
|
|
12
|
+
|
|
13
|
+
autoload :Tag
|
|
14
|
+
autoload :TagList
|
|
15
|
+
autoload :TagListParser
|
|
16
|
+
autoload :Taggable
|
|
17
|
+
autoload :Tagger
|
|
18
|
+
autoload :Tagging
|
|
19
|
+
autoload :TagsHelper
|
|
20
|
+
autoload :VERSION
|
|
21
|
+
|
|
22
|
+
autoload_under 'taggable' do
|
|
23
|
+
autoload :Cache
|
|
24
|
+
autoload :Collection
|
|
25
|
+
autoload :Core
|
|
26
|
+
autoload :Dirty
|
|
27
|
+
autoload :Ownership
|
|
28
|
+
autoload :Related
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
autoload :Utils
|
|
32
|
+
autoload :Compatibility
|
|
33
|
+
|
|
34
|
+
|
|
9
35
|
class DuplicateTagError < StandardError
|
|
10
36
|
end
|
|
11
37
|
|
|
@@ -16,7 +42,7 @@ module ActsAsTaggableOn
|
|
|
16
42
|
|
|
17
43
|
def self.method_missing(method_name, *args, &block)
|
|
18
44
|
@configuration.respond_to?(method_name) ?
|
|
19
|
-
|
|
45
|
+
@configuration.send(method_name, *args, &block) : super
|
|
20
46
|
end
|
|
21
47
|
|
|
22
48
|
def self.respond_to?(method_name, include_private=false)
|
|
@@ -31,7 +57,7 @@ module ActsAsTaggableOn
|
|
|
31
57
|
|
|
32
58
|
class Configuration
|
|
33
59
|
attr_accessor :delimiter, :force_lowercase, :force_parameterize,
|
|
34
|
-
|
|
60
|
+
:strict_case_match, :remove_unused_tags
|
|
35
61
|
|
|
36
62
|
def initialize
|
|
37
63
|
@delimiter = ','
|
|
@@ -45,24 +71,6 @@ module ActsAsTaggableOn
|
|
|
45
71
|
setup
|
|
46
72
|
end
|
|
47
73
|
|
|
48
|
-
require 'acts_as_taggable_on/utils'
|
|
49
|
-
|
|
50
|
-
require 'acts_as_taggable_on/taggable'
|
|
51
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/compatibility'
|
|
52
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/core'
|
|
53
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/collection'
|
|
54
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/cache'
|
|
55
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/ownership'
|
|
56
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/related'
|
|
57
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/dirty'
|
|
58
|
-
|
|
59
|
-
require 'acts_as_taggable_on/tagger'
|
|
60
|
-
require 'acts_as_taggable_on/tag'
|
|
61
|
-
require 'acts_as_taggable_on/tag_list'
|
|
62
|
-
require 'acts_as_taggable_on/tags_helper'
|
|
63
|
-
require 'acts_as_taggable_on/tagging'
|
|
64
|
-
require 'acts_as_taggable_on/engine'
|
|
65
|
-
|
|
66
74
|
ActiveSupport.on_load(:active_record) do
|
|
67
75
|
extend ActsAsTaggableOn::Compatibility
|
|
68
76
|
extend ActsAsTaggableOn::Taggable
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
require 'active_support/core_ext/module/delegation'
|
|
2
3
|
|
|
3
4
|
module ActsAsTaggableOn
|
|
@@ -8,82 +9,6 @@ module ActsAsTaggableOn
|
|
|
8
9
|
add(*args)
|
|
9
10
|
end
|
|
10
11
|
|
|
11
|
-
class << self
|
|
12
|
-
##
|
|
13
|
-
# Returns a new TagList using the given tag string.
|
|
14
|
-
#
|
|
15
|
-
# Example:
|
|
16
|
-
# tag_list = ActsAsTaggableOn::TagList.from("One , Two, Three")
|
|
17
|
-
# tag_list # ["One", "Two", "Three"]
|
|
18
|
-
def from(string)
|
|
19
|
-
string = string.join(ActsAsTaggableOn.glue) if string.respond_to?(:join)
|
|
20
|
-
|
|
21
|
-
new.tap do |tag_list|
|
|
22
|
-
string = string.to_s.dup
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
string.gsub!(double_quote_pattern) {
|
|
26
|
-
# Append the matched tag to the tag list
|
|
27
|
-
tag_list << Regexp.last_match[2]
|
|
28
|
-
# Return the matched delimiter ($3) to replace the matched items
|
|
29
|
-
''
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
string.gsub!(single_quote_pattern) {
|
|
33
|
-
# Append the matched tag ($2) to the tag list
|
|
34
|
-
tag_list << Regexp.last_match[2]
|
|
35
|
-
# Return an empty string to replace the matched items
|
|
36
|
-
''
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
# split the string by the delimiter
|
|
40
|
-
# and add to the tag_list
|
|
41
|
-
tag_list.add(string.split(Regexp.new delimiter))
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def delimiter
|
|
46
|
-
# Parse the quoted tags
|
|
47
|
-
d = ActsAsTaggableOn.delimiter
|
|
48
|
-
# Separate multiple delimiters by bitwise operator
|
|
49
|
-
d = d.join('|') if d.kind_of?(Array)
|
|
50
|
-
|
|
51
|
-
d
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def single_quote_pattern
|
|
55
|
-
%r{
|
|
56
|
-
( # Tag start delimiter ($1)
|
|
57
|
-
\A | # Either string start or
|
|
58
|
-
#{delimiter} # a delimiter
|
|
59
|
-
)
|
|
60
|
-
\s*' # quote (') optionally preceded by whitespace
|
|
61
|
-
(.*?) # Tag ($2)
|
|
62
|
-
'\s* # quote (') optionally followed by whitespace
|
|
63
|
-
(?= # Tag end delimiter (not consumed; is zero-length lookahead)
|
|
64
|
-
#{delimiter}\s* | # Either a delimiter optionally followed by whitespace or
|
|
65
|
-
\z # string end
|
|
66
|
-
)
|
|
67
|
-
}x
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def double_quote_pattern
|
|
71
|
-
%r{
|
|
72
|
-
( # Tag start delimiter ($1)
|
|
73
|
-
\A | # Either string start or
|
|
74
|
-
#{delimiter} # a delimiter
|
|
75
|
-
)
|
|
76
|
-
\s*" # quote (") optionally preceded by whitespace
|
|
77
|
-
(.*?) # Tag ($2)
|
|
78
|
-
"\s* # quote (") optionally followed by whitespace
|
|
79
|
-
(?= # Tag end delimiter (not consumed; is zero-length lookahead)
|
|
80
|
-
#{delimiter}\s* | # Either a delimiter optionally followed by whitespace or
|
|
81
|
-
\z # string end
|
|
82
|
-
)
|
|
83
|
-
}x
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
end
|
|
87
12
|
##
|
|
88
13
|
# Add tags to the tag_list. Duplicate or blank tags will be ignored.
|
|
89
14
|
# Use the <tt>:parse</tt> option to add an unparsed tag string.
|
|
@@ -149,9 +74,10 @@ module ActsAsTaggableOn
|
|
|
149
74
|
|
|
150
75
|
private
|
|
151
76
|
|
|
152
|
-
#
|
|
77
|
+
# Convert everything to string, remove whitespace, duplicates, and blanks.
|
|
153
78
|
def clean!
|
|
154
79
|
reject!(&:blank?)
|
|
80
|
+
map!(&:to_s)
|
|
155
81
|
map!(&:strip)
|
|
156
82
|
map! { |tag| tag.mb_chars.downcase.to_s } if ActsAsTaggableOn.force_lowercase
|
|
157
83
|
map!(&:parameterize) if ActsAsTaggableOn.force_parameterize
|
|
@@ -164,12 +90,23 @@ module ActsAsTaggableOn
|
|
|
164
90
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
165
91
|
options.assert_valid_keys :parse
|
|
166
92
|
|
|
167
|
-
args.map! { |a|
|
|
93
|
+
args.map! { |a| TagListParser.parse(a) } if options[:parse]
|
|
168
94
|
|
|
169
95
|
args.flatten!
|
|
170
96
|
end
|
|
171
97
|
|
|
172
98
|
|
|
99
|
+
## DEPRECATED
|
|
100
|
+
def self.from(string)
|
|
101
|
+
ActiveRecord::Base.logger.warn <<WARNING
|
|
102
|
+
ActsAsTaggableOn::TagList.from is deprecated \
|
|
103
|
+
and will be removed from v4.0+, use \
|
|
104
|
+
ActsAsTaggableOn::TagListParser.parse instead
|
|
105
|
+
WARNING
|
|
106
|
+
TagListParser.parse(string)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
|
|
173
110
|
end
|
|
174
111
|
end
|
|
175
112
|
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
def parse(string)
|
|
11
|
+
string = string.join(ActsAsTaggableOn.glue) if string.respond_to?(:join)
|
|
12
|
+
TagList.new.tap do |tag_list|
|
|
13
|
+
string = string.to_s.dup
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
string.gsub!(double_quote_pattern) {
|
|
17
|
+
# Append the matched tag to the tag list
|
|
18
|
+
tag_list << Regexp.last_match[2]
|
|
19
|
+
# Return the matched delimiter ($3) to replace the matched items
|
|
20
|
+
''
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
string.gsub!(single_quote_pattern) {
|
|
24
|
+
# Append the matched tag ($2) to the tag list
|
|
25
|
+
tag_list << Regexp.last_match[2]
|
|
26
|
+
# Return an empty string to replace the matched items
|
|
27
|
+
''
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# split the string by the delimiter
|
|
31
|
+
# and add to the tag_list
|
|
32
|
+
tag_list.add(string.split(Regexp.new delimiter))
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# private
|
|
38
|
+
def delimiter
|
|
39
|
+
# Parse the quoted tags
|
|
40
|
+
d = ActsAsTaggableOn.delimiter
|
|
41
|
+
# Separate multiple delimiters by bitwise operator
|
|
42
|
+
d = d.join('|') if d.kind_of?(Array)
|
|
43
|
+
d
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# ( # Tag start delimiter ($1)
|
|
47
|
+
# \A | # Either string start or
|
|
48
|
+
# #{delimiter} # a delimiter
|
|
49
|
+
# )
|
|
50
|
+
# \s*" # quote (") optionally preceded by whitespace
|
|
51
|
+
# (.*?) # Tag ($2)
|
|
52
|
+
# "\s* # quote (") optionally followed by whitespace
|
|
53
|
+
# (?= # Tag end delimiter (not consumed; is zero-length lookahead)
|
|
54
|
+
# #{delimiter}\s* | # Either a delimiter optionally followed by whitespace or
|
|
55
|
+
# \z # string end
|
|
56
|
+
# )
|
|
57
|
+
def double_quote_pattern
|
|
58
|
+
/(\A|#{delimiter})\s*"(.*?)"\s*(?=#{delimiter}\s*|\z)/
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# ( # Tag start delimiter ($1)
|
|
62
|
+
# \A | # Either string start or
|
|
63
|
+
# #{delimiter} # a delimiter
|
|
64
|
+
# )
|
|
65
|
+
# \s*' # quote (') optionally preceded by whitespace
|
|
66
|
+
# (.*?) # Tag ($2)
|
|
67
|
+
# '\s* # quote (') optionally followed by whitespace
|
|
68
|
+
# (?= # Tag end delimiter (not consumed; is zero-length lookahead)
|
|
69
|
+
# #{delimiter}\s* | d # Either a delimiter optionally followed by whitespace or
|
|
70
|
+
# \z # string end
|
|
71
|
+
# )
|
|
72
|
+
def single_quote_pattern
|
|
73
|
+
/(\A|#{delimiter})\s*'(.*?)'\s*(?=#{delimiter}\s*|\z)/
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -31,6 +31,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
31
31
|
# to mimic the underlying behavior. While processing this first
|
|
32
32
|
# call to columns, we do the caching column check and dynamically add
|
|
33
33
|
# the class and instance methods
|
|
34
|
+
# FIXME: this method cannot compile in rubinius
|
|
34
35
|
def columns
|
|
35
36
|
@acts_as_taggable_on_cache_columns ||= begin
|
|
36
37
|
db_columns = super
|
|
@@ -53,6 +53,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
53
53
|
# * :order - A piece of SQL to order by. Eg 'tags.count desc' or 'taggings.created_at desc'
|
|
54
54
|
# * :on - Scope the find to only include a certain context
|
|
55
55
|
def all_tags(options = {})
|
|
56
|
+
options = options.dup
|
|
56
57
|
options.assert_valid_keys :start_at, :end_at, :conditions, :order, :limit, :on
|
|
57
58
|
|
|
58
59
|
## Generate conditions:
|
|
@@ -87,6 +88,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
87
88
|
# * :at_most - Exclude tags with a frequency greater than the given value
|
|
88
89
|
# * :on - Scope the find to only include a certain context
|
|
89
90
|
def all_tag_counts(options = {})
|
|
91
|
+
options = options.dup
|
|
90
92
|
options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :on, :id
|
|
91
93
|
|
|
92
94
|
## Generate conditions:
|