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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +21 -1
  3. data/Appraisals +0 -4
  4. data/CHANGELOG.md +27 -15
  5. data/README.md +18 -11
  6. data/acts-as-taggable-on.gemspec +0 -2
  7. data/db/migrate/4_add_missing_taggable_index.rb +9 -0
  8. data/gemfiles/activerecord_3.2.gemfile +0 -1
  9. data/gemfiles/activerecord_4.0.gemfile +0 -1
  10. data/gemfiles/activerecord_4.1.gemfile +0 -1
  11. data/gemfiles/activerecord_edge.gemfile +0 -1
  12. data/lib/acts-as-taggable-on.rb +28 -20
  13. data/lib/acts_as_taggable_on/tag.rb +1 -1
  14. data/lib/acts_as_taggable_on/tag_list.rb +15 -78
  15. data/lib/acts_as_taggable_on/tag_list_parser.rb +78 -0
  16. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/cache.rb +1 -0
  17. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/collection.rb +2 -0
  18. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/core.rb +68 -44
  19. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/ownership.rb +1 -1
  20. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/related.rb +6 -6
  21. data/lib/acts_as_taggable_on/taggable.rb +7 -8
  22. data/lib/acts_as_taggable_on/tagging.rb +7 -1
  23. data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
  24. data/lib/acts_as_taggable_on/utils.rb +26 -50
  25. data/lib/acts_as_taggable_on/version.rb +1 -1
  26. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +1 -1
  27. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +1 -0
  28. data/spec/acts_as_taggable_on/caching_spec.rb +1 -0
  29. data/spec/acts_as_taggable_on/related_spec.rb +1 -0
  30. data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +1 -0
  31. data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +46 -0
  32. data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -40
  33. data/spec/acts_as_taggable_on/tag_spec.rb +34 -38
  34. data/spec/acts_as_taggable_on/taggable/dirty_spec.rb +127 -0
  35. data/spec/acts_as_taggable_on/taggable_spec.rb +84 -164
  36. data/spec/acts_as_taggable_on/tagger_spec.rb +1 -1
  37. data/spec/acts_as_taggable_on/tagging_spec.rb +37 -1
  38. data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -0
  39. data/spec/acts_as_taggable_on/utils_spec.rb +6 -11
  40. data/spec/internal/app/models/cached_model_with_array.rb +1 -1
  41. data/spec/internal/app/models/models.rb +2 -2
  42. data/spec/internal/db/schema.rb +2 -2
  43. data/spec/spec_helper.rb +0 -1
  44. data/spec/support/0-helpers.rb +32 -0
  45. data/spec/support/database_cleaner.rb +4 -0
  46. metadata +17 -47
  47. data/spec/bm.rb +0 -52
  48. data/spec/schema.rb +0 -82
  49. /data/lib/acts_as_taggable_on/{acts_as_taggable_on/compatibility.rb → compatibility.rb} +0 -0
  50. /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: bbd5e8b1bc5588e5fa1f7c52d650d51284426c46
4
- data.tar.gz: 8310137e6ed880ac050af6a67e0afb648720c0a8
3
+ metadata.gz: 5c20928991764b079db87d3d1ba32846d0a3e01f
4
+ data.tar.gz: 94cae20632e6d61d21cd55921cb7ee284cb6f766
5
5
  SHA512:
6
- metadata.gz: 1ba873f7c016fac3ddbbad4f8ff7b946bb7ffca89074b4db76f63d58c241e01726d18fcd502a7e60fe31b635f6f1fae5d5c26dce911211db07a9c8506e07d13b
7
- data.tar.gz: 098fd2268afe38cd7278a8908e6c11b1aeba8370754224730510d71a336130b89831051640fac2af432171c09f51e346b18c141e2d97553a68efc680a6afa680
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.1.0...master)
8
-
9
- * Breaking Changes
10
- * Features
11
- * [@chess #413 Hook to support STI subclasses of Tag in save_tags](https://github.com/mbleigh/acts-as-taggable-on/pull/413)
12
- * Fixes
13
- * [@jdelStrother #515 Rename Compatibility methods to reduce chance of conflicts ](https://github.com/mbleigh/acts-as-taggable-on/pull/515)
14
- * [@seuros #512 fix for << method](https://github.com/mbleigh/acts-as-taggable-on/pull/512)
15
- * [@sonots #510 fix IN subquery error for mysql](https://github.com/mbleigh/acts-as-taggable-on/pull/510)
16
- * [@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)
17
- * [@leklund #496 Fix for distinct and postgresql json columns errors](https://github.com/mbleigh/acts-as-taggable-on/pull/496)
18
- * [@thatbettina & @plexus #394 Multiple quoted tags](https://github.com/mbleigh/acts-as-taggable-on/pull/496)
19
- * Performance
20
- * Misc
21
- * [@seuros #511 Rspec 3](https://github.com/mbleigh/acts-as-taggable-on/pull/511)
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
  [![Build Status](https://secure.travis-ci.org/mbleigh/acts-as-taggable-on.png)](http://travis-ci.org/mbleigh/acts-as-taggable-on)
3
3
  [![Code Climate](https://codeclimate.com/github/mbleigh/acts-as-taggable-on.png)](https://codeclimate.com/github/mbleigh/acts-as-taggable-on)
4
- [![Inline docs](http://inch-pages.github.io/github/mbleigh/acts-as-taggable-on.png)](http://inch-pages.github.io/github/mbleigh/acts-as-taggable-on)
4
+ [![Inline docs](http://inch-ci.org/github/mbleigh/acts-as-taggable-on.png)](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
- @another_story.skill_list.add("joking")
139
- @another_story.save
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: 2>, #<ActsAsTaggableOn::Tag id: 2, name: "clowning", taggings_count: 1>, ...]
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 a user with matching all tags, not just one:
181
+ # Find users that matches all given tags:
180
182
  User.tagged_with(["awesome", "cool"], :match_all => true)
181
183
 
182
- # Find a user with any of the tags:
184
+ # Find users with any of the specified tags:
183
185
  User.tagged_with(["awesome", "cool"], :any => true)
184
186
 
185
- # Find a user that not tags with awesome or cool:
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 a user with any of tags based on context:
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 sql.
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 `[]`, but not all records.
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
- an single user's posts? To achieve this we call tag_counts on the association:
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
@@ -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
@@ -0,0 +1,9 @@
1
+ class AddMissingTaggableIndex < ActiveRecord::Migration
2
+ def self.up
3
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
4
+ end
5
+
6
+ def self.down
7
+ remove_index :taggings, [:taggable_id, :taggable_type, :context]
8
+ end
9
+ end
@@ -3,7 +3,6 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "activerecord", "~> 3.2"
6
- gem "actionpack", "~> 3.2"
7
6
 
8
7
  group :local_development do
9
8
  gem "guard"
@@ -3,7 +3,6 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "activerecord", "~> 4.0"
6
- gem "actionpack", "~> 4.0"
7
6
 
8
7
  group :local_development do
9
8
  gem "guard"
@@ -3,7 +3,6 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "activerecord", "~> 4.1"
6
- gem "actionpack", "~> 4.1"
7
6
 
8
7
  group :local_development do
9
8
  gem "guard"
@@ -3,7 +3,6 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "activerecord", :github => "rails/rails"
6
- gem "actionpack", :github => "rails/rails"
7
6
  gem "arel", :github => "rails/arel"
8
7
 
9
8
  group :local_development do
@@ -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
- @configuration.send(method_name, *args, &block) : super
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
- :strict_case_match, :remove_unused_tags
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,4 +1,4 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
2
  module ActsAsTaggableOn
3
3
  class Tag < ::ActiveRecord::Base
4
4
 
@@ -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
- # Remove whitespace, duplicates, and blanks.
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| self.class.from(a) } if options[:parse]
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: