acts-as-taggable-on 3.2.3 → 3.2.4

Sign up to get free protection for your applications and to get access to all the features.
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: