acts-as-taggable-on 3.3.0 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5c20928991764b079db87d3d1ba32846d0a3e01f
4
- data.tar.gz: 94cae20632e6d61d21cd55921cb7ee284cb6f766
3
+ metadata.gz: 281e3a40ea12a2d4bf7cd9a17548afaa79d38551
4
+ data.tar.gz: 87e34ac29deab9211aea132e6f2189635d6918d5
5
5
  SHA512:
6
- metadata.gz: 29cfdac7542aed8bb4f8f55a0c7d33f592739ae1f887a5399e0bd34fd686a5824b3e5335f5037a0ada5928001b4a8b3ca124d21771d5af648fc6f55735bf31da
7
- data.tar.gz: 535aa2433b00889491d6aaf7f488b372ebc1ac2e60f8d9cd2337ae07a19ce2ce1bce3438a1f2db48c77b52a3a57c56648c8c31ddf1b4f0c2447bebae27d67fe0
6
+ metadata.gz: 2168df4d62261fb9fbaacff107fb1311f9a9c8567f674b74e784dbd48a0e006825371cfa68de03595907974a1fc842383ca2ea90c2d48e51284a7398f492442a
7
+ data.tar.gz: 4478cd0771d44535549f4516f9d087b94250e2087c8c75fe559752e1f033c8336167dcaede44b1223360d1e3821da8d515e5fb03398689ea3fb207dd9f15673b
data/Appraisals CHANGED
@@ -3,11 +3,17 @@ appraise "activerecord-3.2" do
3
3
  end
4
4
 
5
5
  appraise "activerecord-4.0" do
6
- gem "activerecord", "~> 4.0"
6
+ gem "activerecord", "~> 4.0.0"
7
7
  end
8
8
 
9
9
  appraise "activerecord-4.1" do
10
- gem "activerecord", "~> 4.1"
10
+ gem "activerecord", "~> 4.1.0"
11
+ end
12
+
13
+ appraise "activerecord-4.2" do
14
+ gem "railties", ">= 4.2.0.beta1"
15
+ gem "activerecord", ">= 4.2.0.beta1"
16
+ gem "rack", ">= 1.6.0.beta"
11
17
  end
12
18
 
13
19
  appraise "activerecord-edge" do
data/CHANGELOG.md CHANGED
@@ -4,18 +4,99 @@ 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.2.0...master)
7
+ ### Master [changes](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.4.0...master)
8
8
 
9
9
  * Breaking Changes
10
- * Taggable models are not extend with ActsAsTaggableOn::Utils anymore
11
10
  * Features
12
11
  * Fixes
13
12
  * Performance
14
13
  * Misc
15
- * Deleted outdated benchmark script
14
+
15
+ ### [3.4.1 / 2014-09-01](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.4.0...v3.4.1)
16
+ * Fixes
17
+ * [@konukhov fix owned ordered taggable bug]((https://github.com/mbleigh/acts-as-taggable-on/pull/585)
18
+
19
+ ### [3.4.0 / 2014-08-29](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.3.0...v3.4.0)
20
+
21
+ * Features
22
+ * [@ProGM Support for custom parsers for tags](https://github.com/mbleigh/acts-as-taggable-on/pull/579)
23
+ * [@damzcodes #577 Popular feature](https://github.com/mbleigh/acts-as-taggable-on/pull/577)
24
+ * Fixes
25
+ * [@twalpole Update for rails edge (4.2)](https://github.com/mbleigh/acts-as-taggable-on/pull/583)
26
+ * Performance
27
+ * [@dontfidget #587 Use pluck instead of select](https://github.com/mbleigh/acts-as-taggable-on/pull/587)
28
+
29
+ ### [3.3.0 / 2014-07-08](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.6...v3.3.0)
30
+
31
+ * Features
32
+ * [@felipeclopes #488 Support for `start_at` and `end_at` restrictions when selecting tags](https://github.com/mbleigh/acts-as-taggable-on/pull/488)
33
+
34
+ * Fixes
35
+ * [@tonytonyjan #560 Fix for `ActsAsTaggableOn.remove_unused_tags` doesn't work](https://github.com/mbleigh/acts-as-taggable-on/pull/560)
36
+ * [@ThearkInn #555 Fix for `tag_cloud` helper to generate correct css tags](https://github.com/mbleigh/acts-as-taggable-on/pull/555)
37
+
38
+ * Performance
39
+ * [@pcai #556 Add back taggables index in the taggins table](https://github.com/mbleigh/acts-as-taggable-on/pull/556)
40
+
41
+
42
+ ### [3.2.6 / 2014-05-28](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.5...v3.2.6)
43
+
44
+ * Fixes
45
+ * [@seuros #548 Fix dirty marking when tags are not ordered](https://github.com/mbleigh/acts-as-taggable-on/issues/548)
46
+
47
+ * Misc
48
+ * [@seuros Remove actionpack dependency](https://github.com/mbleigh/acts-as-taggable-on/commit/5d20e0486c892fbe21af42fdcd79d0b6ebe87ed4)
49
+ * [@seuros #547 Add tests for update_attributes](https://github.com/mbleigh/acts-as-taggable-on/issues/547)
50
+
51
+
52
+ ### [3.2.5 / 2014-05-25](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.4...v3.2.5)
53
+
54
+ * Fixes
55
+ * [@seuros #546 Fix autoload bug. Now require engine file instead of autoloading it](https://github.com/mbleigh/acts-as-taggable-on/issues/546)
56
+
57
+
58
+ ### [3.2.4 / 2014-05-24](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.3...v3.2.4)
59
+
60
+ * Fixes
61
+ * [@seuros #544 Fix incorrect query generation related to `GROUP BY` SQL statement](https://github.com/mbleigh/acts-as-taggable-on/issues/544)
62
+
63
+ * Misc
64
+ * [@seuros #545 Remove `ammeter` development dependency](https://github.com/mbleigh/acts-as-taggable-on/pull/545)
65
+ * [@seuros #545 Deprecate `TagList.from` in favor of `TagListParser.parse`](https://github.com/mbleigh/acts-as-taggable-on/pull/545)
66
+ * [@seuros #543 Introduce lazy loading](https://github.com/mbleigh/acts-as-taggable-on/pull/543)
67
+ * [@seuros #541 Deprecate ActsAsTaggableOn::Utils](https://github.com/mbleigh/acts-as-taggable-on/pull/541)
68
+
69
+
70
+ ### [3.2.3 / 2014-05-16](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.2...v3.2.3)
71
+
72
+ * Fixes
73
+ * [@seuros #540 Fix for tags removal (it was affecting all records with the same tag)](https://github.com/mbleigh/acts-as-taggable-on/pull/540)
74
+ * [@akcho8 #535 Fix for `options` Hash passed to methods from being deleted by those methods](https://github.com/mbleigh/acts-as-taggable-on/pull/535)
75
+
76
+
77
+ ### [3.2.2 / 2014-05-07](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.1...v3.2.2)
78
+
79
+ * Breaking Changes
80
+ * [@seuros #526 Taggable models are not extended with ActsAsTaggableOn::Utils anymore](https://github.com/mbleigh/acts-as-taggable-on/pull/526)
81
+
82
+ * Fixes
83
+ * [@seuros #536 Add explicit conversion of tags to strings (when assigning tags)](https://github.com/mbleigh/acts-as-taggable-on/pull/536)
84
+
85
+ * Misc
86
+ * [@seuros #526 Delete outdated benchmark script](https://github.com/mbleigh/acts-as-taggable-on/pull/526)
87
+ * [@seuros #525 Fix tests so that they pass with MySQL](https://github.com/mbleigh/acts-as-taggable-on/pull/525)
88
+
89
+
90
+ ### [3.2.1 / 2014-05-06](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.0...v3.2.1)
91
+
92
+ * Misc
93
+ * [@seuros #523 Run tests loading only ActiveRecord (without the full Rails stack)](https://github.com/mbleigh/acts-as-taggable-on/pull/523)
94
+ * [@seuros #523 Remove activesupport dependency](https://github.com/mbleigh/acts-as-taggable-on/pull/523)
95
+ * [@seuros #523 Introduce database_cleaner in specs](https://github.com/mbleigh/acts-as-taggable-on/pull/523)
96
+ * [@seuros #520 Tag_list cleanup](https://github.com/mbleigh/acts-as-taggable-on/pull/520)
16
97
 
17
98
 
18
- ### [3.2.0 / 2014-05-1](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.1.1...v3.2.0)
99
+ ### [3.2.0 / 2014-05-01](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.1.1...v3.2.0)
19
100
 
20
101
  * Breaking Changes
21
102
  * ActsAsTaggableOn::Tag is not extend with ActsAsTaggableOn::Utils anymore
data/README.md CHANGED
@@ -70,6 +70,12 @@ class User < ActiveRecord::Base
70
70
  acts_as_taggable_on :skills, :interests
71
71
  end
72
72
 
73
+ class UsersController < ApplicationController
74
+ def user_params
75
+ params.require(:user).permit(:name, :tag_list) ## Rails 4 strong params usage
76
+ end
77
+ end
78
+
73
79
  @user = User.new(:name => "Bobby")
74
80
  ```
75
81
 
@@ -164,6 +170,22 @@ end
164
170
  @user.tag_list # => ["north", "east", "south", "west"]
165
171
  ```
166
172
 
173
+ ### Finding most or least used tags
174
+
175
+ You can find the most or least used tags by using:
176
+
177
+ ```ruby
178
+ ActsAsTaggableOn::Tag.most_used
179
+ ActsAsTaggableOn::Tag.least_used
180
+ ```
181
+
182
+ You can also filter the results by passing the method a limit, however the default limit is 50.
183
+
184
+ ```ruby
185
+ ActsAsTaggableOn::Tag.most_used(10)
186
+ ActsAsTaggableOn::Tag.least_used(10)
187
+ ```
188
+
167
189
  ### Finding Tagged Objects
168
190
 
169
191
  Acts As Taggable On uses scopes to create an association for tags.
@@ -195,6 +217,7 @@ You can also use `:wild => true` option along with `:any` or `:exclude` option.
195
217
 
196
218
  __Tip:__ `User.tagged_with([])` or `User.tagged_with('')` will return `[]`, an empty set of records.
197
219
 
220
+
198
221
  ### Relationships
199
222
 
200
223
  You can find objects of the same type based on similar tags on certain contexts.
@@ -231,6 +254,43 @@ to allow for dynamic tag contexts (this could be user generated tag contexts!)
231
254
  User.tagged_with("same", :on => :customs) # => [@user]
232
255
  ```
233
256
 
257
+ ### Tag Parsers
258
+
259
+ If you want to change how tags are parsed, you can define a your own implementation:
260
+
261
+ ```ruby
262
+ class MyParser < ActsAsTaggableOn::GenericParser
263
+ def parse
264
+ TagList.new.tap do |tag_list|
265
+ tag_list.add @tag_list.split('|')
266
+ end
267
+ end
268
+ end
269
+ ```
270
+
271
+ Now you can use this parser, passing it as parameter:
272
+
273
+ ```ruby
274
+ @user = User.new(:name => "Bobby")
275
+ @user.tag_list = "east, south"
276
+ @user.tag_list.add("north|west", parser: MyParser)
277
+ @user.tag_list # => ["north", "east", "south", "west"]
278
+
279
+ # Or also:
280
+ @user.tag_list.parser = MyParser
281
+ @user.tag_list.add("north|west")
282
+ @user.tag_list # => ["north", "east", "south", "west"]
283
+ ```
284
+
285
+ Or change it globally:
286
+
287
+ ```ruby
288
+ ActsAsTaggable.default_parser = MyParser
289
+ @user = User.new(:name => "Bobby")
290
+ @user.tag_list = "east|south"
291
+ @user.tag_list # => ["east", "south"]
292
+ ```
293
+
234
294
  ### Tag Ownership
235
295
 
236
296
  Tags can have owners:
@@ -22,13 +22,13 @@ Gem::Specification.new do |gem|
22
22
  gem.post_install_message = File.read('UPGRADING.md')
23
23
  end
24
24
 
25
- gem.add_runtime_dependency 'activerecord', ['>= 3', '< 5']
25
+ gem.add_runtime_dependency 'activerecord', ['>= 3.2', '< 5']
26
26
 
27
27
  gem.add_development_dependency 'sqlite3'
28
28
  gem.add_development_dependency 'mysql2', '~> 0.3.7'
29
29
  gem.add_development_dependency 'pg'
30
30
 
31
- gem.add_development_dependency 'rspec-rails' , '~> 3.0.0.beta'
31
+ gem.add_development_dependency 'rspec-rails'
32
32
  gem.add_development_dependency 'rspec-its'
33
33
  gem.add_development_dependency 'rspec'
34
34
  gem.add_development_dependency 'barrier'
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 4.0"
5
+ gem "activerecord", "~> 4.0.0"
6
6
 
7
7
  group :local_development do
8
8
  gem "guard"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 4.1"
5
+ gem "activerecord", "~> 4.1.0"
6
6
 
7
7
  group :local_development do
8
8
  gem "guard"
@@ -0,0 +1,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "railties", ">= 4.2.0.beta1"
6
+ gem "activerecord", ">= 4.2.0.beta1"
7
+ gem "rack", ">= 1.6.0.beta"
8
+
9
+ group :local_development do
10
+ gem "guard"
11
+ gem "guard-rspec"
12
+ gem "appraisal"
13
+ gem "rake"
14
+ end
15
+
16
+ gemspec :path => "../"
@@ -1,7 +1,6 @@
1
1
  require 'active_record'
2
2
  require 'active_record/version'
3
3
  require 'active_support/core_ext/module'
4
- require 'action_view'
5
4
 
6
5
  require_relative 'acts_as_taggable_on/engine' if defined?(Rails)
7
6
 
@@ -12,6 +11,8 @@ module ActsAsTaggableOn
12
11
 
13
12
  autoload :Tag
14
13
  autoload :TagList
14
+ autoload :GenericParser
15
+ autoload :DefaultParser
15
16
  autoload :TagListParser
16
17
  autoload :Taggable
17
18
  autoload :Tagger
@@ -57,7 +58,7 @@ module ActsAsTaggableOn
57
58
 
58
59
  class Configuration
59
60
  attr_accessor :delimiter, :force_lowercase, :force_parameterize,
60
- :strict_case_match, :remove_unused_tags
61
+ :strict_case_match, :remove_unused_tags, :default_parser
61
62
 
62
63
  def initialize
63
64
  @delimiter = ','
@@ -65,6 +66,16 @@ module ActsAsTaggableOn
65
66
  @force_parameterize = false
66
67
  @strict_case_match = false
67
68
  @remove_unused_tags = false
69
+ @default_parser = DefaultParser
70
+ end
71
+
72
+ def delimiter=(string)
73
+ ActiveRecord::Base.logger.warn <<WARNING
74
+ ActsAsTaggableOn.delimiter is deprecated \
75
+ and will be removed from v4.0+, use \
76
+ a ActsAsTaggableOn.default_parser instead
77
+ WARNING
78
+ @delimiter = string
68
79
  end
69
80
  end
70
81
 
@@ -0,0 +1,79 @@
1
+ module ActsAsTaggableOn
2
+ ##
3
+ # Returns a new TagList using the given tag string.
4
+ #
5
+ # Example:
6
+ # tag_list = ActsAsTaggableOn::DefaultParser.parse("One , Two, Three")
7
+ # tag_list # ["One", "Two", "Three"]
8
+ class DefaultParser < GenericParser
9
+
10
+ def parse
11
+ string = @tag_list
12
+
13
+ string = string.join(ActsAsTaggableOn.glue) if string.respond_to?(:join)
14
+ TagList.new.tap do |tag_list|
15
+ string = string.to_s.dup
16
+
17
+ string.gsub!(double_quote_pattern) {
18
+ # Append the matched tag to the tag list
19
+ tag_list << Regexp.last_match[2]
20
+ # Return the matched delimiter ($3) to replace the matched items
21
+ ''
22
+ }
23
+
24
+ string.gsub!(single_quote_pattern) {
25
+ # Append the matched tag ($2) to the tag list
26
+ tag_list << Regexp.last_match[2]
27
+ # Return an empty string to replace the matched items
28
+ ''
29
+ }
30
+
31
+ # split the string by the delimiter
32
+ # and add to the tag_list
33
+ tag_list.add(string.split(Regexp.new delimiter))
34
+ end
35
+ end
36
+
37
+
38
+ # private
39
+ def delimiter
40
+ # Parse the quoted tags
41
+ d = ActsAsTaggableOn.delimiter
42
+ # Separate multiple delimiters by bitwise operator
43
+ d = d.join('|') if d.kind_of?(Array)
44
+ d
45
+ end
46
+
47
+ # ( # Tag start delimiter ($1)
48
+ # \A | # Either string start or
49
+ # #{delimiter} # a delimiter
50
+ # )
51
+ # \s*" # quote (") optionally preceded by whitespace
52
+ # (.*?) # Tag ($2)
53
+ # "\s* # quote (") optionally followed by whitespace
54
+ # (?= # Tag end delimiter (not consumed; is zero-length lookahead)
55
+ # #{delimiter}\s* | # Either a delimiter optionally followed by whitespace or
56
+ # \z # string end
57
+ # )
58
+ def double_quote_pattern
59
+ /(\A|#{delimiter})\s*"(.*?)"\s*(?=#{delimiter}\s*|\z)/
60
+ end
61
+
62
+ # ( # Tag start delimiter ($1)
63
+ # \A | # Either string start or
64
+ # #{delimiter} # a delimiter
65
+ # )
66
+ # \s*' # quote (') optionally preceded by whitespace
67
+ # (.*?) # Tag ($2)
68
+ # '\s* # quote (') optionally followed by whitespace
69
+ # (?= # Tag end delimiter (not consumed; is zero-length lookahead)
70
+ # #{delimiter}\s* | d # Either a delimiter optionally followed by whitespace or
71
+ # \z # string end
72
+ # )
73
+ def single_quote_pattern
74
+ /(\A|#{delimiter})\s*'(.*?)'\s*(?=#{delimiter}\s*|\z)/
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,19 @@
1
+ module ActsAsTaggableOn
2
+ ##
3
+ # Returns a new TagList using the given tag string.
4
+ #
5
+ # Example:
6
+ # tag_list = ActsAsTaggableOn::GenericParser.new.parse("One , Two, Three")
7
+ # tag_list # ["One", "Two", "Three"]
8
+ class GenericParser
9
+ def initialize(tag_list)
10
+ @tag_list = tag_list
11
+ end
12
+
13
+ def parse
14
+ TagList.new.tap do |tag_list|
15
+ tag_list.add @tag_list.split(',').map(&:strip).reject(&:empty?)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -20,6 +20,8 @@ module ActsAsTaggableOn
20
20
  end
21
21
 
22
22
  ### SCOPES:
23
+ scope :most_used, ->(limit = 20) { order('taggings_count desc').limit(limit) }
24
+ scope :least_used, ->(limit = 20) { order('taggings_count asc').limit(limit) }
23
25
 
24
26
  def self.named(name)
25
27
  if ActsAsTaggableOn.strict_case_match
@@ -101,6 +103,9 @@ module ActsAsTaggableOn
101
103
  end
102
104
 
103
105
  class << self
106
+
107
+
108
+
104
109
  private
105
110
 
106
111
  def comparable_name(str)
@@ -4,8 +4,10 @@ require 'active_support/core_ext/module/delegation'
4
4
  module ActsAsTaggableOn
5
5
  class TagList < Array
6
6
  attr_accessor :owner
7
+ attr_accessor :parser
7
8
 
8
9
  def initialize(*args)
10
+ @parser = ActsAsTaggableOn.default_parser
9
11
  add(*args)
10
12
  end
11
13
 
@@ -88,9 +90,11 @@ module ActsAsTaggableOn
88
90
 
89
91
  def extract_and_apply_options!(args)
90
92
  options = args.last.is_a?(Hash) ? args.pop : {}
91
- options.assert_valid_keys :parse
93
+ options.assert_valid_keys :parse, :parser
92
94
 
93
- args.map! { |a| TagListParser.parse(a) } if options[:parse]
95
+ parser = options[:parser] ? options[:parser] : @parser
96
+
97
+ args.map! { |a| parser.new(a).parse } if options[:parse] || options[:parser]
94
98
 
95
99
  args.flatten!
96
100
  end
@@ -101,9 +105,9 @@ module ActsAsTaggableOn
101
105
  ActiveRecord::Base.logger.warn <<WARNING
102
106
  ActsAsTaggableOn::TagList.from is deprecated \
103
107
  and will be removed from v4.0+, use \
104
- ActsAsTaggableOn::TagListParser.parse instead
108
+ ActsAsTaggableOn::DefaultParser.new instead
105
109
  WARNING
106
- TagListParser.parse(string)
110
+ @parser.new(string).parse
107
111
  end
108
112
 
109
113
 
@@ -7,72 +7,15 @@ module ActsAsTaggableOn
7
7
  # tag_list # ["One", "Two", "Three"]
8
8
  module TagListParser
9
9
  class << self
10
+ ## DEPRECATED
10
11
  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
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
34
18
  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
19
  end
77
20
  end
78
21
  end
@@ -135,7 +135,7 @@ module ActsAsTaggableOn::Taggable
135
135
  table_name_pkey = "#{table_name}.#{primary_key}"
136
136
  if ActsAsTaggableOn::Utils.using_mysql?
137
137
  # See https://github.com/mbleigh/acts-as-taggable-on/pull/457 for details
138
- scoped_ids = select(table_name_pkey).map(&:id)
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
141
  tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{safe_to_sql(select(table_name_pkey))})")
@@ -77,15 +77,15 @@ module ActsAsTaggableOn::Taggable
77
77
  # * <tt>:end_at</tt> - Restrict the tags to those created before a certain time
78
78
  #
79
79
  # Example:
80
- # User.tagged_with("awesome", "cool") # Users that are tagged with awesome and cool
81
- # User.tagged_with("awesome", "cool", :exclude => true) # Users that are not tagged with awesome or cool
82
- # User.tagged_with("awesome", "cool", :any => true) # Users that are tagged with awesome or cool
83
- # User.tagged_with("awesome", "cool", :any => true, :order_by_matching_tag_count => true) # Sort by users who match the most tags, descending
84
- # User.tagged_with("awesome", "cool", :match_all => true) # Users that are tagged with just awesome and cool
85
- # User.tagged_with("awesome", "cool", :owned_by => foo ) # Users that are tagged with just awesome and cool by 'foo'
86
- # User.tagged_with("awesome", "cool", :owned_by => foo, :start_at => Date.today ) # Users that are tagged with just awesome, cool by 'foo' and starting today
80
+ # User.tagged_with(["awesome", "cool"]) # Users that are tagged with awesome and cool
81
+ # User.tagged_with(["awesome", "cool"], :exclude => true) # Users that are not tagged with awesome or cool
82
+ # User.tagged_with(["awesome", "cool"], :any => true) # Users that are tagged with awesome or cool
83
+ # User.tagged_with(["awesome", "cool"], :any => true, :order_by_matching_tag_count => true) # Sort by users who match the most tags, descending
84
+ # User.tagged_with(["awesome", "cool"], :match_all => true) # Users that are tagged with just awesome and cool
85
+ # User.tagged_with(["awesome", "cool"], :owned_by => foo ) # Users that are tagged with just awesome and cool by 'foo'
86
+ # User.tagged_with(["awesome", "cool"], :owned_by => foo, :start_at => Date.today ) # Users that are tagged with just awesome, cool by 'foo' and starting today
87
87
  def tagged_with(tags, options = {})
88
- tag_list = ActsAsTaggableOn::TagListParser.parse(tags)
88
+ tag_list = ActsAsTaggableOn.default_parser.new(tags).parse
89
89
  options = options.dup
90
90
  empty_result = where('1 = 0')
91
91
 
@@ -278,7 +278,7 @@ module ActsAsTaggableOn::Taggable
278
278
  if instance_variable_get(variable_name)
279
279
  instance_variable_get(variable_name)
280
280
  elsif cached_tag_list_on(context) && self.class.caching_tag_list_on?(context)
281
- instance_variable_set(variable_name, ActsAsTaggableOn::TagListParser.parse(cached_tag_list_on(context)))
281
+ instance_variable_set(variable_name, ActsAsTaggableOn.default_parser.new(cached_tag_list_on(context)).parse)
282
282
  else
283
283
  instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(tags_on(context).map(&:name)))
284
284
  end
@@ -328,7 +328,7 @@ module ActsAsTaggableOn::Taggable
328
328
  variable_name = "@#{context.to_s.singularize}_list"
329
329
  process_dirty_object(context, new_list) unless custom_contexts.include?(context.to_s)
330
330
 
331
- instance_variable_set(variable_name, ActsAsTaggableOn::TagListParser.parse(new_list))
331
+ instance_variable_set(variable_name, ActsAsTaggableOn.default_parser.new(new_list).parse)
332
332
  end
333
333
 
334
334
  def tagging_contexts
@@ -342,13 +342,13 @@ module ActsAsTaggableOn::Taggable
342
342
  if changed_attributes.include?(attrib)
343
343
  # The attribute already has an unsaved change.
344
344
  old = changed_attributes[attrib]
345
- changed_attributes.delete(attrib) if old.to_s == value.to_s
345
+ @changed_attributes.delete(attrib) if old.to_s == value.to_s
346
346
  else
347
347
  old = tag_list_on(context)
348
348
  if self.class.preserve_tag_order
349
- changed_attributes[attrib] = old if old.to_s != value.to_s
349
+ @changed_attributes[attrib] = old if old.to_s != value.to_s
350
350
  else
351
- changed_attributes[attrib] = old.to_s if old.sort != ActsAsTaggableOn::TagListParser.parse(value).sort
351
+ @changed_attributes[attrib] = old.to_s if old.sort != ActsAsTaggableOn.default_parser.new(value).parse.sort
352
352
  end
353
353
  end
354
354
  end
@@ -420,6 +420,18 @@ module ActsAsTaggableOn::Taggable
420
420
 
421
421
  private
422
422
 
423
+ # Filters the tag lists from the attribute names.
424
+ def attributes_for_update(attribute_names)
425
+ tag_lists = tag_types.map {|tags_type| "#{tags_type.to_s.singularize}_list"}
426
+ super.delete_if {|attr| tag_lists.include? attr }
427
+ end
428
+
429
+ # Filters the tag lists from the attribute names.
430
+ def attributes_for_create(attribute_names)
431
+ tag_lists = tag_types.map {|tags_type| "#{tags_type.to_s.singularize}_list"}
432
+ super.delete_if {|attr| tag_lists.include? attr }
433
+ end
434
+
423
435
  ##
424
436
  # Override this hook if you wish to subclass {ActsAsTaggableOn::Tag} --
425
437
  # context is provided so that you may conditionally use a Tag subclass
@@ -63,7 +63,7 @@ module ActsAsTaggableOn::Taggable
63
63
 
64
64
  cache = cached_owned_tag_list_on(context)
65
65
 
66
- cache[owner] = ActsAsTaggableOn::TagListParser.parse(new_list)
66
+ cache[owner] = ActsAsTaggableOn.default_parser.new(new_list).parse
67
67
  end
68
68
 
69
69
  def reload(*args)
@@ -82,7 +82,7 @@ module ActsAsTaggableOn::Taggable
82
82
  tags = find_or_create_tags_from_list_with_context(tag_list.uniq, context)
83
83
 
84
84
  # Tag objects for owned tags
85
- owned_tags = owner_tags_on(owner, context)
85
+ owned_tags = owner_tags_on(owner, context).to_a
86
86
 
87
87
  # Tag maintenance based on whether preserving the created order of tags
88
88
  if self.class.preserve_tag_order?
@@ -1,4 +1,4 @@
1
1
  module ActsAsTaggableOn
2
- VERSION = '3.3.0'
2
+ VERSION = '3.4.1'
3
3
  end
4
4
 
@@ -0,0 +1,47 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe ActsAsTaggableOn::DefaultParser do
5
+ it '#parse should return empty array if empty array is passed' do
6
+ parser = ActsAsTaggableOn::DefaultParser.new([])
7
+ expect(parser.parse).to be_empty
8
+ end
9
+
10
+ describe 'Multiple Delimiter' do
11
+ before do
12
+ @old_delimiter = ActsAsTaggableOn.delimiter
13
+ end
14
+
15
+ after do
16
+ ActsAsTaggableOn.delimiter = @old_delimiter
17
+ end
18
+
19
+ it 'should separate tags by delimiters' do
20
+ ActsAsTaggableOn.delimiter = [',', ' ', '\|']
21
+ parser = ActsAsTaggableOn::DefaultParser.new('cool, data|I have')
22
+ expect(parser.parse.to_s).to eq('cool, data, I, have')
23
+ end
24
+
25
+ it 'should escape quote' do
26
+ ActsAsTaggableOn.delimiter = [',', ' ', '\|']
27
+ parser = ActsAsTaggableOn::DefaultParser.new("'I have'|cool, data")
28
+ expect(parser.parse.to_s).to eq('"I have", cool, data')
29
+
30
+ parser = ActsAsTaggableOn::DefaultParser.new('"I, have"|cool, data')
31
+ expect(parser.parse.to_s).to eq('"I, have", cool, data')
32
+ end
33
+
34
+ it 'should work for utf8 delimiter and long delimiter' do
35
+ ActsAsTaggableOn.delimiter = [',', '的', '可能是']
36
+ parser = ActsAsTaggableOn::DefaultParser.new('我的东西可能是不见了,还好有备份')
37
+ expect(parser.parse.to_s).to eq('我, 东西, 不见了, 还好有备份')
38
+ end
39
+
40
+ it 'should work for multiple quoted tags' do
41
+ ActsAsTaggableOn.delimiter = [',']
42
+ parser = ActsAsTaggableOn::DefaultParser.new('"Ruby Monsters","eat Katzenzungen"')
43
+ expect(parser.parse.to_s).to eq('Ruby Monsters, eat Katzenzungen')
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,14 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe ActsAsTaggableOn::GenericParser do
5
+ it '#parse should return empty array if empty tag string is passed' do
6
+ tag_list = ActsAsTaggableOn::GenericParser.new('')
7
+ expect(tag_list.parse).to be_empty
8
+ end
9
+
10
+ it '#parse should separate tags by comma' do
11
+ tag_list = ActsAsTaggableOn::GenericParser.new('cool,data,,I,have')
12
+ expect(tag_list.parse).to eq(%w(cool data I have))
13
+ end
14
+ end
@@ -114,7 +114,36 @@ describe ActsAsTaggableOn::TagList do
114
114
 
115
115
  ActsAsTaggableOn.force_lowercase = false
116
116
  end
117
+ end
118
+
119
+ describe 'custom parser' do
120
+ let(:parser) { double(parse: %w(cool wicked)) }
121
+ let(:parser_class) { stub_const('MyParser', Class) }
122
+
123
+ it 'should use a the default parser if none is set as parameter' do
124
+ allow(ActsAsTaggableOn.default_parser).to receive(:new).and_return(parser)
125
+ ActsAsTaggableOn::TagList.new('cool, wicked', parse: true)
126
+
127
+ expect(parser).to have_received(:parse)
128
+ end
129
+
130
+ it 'should use the custom parser passed as parameter' do
131
+ allow(parser_class).to receive(:new).and_return(parser)
117
132
 
133
+ ActsAsTaggableOn::TagList.new('cool, wicked', parser: parser_class)
134
+
135
+ expect(parser).to have_received(:parse)
136
+ end
137
+
138
+ it 'should use the parser setted as attribute' do
139
+ allow(parser_class).to receive(:new).with('new, tag').and_return(parser)
140
+
141
+ tag_list = ActsAsTaggableOn::TagList.new('example')
142
+ tag_list.parser = parser_class
143
+ tag_list.add('new, tag', parse: true)
144
+
145
+ expect(parser).to have_received(:parse)
146
+ end
118
147
  end
119
148
 
120
149
 
@@ -286,4 +286,24 @@ describe ActsAsTaggableOn::Tag do
286
286
  end
287
287
  end
288
288
  end
289
+
290
+ describe 'popular tags' do
291
+ before do
292
+ %w(sports rails linux tennis golden_syrup).each_with_index do |t, i|
293
+ tag = ActsAsTaggableOn::Tag.new(name: t)
294
+ tag.taggings_count = i
295
+ tag.save!
296
+ end
297
+ end
298
+
299
+ it 'should find the most popular tags' do
300
+ expect(ActsAsTaggableOn::Tag.most_used(3).first.name).to eq("golden_syrup")
301
+ expect(ActsAsTaggableOn::Tag.most_used(3).length).to eq(3)
302
+ end
303
+
304
+ it 'should find the least popular tags' do
305
+ expect(ActsAsTaggableOn::Tag.least_used(3).first.name).to eq("sports")
306
+ expect(ActsAsTaggableOn::Tag.least_used(3).length).to eq(3)
307
+ end
308
+ end
289
309
  end
@@ -137,4 +137,17 @@ describe 'Tagger' do
137
137
  }).to_not change(ActsAsTaggableOn::Tagging, :count)
138
138
  end
139
139
 
140
+ it 'should change tags order in ordered taggable' do
141
+ @ordered_taggable = OrderedTaggableModel.create name: 'hey!'
142
+
143
+ @user.tag @ordered_taggable, with: 'tag, tag1', on: :tags
144
+ expect(@ordered_taggable.reload.tags_from(@user)).to eq(['tag', 'tag1'])
145
+
146
+ @user.tag @ordered_taggable, with: 'tag2, tag1', on: :tags
147
+ expect(@ordered_taggable.reload.tags_from(@user)).to eq(['tag2', 'tag1'])
148
+
149
+ @user.tag @ordered_taggable, with: 'tag1, tag2', on: :tags
150
+ expect(@ordered_taggable.reload.tags_from(@user)).to eq(['tag1', 'tag2'])
151
+ end
152
+
140
153
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts-as-taggable-on
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
4
+ version: 3.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-07-08 00:00:00.000000000 Z
12
+ date: 2014-09-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -17,7 +17,7 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '3'
20
+ version: '3.2'
21
21
  - - "<"
22
22
  - !ruby/object:Gem::Version
23
23
  version: '5'
@@ -27,7 +27,7 @@ dependencies:
27
27
  requirements:
28
28
  - - ">="
29
29
  - !ruby/object:Gem::Version
30
- version: '3'
30
+ version: '3.2'
31
31
  - - "<"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '5'
@@ -77,16 +77,16 @@ dependencies:
77
77
  name: rspec-rails
78
78
  requirement: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: 3.0.0.beta
82
+ version: '0'
83
83
  type: :development
84
84
  prerelease: false
85
85
  version_requirements: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: 3.0.0.beta
89
+ version: '0'
90
90
  - !ruby/object:Gem::Dependency
91
91
  name: rspec-its
92
92
  requirement: !ruby/object:Gem::Requirement
@@ -172,11 +172,14 @@ files:
172
172
  - gemfiles/activerecord_3.2.gemfile
173
173
  - gemfiles/activerecord_4.0.gemfile
174
174
  - gemfiles/activerecord_4.1.gemfile
175
+ - gemfiles/activerecord_4.2.gemfile
175
176
  - gemfiles/activerecord_edge.gemfile
176
177
  - lib/acts-as-taggable-on.rb
177
178
  - lib/acts_as_taggable_on.rb
178
179
  - lib/acts_as_taggable_on/compatibility.rb
180
+ - lib/acts_as_taggable_on/default_parser.rb
179
181
  - lib/acts_as_taggable_on/engine.rb
182
+ - lib/acts_as_taggable_on/generic_parser.rb
180
183
  - lib/acts_as_taggable_on/tag.rb
181
184
  - lib/acts_as_taggable_on/tag_list.rb
182
185
  - lib/acts_as_taggable_on/tag_list_parser.rb
@@ -195,6 +198,8 @@ files:
195
198
  - spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb
196
199
  - spec/acts_as_taggable_on/acts_as_tagger_spec.rb
197
200
  - spec/acts_as_taggable_on/caching_spec.rb
201
+ - spec/acts_as_taggable_on/default_parser_spec.rb
202
+ - spec/acts_as_taggable_on/generic_parser_spec.rb
198
203
  - spec/acts_as_taggable_on/related_spec.rb
199
204
  - spec/acts_as_taggable_on/single_table_inheritance_spec.rb
200
205
  - spec/acts_as_taggable_on/tag_list_parser_spec.rb
@@ -261,7 +266,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
261
266
  version: '0'
262
267
  requirements: []
263
268
  rubyforge_project:
264
- rubygems_version: 2.2.2
269
+ rubygems_version: 2.4.1
265
270
  signing_key:
266
271
  specification_version: 4
267
272
  summary: Advanced tagging for Rails.
@@ -269,6 +274,8 @@ test_files:
269
274
  - spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb
270
275
  - spec/acts_as_taggable_on/acts_as_tagger_spec.rb
271
276
  - spec/acts_as_taggable_on/caching_spec.rb
277
+ - spec/acts_as_taggable_on/default_parser_spec.rb
278
+ - spec/acts_as_taggable_on/generic_parser_spec.rb
272
279
  - spec/acts_as_taggable_on/related_spec.rb
273
280
  - spec/acts_as_taggable_on/single_table_inheritance_spec.rb
274
281
  - spec/acts_as_taggable_on/tag_list_parser_spec.rb