acts-as-taggable-on 3.2.3 → 3.2.4

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -7
  3. data/Gemfile +0 -1
  4. data/acts-as-taggable-on.gemspec +6 -7
  5. data/gemfiles/activerecord_3.2.gemfile +0 -1
  6. data/gemfiles/activerecord_4.0.gemfile +0 -1
  7. data/gemfiles/activerecord_4.1.gemfile +0 -1
  8. data/gemfiles/activerecord_edge.gemfile +0 -1
  9. data/lib/acts-as-taggable-on.rb +26 -20
  10. data/lib/acts_as_taggable_on/{acts_as_taggable_on/compatibility.rb → compatibility.rb} +0 -0
  11. data/lib/acts_as_taggable_on/tag_list.rb +13 -77
  12. data/lib/acts_as_taggable_on/tag_list_parser.rb +78 -0
  13. data/lib/acts_as_taggable_on/taggable.rb +7 -6
  14. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/cache.rb +0 -0
  15. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/collection.rb +0 -0
  16. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/core.rb +14 -10
  17. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/dirty.rb +0 -0
  18. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/ownership.rb +1 -1
  19. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/related.rb +0 -0
  20. data/lib/acts_as_taggable_on/utils.rb +2 -26
  21. data/lib/acts_as_taggable_on/version.rb +1 -1
  22. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +1 -1
  23. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +1 -0
  24. data/spec/acts_as_taggable_on/caching_spec.rb +1 -0
  25. data/spec/acts_as_taggable_on/related_spec.rb +1 -0
  26. data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +1 -0
  27. data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +46 -0
  28. data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -40
  29. data/spec/acts_as_taggable_on/tag_spec.rb +29 -35
  30. data/spec/acts_as_taggable_on/taggable_spec.rb +55 -55
  31. data/spec/acts_as_taggable_on/tagger_spec.rb +1 -0
  32. data/spec/acts_as_taggable_on/tagging_spec.rb +2 -1
  33. data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -0
  34. data/spec/acts_as_taggable_on/utils_spec.rb +1 -0
  35. data/spec/internal/app/models/cached_model_with_array.rb +1 -1
  36. data/spec/internal/app/models/models.rb +2 -2
  37. data/spec/internal/db/schema.rb +2 -2
  38. data/spec/spec_helper.rb +0 -1
  39. data/spec/support/0-helpers.rb +32 -0
  40. metadata +36 -47
  41. data/spec/schema.rb +0 -82
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 755e4d829ddc31ccd6ffb8385594ed8c5c64971f
4
- data.tar.gz: 1ce12972b7d9d436484d619843a7bdf316a5c3b7
3
+ metadata.gz: 545d0523ad32bdf17c9ee4f78222e04311842930
4
+ data.tar.gz: 530c8f90ff6526185bddcbd43e88782d23cfbed4
5
5
  SHA512:
6
- metadata.gz: c8f129a07903598bcb765b260bd14db168b4a6fbf6989cef816157ae6350cadd733f75ca6cc0169e00fa8c19b59710198b868f52044f07efca451c44aa9a046c
7
- data.tar.gz: 578b3d36b654b9343a36df3a7086b40b3ef32f6b43973d92c0f31251ea095405b692d2ef131ba74086366d117eef0fcd2334b51ce8f7ce3c0266481819a0a2a1
6
+ metadata.gz: b88db309cfee2467aa6aa30b0b1bb62d459ebb822c0c20a9a2901f46a6501f38c0728dd9156ac37e262a55d312b3dbf6deca9c8a919ae8737d3e78e4cce9a006
7
+ data.tar.gz: eb9ec9f85fdd8f91136b5a848075fd6e310b278345878cf3aef02293d948ed29b98d3268235aacb216ba181759080ef635b1ac1443005d44acc8472aaa28482e
data/.travis.yml CHANGED
@@ -25,18 +25,19 @@ matrix:
25
25
  allow_failures:
26
26
  - gemfile: gemfiles/activerecord_edge.gemfile
27
27
  - rvm: rbx-2
28
+ - rvm: ruby-head
28
29
  exclude:
29
30
  - rvm: 1.9.3
30
- gemfile: ci/Gemfile.activerecord-4.0.x
31
+ gemfile: gemfiles/activerecord_4.0.gemfile
31
32
  - rvm: 1.9.3
32
- gemfile: ci/Gemfile.activerecord-4.1.x
33
+ gemfile: gemfiles/activerecord_4.1.gemfile
33
34
  - rvm: 1.9.3
34
- gemfile: ci/Gemfile.activerecord-edge
35
+ gemfile: gemfiles/activerecord_edge.gemfile
35
36
  - rvm: rbx-2
36
- gemfile: ci/Gemfile.activerecord-3.2.x
37
+ gemfile: gemfiles/activerecord_3.2.gemfile
37
38
  - rvm: ruby-head
38
- gemfile: ci/Gemfile.activerecord-3.2.x
39
+ gemfile: gemfiles/activerecord_3.2.gemfile
39
40
  - rvm: ruby-head
40
- gemfile: ci/Gemfile.activerecord-4.0.x
41
+ gemfile: gemfiles/activerecord_4.0.gemfile
41
42
  - rvm: ruby-head
42
- gemfile: ci/Gemfile.activerecord-4.1.x
43
+ gemfile: gemfiles/activerecord_4.1.gemfile
data/Gemfile CHANGED
@@ -7,5 +7,4 @@ group :local_development do
7
7
  gem 'guard-rspec'
8
8
  gem 'appraisal'
9
9
  gem 'rake'
10
- gem 'byebug'
11
10
  end
@@ -26,13 +26,12 @@ Gem::Specification.new do |gem|
26
26
  gem.add_runtime_dependency 'actionpack', ['>= 3', '< 5']
27
27
 
28
28
  gem.add_development_dependency 'sqlite3'
29
- gem.add_development_dependency 'mysql2'
29
+ gem.add_development_dependency 'mysql2', '~> 0.3.7'
30
30
  gem.add_development_dependency 'pg'
31
31
 
32
- gem.add_development_dependency 'rspec-rails' , '~> 3.0.0.beta1'
33
- gem.add_development_dependency 'rspec-its', '~> 1.0'
34
- gem.add_development_dependency 'rspec', '3.0.0.beta2'
35
- gem.add_development_dependency 'ammeter', '~> 1.0'
36
- gem.add_development_dependency 'barrier', '~> 1.0'
37
- gem.add_development_dependency 'database_cleaner', '~> 1.2'
32
+ gem.add_development_dependency 'rspec-rails' , '~> 3.0.0.beta'
33
+ gem.add_development_dependency 'rspec-its'
34
+ gem.add_development_dependency 'rspec'
35
+ gem.add_development_dependency 'barrier'
36
+ gem.add_development_dependency 'database_cleaner'
38
37
  end
@@ -10,7 +10,6 @@ group :local_development do
10
10
  gem "guard-rspec"
11
11
  gem "appraisal"
12
12
  gem "rake"
13
- gem "byebug"
14
13
  end
15
14
 
16
15
  gemspec :path => "../"
@@ -10,7 +10,6 @@ group :local_development do
10
10
  gem "guard-rspec"
11
11
  gem "appraisal"
12
12
  gem "rake"
13
- gem "byebug"
14
13
  end
15
14
 
16
15
  gemspec :path => "../"
@@ -10,7 +10,6 @@ group :local_development do
10
10
  gem "guard-rspec"
11
11
  gem "appraisal"
12
12
  gem "rake"
13
- gem "byebug"
14
13
  end
15
14
 
16
15
  gemspec :path => "../"
@@ -11,7 +11,6 @@ group :local_development do
11
11
  gem "guard-rspec"
12
12
  gem "appraisal"
13
13
  gem "rake"
14
- gem "byebug"
15
14
  end
16
15
 
17
16
  gemspec :path => "../"
@@ -6,6 +6,30 @@ require 'action_view'
6
6
  require 'digest/sha1'
7
7
 
8
8
  module ActsAsTaggableOn
9
+ extend ActiveSupport::Autoload
10
+
11
+ autoload :Engine
12
+ autoload :Tag
13
+ autoload :TagList
14
+ autoload :TagListParser
15
+ autoload :Taggable
16
+ autoload :Tagger
17
+ autoload :Tagging
18
+ autoload :TagsHelper
19
+
20
+ autoload_under 'taggable' do
21
+ autoload :Cache
22
+ autoload :Collection
23
+ autoload :Core
24
+ autoload :Dirty
25
+ autoload :Ownership
26
+ autoload :Related
27
+ end
28
+
29
+ autoload :Utils
30
+ autoload :Compatibility
31
+
32
+
9
33
  class DuplicateTagError < StandardError
10
34
  end
11
35
 
@@ -16,7 +40,7 @@ module ActsAsTaggableOn
16
40
 
17
41
  def self.method_missing(method_name, *args, &block)
18
42
  @configuration.respond_to?(method_name) ?
19
- @configuration.send(method_name, *args, &block) : super
43
+ @configuration.send(method_name, *args, &block) : super
20
44
  end
21
45
 
22
46
  def self.respond_to?(method_name, include_private=false)
@@ -31,7 +55,7 @@ module ActsAsTaggableOn
31
55
 
32
56
  class Configuration
33
57
  attr_accessor :delimiter, :force_lowercase, :force_parameterize,
34
- :strict_case_match, :remove_unused_tags
58
+ :strict_case_match, :remove_unused_tags
35
59
 
36
60
  def initialize
37
61
  @delimiter = ','
@@ -45,24 +69,6 @@ module ActsAsTaggableOn
45
69
  setup
46
70
  end
47
71
 
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
72
  ActiveSupport.on_load(:active_record) do
67
73
  extend ActsAsTaggableOn::Compatibility
68
74
  extend ActsAsTaggableOn::Taggable
@@ -1,3 +1,4 @@
1
+
1
2
  require 'active_support/core_ext/module/delegation'
2
3
 
3
4
  module ActsAsTaggableOn
@@ -8,82 +9,6 @@ module ActsAsTaggableOn
8
9
  add(*args)
9
10
  end
10
11
 
11
- class << self
12
- ##
13
- # Returns a new TagList using the given tag string.
14
- #
15
- # Example:
16
- # tag_list = ActsAsTaggableOn::TagList.from("One , Two, Three")
17
- # tag_list # ["One", "Two", "Three"]
18
- def from(string)
19
- string = string.join(ActsAsTaggableOn.glue) if string.respond_to?(:join)
20
-
21
- new.tap do |tag_list|
22
- string = string.to_s.dup
23
-
24
-
25
- string.gsub!(double_quote_pattern) {
26
- # Append the matched tag to the tag list
27
- tag_list << Regexp.last_match[2]
28
- # Return the matched delimiter ($3) to replace the matched items
29
- ''
30
- }
31
-
32
- string.gsub!(single_quote_pattern) {
33
- # Append the matched tag ($2) to the tag list
34
- tag_list << Regexp.last_match[2]
35
- # Return an empty string to replace the matched items
36
- ''
37
- }
38
-
39
- # split the string by the delimiter
40
- # and add to the tag_list
41
- tag_list.add(string.split(Regexp.new delimiter))
42
- end
43
- end
44
-
45
- def delimiter
46
- # Parse the quoted tags
47
- d = ActsAsTaggableOn.delimiter
48
- # Separate multiple delimiters by bitwise operator
49
- d = d.join('|') if d.kind_of?(Array)
50
-
51
- d
52
- end
53
-
54
- def single_quote_pattern
55
- %r{
56
- ( # Tag start delimiter ($1)
57
- \A | # Either string start or
58
- #{delimiter} # a delimiter
59
- )
60
- \s*' # quote (') optionally preceded by whitespace
61
- (.*?) # Tag ($2)
62
- '\s* # quote (') optionally followed by whitespace
63
- (?= # Tag end delimiter (not consumed; is zero-length lookahead)
64
- #{delimiter}\s* | # Either a delimiter optionally followed by whitespace or
65
- \z # string end
66
- )
67
- }x
68
- end
69
-
70
- def double_quote_pattern
71
- %r{
72
- ( # Tag start delimiter ($1)
73
- \A | # Either string start or
74
- #{delimiter} # a delimiter
75
- )
76
- \s*" # quote (") optionally preceded by whitespace
77
- (.*?) # Tag ($2)
78
- "\s* # quote (") optionally followed by whitespace
79
- (?= # Tag end delimiter (not consumed; is zero-length lookahead)
80
- #{delimiter}\s* | # Either a delimiter optionally followed by whitespace or
81
- \z # string end
82
- )
83
- }x
84
- end
85
-
86
- end
87
12
  ##
88
13
  # Add tags to the tag_list. Duplicate or blank tags will be ignored.
89
14
  # Use the <tt>:parse</tt> option to add an unparsed tag string.
@@ -165,12 +90,23 @@ module ActsAsTaggableOn
165
90
  options = args.last.is_a?(Hash) ? args.pop : {}
166
91
  options.assert_valid_keys :parse
167
92
 
168
- args.map! { |a| self.class.from(a) } if options[:parse]
93
+ args.map! { |a| TagListParser.parse(a) } if options[:parse]
169
94
 
170
95
  args.flatten!
171
96
  end
172
97
 
173
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
+
174
110
  end
175
111
  end
176
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
@@ -1,5 +1,6 @@
1
1
  module ActsAsTaggableOn
2
2
  module Taggable
3
+
3
4
  def taggable?
4
5
  false
5
6
  end
@@ -90,12 +91,12 @@ module ActsAsTaggableOn
90
91
 
91
92
  # each of these add context-specific methods and must be
92
93
  # called on each call of taggable_on
93
- include ActsAsTaggableOn::Taggable::Core
94
- include ActsAsTaggableOn::Taggable::Collection
95
- include ActsAsTaggableOn::Taggable::Cache
96
- include ActsAsTaggableOn::Taggable::Ownership
97
- include ActsAsTaggableOn::Taggable::Related
98
- include ActsAsTaggableOn::Taggable::Dirty
94
+ include Core
95
+ include Collection
96
+ include Cache
97
+ include Ownership
98
+ include Related
99
+ include Dirty
99
100
  end
100
101
  end
101
102
  end
@@ -82,7 +82,7 @@ module ActsAsTaggableOn::Taggable
82
82
  # User.tagged_with("awesome", "cool", :match_all => true) # Users that are tagged with just awesome and cool
83
83
  # User.tagged_with("awesome", "cool", :owned_by => foo ) # Users that are tagged with just awesome and cool by 'foo'
84
84
  def tagged_with(tags, options = {})
85
- tag_list = ActsAsTaggableOn::TagList.from(tags)
85
+ tag_list = ActsAsTaggableOn::TagListParser.parse(tags)
86
86
  options = options.dup
87
87
  empty_result = where('1 = 0')
88
88
 
@@ -116,7 +116,7 @@ module ActsAsTaggableOn::Taggable
116
116
  " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = #{quote_value(owned_by.class.base_class.to_s, nil)}"
117
117
  end
118
118
 
119
- elsif options.delete(:any)
119
+ elsif any = options.delete(:any)
120
120
  # get tags, drop out if nothing returned (we need at least one)
121
121
  tags = if options.delete(:wild)
122
122
  ActsAsTaggableOn::Tag.named_like_any(tag_list)
@@ -141,7 +141,7 @@ module ActsAsTaggableOn::Taggable
141
141
 
142
142
  # don't need to sanitize sql, map all ids and join with OR logic
143
143
  conditions << tags.map { |t| "#{taggings_alias}.tag_id = #{quote_value(t.id, nil)}" }.join(' OR ')
144
- select_clause = " #{table_name}.*" unless context and tag_types.one?
144
+ select_clause << " #{table_name}.*" unless context and tag_types.one?
145
145
 
146
146
  if owned_by
147
147
  tagging_join << ' AND ' +
@@ -153,7 +153,10 @@ module ActsAsTaggableOn::Taggable
153
153
  end
154
154
 
155
155
  joins << tagging_join
156
- group = "#{table_name}.#{primary_key}"
156
+ unless any == 'distinct' # Fix issue #544
157
+ group = "#{table_name}.#{primary_key}"
158
+ select_clause << group
159
+ end
157
160
  else
158
161
  tags = ActsAsTaggableOn::Tag.named_any(tag_list)
159
162
 
@@ -183,7 +186,7 @@ module ActsAsTaggableOn::Taggable
183
186
 
184
187
  group ||= [] # Rails interprets this as a no-op in the group() call below
185
188
  if options.delete(:order_by_matching_tag_count)
186
- select_clause = "#{table_name}.*, COUNT(#{taggings_alias}.tag_id) AS #{taggings_alias}_count"
189
+ select_clause << "#{table_name}.*, COUNT(#{taggings_alias}.tag_id) AS #{taggings_alias}_count"
187
190
  group_columns = ActsAsTaggableOn::Utils.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
188
191
  group = group_columns
189
192
  order_by << "#{taggings_alias}_count DESC"
@@ -203,8 +206,9 @@ module ActsAsTaggableOn::Taggable
203
206
 
204
207
  order_by << options[:order] if options[:order].present?
205
208
 
206
- select(select_clause)
207
- .joins(joins.join(' '))
209
+ query = self
210
+ query = self.select(select_clause.join(',')) unless select_clause.empty?
211
+ query.joins(joins.join(' '))
208
212
  .where(conditions.join(' AND '))
209
213
  .group(group)
210
214
  .having(having)
@@ -259,7 +263,7 @@ module ActsAsTaggableOn::Taggable
259
263
  if instance_variable_get(variable_name)
260
264
  instance_variable_get(variable_name)
261
265
  elsif cached_tag_list_on(context) && self.class.caching_tag_list_on?(context)
262
- instance_variable_set(variable_name, ActsAsTaggableOn::TagList.from(cached_tag_list_on(context)))
266
+ instance_variable_set(variable_name, ActsAsTaggableOn::TagListParser.parse(cached_tag_list_on(context)))
263
267
  else
264
268
  instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(tags_on(context).map(&:name)))
265
269
  end
@@ -309,7 +313,7 @@ module ActsAsTaggableOn::Taggable
309
313
  variable_name = "@#{context.to_s.singularize}_list"
310
314
  process_dirty_object(context, new_list) unless custom_contexts.include?(context.to_s)
311
315
 
312
- instance_variable_set(variable_name, ActsAsTaggableOn::TagList.from(new_list))
316
+ instance_variable_set(variable_name, ActsAsTaggableOn::TagListParser.parse(new_list))
313
317
  end
314
318
 
315
319
  def tagging_contexts
@@ -383,7 +387,7 @@ module ActsAsTaggableOn::Taggable
383
387
 
384
388
  # Destroy old taggings:
385
389
  if old_tags.present?
386
- self.taggings.not_owned.by_context(context).destroy_all(tag_id: old_tags)
390
+ taggings.not_owned.by_context(context).destroy_all(tag_id: old_tags)
387
391
  end
388
392
 
389
393
  # Create new taggings: