acts-as-taggable-on 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5c20928991764b079db87d3d1ba32846d0a3e01f
4
- data.tar.gz: 94cae20632e6d61d21cd55921cb7ee284cb6f766
3
+ metadata.gz: 1d8fbbfcf90748ab7a565ca2d911de6cff93a5d6
4
+ data.tar.gz: bcdadfcc7559b5d1e1cbe41214493366a700b6e6
5
5
  SHA512:
6
- metadata.gz: 29cfdac7542aed8bb4f8f55a0c7d33f592739ae1f887a5399e0bd34fd686a5824b3e5335f5037a0ada5928001b4a8b3ca124d21771d5af648fc6f55735bf31da
7
- data.tar.gz: 535aa2433b00889491d6aaf7f488b372ebc1ac2e60f8d9cd2337ae07a19ce2ce1bce3438a1f2db48c77b52a3a57c56648c8c31ddf1b4f0c2447bebae27d67fe0
6
+ metadata.gz: 8af6b4c5ca019197c14bf67980889f0639f61fd7d53333316b6202d9c00bbbac3533bb77a6b055706c13f24102ed8d30064675dd4609f932a3413a54dbd7e674
7
+ data.tar.gz: 103300024612ce7fd41600b58158aa05edee3c672cec7c611bdab5bdacb7d98b59aa78e7c0418e753b990c9be6962da56a5b4acf999dbbf5047f9d2aadc26ab2
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
@@ -4,18 +4,95 @@ 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
16
14
 
15
+ ### [3.4.0 / 2014-08-29](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.3.0...v3.4.0)
17
16
 
18
- ### [3.2.0 / 2014-05-1](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.1.1...v3.2.0)
17
+ * Features
18
+ * [@ProGM Support for custom parsers for tags](https://github.com/mbleigh/acts-as-taggable-on/pull/579)
19
+ * [@damzcodes #577 Popular feature](https://github.com/mbleigh/acts-as-taggable-on/pull/577)
20
+ * Fixes
21
+ * [@twalpole Update for rails edge (4.2)](https://github.com/mbleigh/acts-as-taggable-on/pull/583)
22
+ * Performance
23
+ * [@dontfidget #587 Use pluck instead of select](https://github.com/mbleigh/acts-as-taggable-on/pull/587)
24
+
25
+ ### [3.3.0 / 2014-07-08](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.6...v3.3.0)
26
+
27
+ * Features
28
+ * [@felipeclopes #488 Support for `start_at` and `end_at` restrictions when selecting tags](https://github.com/mbleigh/acts-as-taggable-on/pull/488)
29
+
30
+ * Fixes
31
+ * [@tonytonyjan #560 Fix for `ActsAsTaggableOn.remove_unused_tags` doesn't work](https://github.com/mbleigh/acts-as-taggable-on/pull/560)
32
+ * [@ThearkInn #555 Fix for `tag_cloud` helper to generate correct css tags](https://github.com/mbleigh/acts-as-taggable-on/pull/555)
33
+
34
+ * Performance
35
+ * [@pcai #556 Add back taggables index in the taggins table](https://github.com/mbleigh/acts-as-taggable-on/pull/556)
36
+
37
+
38
+ ### [3.2.6 / 2014-05-28](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.5...v3.2.6)
39
+
40
+ * Fixes
41
+ * [@seuros #548 Fix dirty marking when tags are not ordered](https://github.com/mbleigh/acts-as-taggable-on/issues/548)
42
+
43
+ * Misc
44
+ * [@seuros Remove actionpack dependency](https://github.com/mbleigh/acts-as-taggable-on/commit/5d20e0486c892fbe21af42fdcd79d0b6ebe87ed4)
45
+ * [@seuros #547 Add tests for update_attributes](https://github.com/mbleigh/acts-as-taggable-on/issues/547)
46
+
47
+
48
+ ### [3.2.5 / 2014-05-25](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.4...v3.2.5)
49
+
50
+ * Fixes
51
+ * [@seuros #546 Fix autoload bug. Now require engine file instead of autoloading it](https://github.com/mbleigh/acts-as-taggable-on/issues/546)
52
+
53
+
54
+ ### [3.2.4 / 2014-05-24](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.3...v3.2.4)
55
+
56
+ * Fixes
57
+ * [@seuros #544 Fix incorrect query generation related to `GROUP BY` SQL statement](https://github.com/mbleigh/acts-as-taggable-on/issues/544)
58
+
59
+ * Misc
60
+ * [@seuros #545 Remove `ammeter` development dependency](https://github.com/mbleigh/acts-as-taggable-on/pull/545)
61
+ * [@seuros #545 Deprecate `TagList.from` in favor of `TagListParser.parse`](https://github.com/mbleigh/acts-as-taggable-on/pull/545)
62
+ * [@seuros #543 Introduce lazy loading](https://github.com/mbleigh/acts-as-taggable-on/pull/543)
63
+ * [@seuros #541 Deprecate ActsAsTaggableOn::Utils](https://github.com/mbleigh/acts-as-taggable-on/pull/541)
64
+
65
+
66
+ ### [3.2.3 / 2014-05-16](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.2...v3.2.3)
67
+
68
+ * Fixes
69
+ * [@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)
70
+ * [@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)
71
+
72
+
73
+ ### [3.2.2 / 2014-05-07](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.1...v3.2.2)
74
+
75
+ * Breaking Changes
76
+ * [@seuros #526 Taggable models are not extended with ActsAsTaggableOn::Utils anymore](https://github.com/mbleigh/acts-as-taggable-on/pull/526)
77
+
78
+ * Fixes
79
+ * [@seuros #536 Add explicit conversion of tags to strings (when assigning tags)](https://github.com/mbleigh/acts-as-taggable-on/pull/536)
80
+
81
+ * Misc
82
+ * [@seuros #526 Delete outdated benchmark script](https://github.com/mbleigh/acts-as-taggable-on/pull/526)
83
+ * [@seuros #525 Fix tests so that they pass with MySQL](https://github.com/mbleigh/acts-as-taggable-on/pull/525)
84
+
85
+
86
+ ### [3.2.1 / 2014-05-06](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.2.0...v3.2.1)
87
+
88
+ * Misc
89
+ * [@seuros #523 Run tests loading only ActiveRecord (without the full Rails stack)](https://github.com/mbleigh/acts-as-taggable-on/pull/523)
90
+ * [@seuros #523 Remove activesupport dependency](https://github.com/mbleigh/acts-as-taggable-on/pull/523)
91
+ * [@seuros #523 Introduce database_cleaner in specs](https://github.com/mbleigh/acts-as-taggable-on/pull/523)
92
+ * [@seuros #520 Tag_list cleanup](https://github.com/mbleigh/acts-as-taggable-on/pull/520)
93
+
94
+
95
+ ### [3.2.0 / 2014-05-01](https://github.com/mbleigh/acts-as-taggable-on/compare/v3.1.1...v3.2.0)
19
96
 
20
97
  * Breaking Changes
21
98
  * 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)
@@ -1,4 +1,4 @@
1
1
  module ActsAsTaggableOn
2
- VERSION = '3.3.0'
2
+ VERSION = '3.4.0'
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
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.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-07-08 00:00:00.000000000 Z
12
+ date: 2014-08-29 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