acts-as-taggable-on 3.2.3 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +8 -7
- data/Appraisals +0 -4
- data/Gemfile +0 -1
- data/README.md +1 -1
- data/acts-as-taggable-on.gemspec +6 -8
- data/db/migrate/4_add_missing_taggable_index.rb +9 -0
- data/gemfiles/activerecord_3.2.gemfile +0 -2
- data/gemfiles/activerecord_4.0.gemfile +0 -2
- data/gemfiles/activerecord_4.1.gemfile +0 -2
- data/gemfiles/activerecord_edge.gemfile +0 -2
- data/lib/acts-as-taggable-on.rb +28 -20
- data/lib/acts_as_taggable_on/tag_list.rb +13 -77
- data/lib/acts_as_taggable_on/tag_list_parser.rb +78 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/core.rb +46 -23
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/ownership.rb +1 -1
- data/lib/acts_as_taggable_on/taggable.rb +7 -6
- data/lib/acts_as_taggable_on/tagging.rb +1 -1
- data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
- data/lib/acts_as_taggable_on/utils.rb +2 -26
- data/lib/acts_as_taggable_on/version.rb +1 -1
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +1 -1
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +1 -0
- data/spec/acts_as_taggable_on/caching_spec.rb +1 -0
- data/spec/acts_as_taggable_on/related_spec.rb +1 -0
- data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +1 -0
- data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +46 -0
- data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -40
- data/spec/acts_as_taggable_on/tag_spec.rb +29 -35
- data/spec/acts_as_taggable_on/taggable/dirty_spec.rb +127 -0
- data/spec/acts_as_taggable_on/taggable_spec.rb +76 -163
- data/spec/acts_as_taggable_on/tagger_spec.rb +1 -1
- data/spec/acts_as_taggable_on/tagging_spec.rb +13 -1
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -0
- data/spec/acts_as_taggable_on/utils_spec.rb +1 -0
- data/spec/internal/app/models/cached_model_with_array.rb +1 -1
- data/spec/internal/app/models/models.rb +2 -2
- data/spec/internal/db/schema.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/support/0-helpers.rb +32 -0
- metadata +39 -67
- data/spec/schema.rb +0 -82
- /data/lib/acts_as_taggable_on/{acts_as_taggable_on/compatibility.rb → compatibility.rb} +0 -0
- /data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/cache.rb +0 -0
- /data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/collection.rb +0 -0
- /data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/dirty.rb +0 -0
- /data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/related.rb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5c20928991764b079db87d3d1ba32846d0a3e01f
|
|
4
|
+
data.tar.gz: 94cae20632e6d61d21cd55921cb7ee284cb6f766
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 29cfdac7542aed8bb4f8f55a0c7d33f592739ae1f887a5399e0bd34fd686a5824b3e5335f5037a0ada5928001b4a8b3ca124d21771d5af648fc6f55735bf31da
|
|
7
|
+
data.tar.gz: 535aa2433b00889491d6aaf7f488b372ebc1ac2e60f8d9cd2337ae07a19ce2ce1bce3438a1f2db48c77b52a3a57c56648c8c31ddf1b4f0c2447bebae27d67fe0
|
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:
|
|
31
|
+
gemfile: gemfiles/activerecord_4.0.gemfile
|
|
31
32
|
- rvm: 1.9.3
|
|
32
|
-
gemfile:
|
|
33
|
+
gemfile: gemfiles/activerecord_4.1.gemfile
|
|
33
34
|
- rvm: 1.9.3
|
|
34
|
-
gemfile:
|
|
35
|
+
gemfile: gemfiles/activerecord_edge.gemfile
|
|
35
36
|
- rvm: rbx-2
|
|
36
|
-
gemfile:
|
|
37
|
+
gemfile: gemfiles/activerecord_3.2.gemfile
|
|
37
38
|
- rvm: ruby-head
|
|
38
|
-
gemfile:
|
|
39
|
+
gemfile: gemfiles/activerecord_3.2.gemfile
|
|
39
40
|
- rvm: ruby-head
|
|
40
|
-
gemfile:
|
|
41
|
+
gemfile: gemfiles/activerecord_4.0.gemfile
|
|
41
42
|
- rvm: ruby-head
|
|
42
|
-
gemfile:
|
|
43
|
+
gemfile: gemfiles/activerecord_4.1.gemfile
|
data/Appraisals
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
appraise "activerecord-3.2" do
|
|
2
2
|
gem "activerecord", "~> 3.2"
|
|
3
|
-
gem "actionpack", "~> 3.2"
|
|
4
3
|
end
|
|
5
4
|
|
|
6
5
|
appraise "activerecord-4.0" do
|
|
7
6
|
gem "activerecord", "~> 4.0"
|
|
8
|
-
gem "actionpack", "~> 4.0"
|
|
9
7
|
end
|
|
10
8
|
|
|
11
9
|
appraise "activerecord-4.1" do
|
|
12
10
|
gem "activerecord", "~> 4.1"
|
|
13
|
-
gem "actionpack", "~> 4.1"
|
|
14
11
|
end
|
|
15
12
|
|
|
16
13
|
appraise "activerecord-edge" do
|
|
17
14
|
gem "activerecord", github: "rails/rails"
|
|
18
|
-
gem "actionpack", github: "rails/rails"
|
|
19
15
|
gem 'arel', github: 'rails/arel'
|
|
20
16
|
end
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# ActsAsTaggableOn
|
|
2
2
|
[](http://travis-ci.org/mbleigh/acts-as-taggable-on)
|
|
3
3
|
[](https://codeclimate.com/github/mbleigh/acts-as-taggable-on)
|
|
4
|
-
[](http://inch-ci.org/github/mbleigh/acts-as-taggable-on)
|
|
5
5
|
|
|
6
6
|
This plugin was originally based on Acts as Taggable on Steroids by Jonathan Viney.
|
|
7
7
|
It has evolved substantially since that point, but all credit goes to him for the
|
data/acts-as-taggable-on.gemspec
CHANGED
|
@@ -23,16 +23,14 @@ Gem::Specification.new do |gem|
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
gem.add_runtime_dependency 'activerecord', ['>= 3', '< 5']
|
|
26
|
-
gem.add_runtime_dependency 'actionpack', ['>= 3', '< 5']
|
|
27
26
|
|
|
28
27
|
gem.add_development_dependency 'sqlite3'
|
|
29
|
-
gem.add_development_dependency 'mysql2'
|
|
28
|
+
gem.add_development_dependency 'mysql2', '~> 0.3.7'
|
|
30
29
|
gem.add_development_dependency 'pg'
|
|
31
30
|
|
|
32
|
-
gem.add_development_dependency 'rspec-rails' , '~> 3.0.0.
|
|
33
|
-
gem.add_development_dependency 'rspec-its'
|
|
34
|
-
gem.add_development_dependency 'rspec'
|
|
35
|
-
gem.add_development_dependency '
|
|
36
|
-
gem.add_development_dependency '
|
|
37
|
-
gem.add_development_dependency 'database_cleaner', '~> 1.2'
|
|
31
|
+
gem.add_development_dependency 'rspec-rails' , '~> 3.0.0.beta'
|
|
32
|
+
gem.add_development_dependency 'rspec-its'
|
|
33
|
+
gem.add_development_dependency 'rspec'
|
|
34
|
+
gem.add_development_dependency 'barrier'
|
|
35
|
+
gem.add_development_dependency 'database_cleaner'
|
|
38
36
|
end
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
source "https://rubygems.org"
|
|
4
4
|
|
|
5
5
|
gem "activerecord", :github => "rails/rails"
|
|
6
|
-
gem "actionpack", :github => "rails/rails"
|
|
7
6
|
gem "arel", :github => "rails/arel"
|
|
8
7
|
|
|
9
8
|
group :local_development do
|
|
@@ -11,7 +10,6 @@ group :local_development do
|
|
|
11
10
|
gem "guard-rspec"
|
|
12
11
|
gem "appraisal"
|
|
13
12
|
gem "rake"
|
|
14
|
-
gem "byebug"
|
|
15
13
|
end
|
|
16
14
|
|
|
17
15
|
gemspec :path => "../"
|
data/lib/acts-as-taggable-on.rb
CHANGED
|
@@ -3,9 +3,35 @@ require 'active_record/version'
|
|
|
3
3
|
require 'active_support/core_ext/module'
|
|
4
4
|
require 'action_view'
|
|
5
5
|
|
|
6
|
+
require_relative 'acts_as_taggable_on/engine' if defined?(Rails)
|
|
7
|
+
|
|
6
8
|
require 'digest/sha1'
|
|
7
9
|
|
|
8
10
|
module ActsAsTaggableOn
|
|
11
|
+
extend ActiveSupport::Autoload
|
|
12
|
+
|
|
13
|
+
autoload :Tag
|
|
14
|
+
autoload :TagList
|
|
15
|
+
autoload :TagListParser
|
|
16
|
+
autoload :Taggable
|
|
17
|
+
autoload :Tagger
|
|
18
|
+
autoload :Tagging
|
|
19
|
+
autoload :TagsHelper
|
|
20
|
+
autoload :VERSION
|
|
21
|
+
|
|
22
|
+
autoload_under 'taggable' do
|
|
23
|
+
autoload :Cache
|
|
24
|
+
autoload :Collection
|
|
25
|
+
autoload :Core
|
|
26
|
+
autoload :Dirty
|
|
27
|
+
autoload :Ownership
|
|
28
|
+
autoload :Related
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
autoload :Utils
|
|
32
|
+
autoload :Compatibility
|
|
33
|
+
|
|
34
|
+
|
|
9
35
|
class DuplicateTagError < StandardError
|
|
10
36
|
end
|
|
11
37
|
|
|
@@ -16,7 +42,7 @@ module ActsAsTaggableOn
|
|
|
16
42
|
|
|
17
43
|
def self.method_missing(method_name, *args, &block)
|
|
18
44
|
@configuration.respond_to?(method_name) ?
|
|
19
|
-
|
|
45
|
+
@configuration.send(method_name, *args, &block) : super
|
|
20
46
|
end
|
|
21
47
|
|
|
22
48
|
def self.respond_to?(method_name, include_private=false)
|
|
@@ -31,7 +57,7 @@ module ActsAsTaggableOn
|
|
|
31
57
|
|
|
32
58
|
class Configuration
|
|
33
59
|
attr_accessor :delimiter, :force_lowercase, :force_parameterize,
|
|
34
|
-
|
|
60
|
+
:strict_case_match, :remove_unused_tags
|
|
35
61
|
|
|
36
62
|
def initialize
|
|
37
63
|
@delimiter = ','
|
|
@@ -45,24 +71,6 @@ module ActsAsTaggableOn
|
|
|
45
71
|
setup
|
|
46
72
|
end
|
|
47
73
|
|
|
48
|
-
require 'acts_as_taggable_on/utils'
|
|
49
|
-
|
|
50
|
-
require 'acts_as_taggable_on/taggable'
|
|
51
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/compatibility'
|
|
52
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/core'
|
|
53
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/collection'
|
|
54
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/cache'
|
|
55
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/ownership'
|
|
56
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/related'
|
|
57
|
-
require 'acts_as_taggable_on/acts_as_taggable_on/dirty'
|
|
58
|
-
|
|
59
|
-
require 'acts_as_taggable_on/tagger'
|
|
60
|
-
require 'acts_as_taggable_on/tag'
|
|
61
|
-
require 'acts_as_taggable_on/tag_list'
|
|
62
|
-
require 'acts_as_taggable_on/tags_helper'
|
|
63
|
-
require 'acts_as_taggable_on/tagging'
|
|
64
|
-
require 'acts_as_taggable_on/engine'
|
|
65
|
-
|
|
66
74
|
ActiveSupport.on_load(:active_record) do
|
|
67
75
|
extend ActsAsTaggableOn::Compatibility
|
|
68
76
|
extend ActsAsTaggableOn::Taggable
|
|
@@ -1,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|
|
|
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
|
|
@@ -73,6 +73,8 @@ module ActsAsTaggableOn::Taggable
|
|
|
73
73
|
# * <tt>:order_by_matching_tag_count</tt> - if set to true and used with :any, sort by objects matching the most tags, descending
|
|
74
74
|
# * <tt>:match_all</tt> - if set to true, return objects that are *ONLY* tagged with the specified tags
|
|
75
75
|
# * <tt>:owned_by</tt> - return objects that are *ONLY* owned by the owner
|
|
76
|
+
# * <tt>:start_at</tt> - Restrict the tags to those created after a certain time
|
|
77
|
+
# * <tt>:end_at</tt> - Restrict the tags to those created before a certain time
|
|
76
78
|
#
|
|
77
79
|
# Example:
|
|
78
80
|
# User.tagged_with("awesome", "cool") # Users that are tagged with awesome and cool
|
|
@@ -81,8 +83,9 @@ module ActsAsTaggableOn::Taggable
|
|
|
81
83
|
# User.tagged_with("awesome", "cool", :any => true, :order_by_matching_tag_count => true) # Sort by users who match the most tags, descending
|
|
82
84
|
# User.tagged_with("awesome", "cool", :match_all => true) # Users that are tagged with just awesome and cool
|
|
83
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
|
|
84
87
|
def tagged_with(tags, options = {})
|
|
85
|
-
tag_list = ActsAsTaggableOn::
|
|
88
|
+
tag_list = ActsAsTaggableOn::TagListParser.parse(tags)
|
|
86
89
|
options = options.dup
|
|
87
90
|
empty_result = where('1 = 0')
|
|
88
91
|
|
|
@@ -109,14 +112,17 @@ module ActsAsTaggableOn::Taggable
|
|
|
109
112
|
conditions << "#{table_name}.#{primary_key} NOT IN (SELECT #{ActsAsTaggableOn::Tagging.table_name}.taggable_id FROM #{ActsAsTaggableOn::Tagging.table_name} JOIN #{ActsAsTaggableOn::Tag.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key} AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)})"
|
|
110
113
|
|
|
111
114
|
if owned_by
|
|
112
|
-
joins <<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
joins << "JOIN #{ActsAsTaggableOn::Tagging.table_name}" +
|
|
116
|
+
" ON #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
|
|
117
|
+
" AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)}" +
|
|
118
|
+
" AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = #{quote_value(owned_by.id, nil)}" +
|
|
119
|
+
" AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = #{quote_value(owned_by.class.base_class.to_s, nil)}"
|
|
120
|
+
|
|
121
|
+
joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
|
|
122
|
+
joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
|
|
117
123
|
end
|
|
118
124
|
|
|
119
|
-
elsif options.delete(:any)
|
|
125
|
+
elsif any = options.delete(:any)
|
|
120
126
|
# get tags, drop out if nothing returned (we need at least one)
|
|
121
127
|
tags = if options.delete(:wild)
|
|
122
128
|
ActsAsTaggableOn::Tag.named_like_any(tag_list)
|
|
@@ -134,14 +140,18 @@ module ActsAsTaggableOn::Taggable
|
|
|
134
140
|
"#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tags.map(&:name).join('_'))}"
|
|
135
141
|
)
|
|
136
142
|
|
|
137
|
-
tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}"
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
143
|
+
tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
|
|
144
|
+
" ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
|
|
145
|
+
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
|
|
146
|
+
|
|
147
|
+
tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
|
|
148
|
+
tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
|
|
149
|
+
|
|
150
|
+
tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
|
|
141
151
|
|
|
142
152
|
# don't need to sanitize sql, map all ids and join with OR logic
|
|
143
153
|
conditions << tags.map { |t| "#{taggings_alias}.tag_id = #{quote_value(t.id, nil)}" }.join(' OR ')
|
|
144
|
-
select_clause
|
|
154
|
+
select_clause << " #{table_name}.*" unless context and tag_types.one?
|
|
145
155
|
|
|
146
156
|
if owned_by
|
|
147
157
|
tagging_join << ' AND ' +
|
|
@@ -153,7 +163,10 @@ module ActsAsTaggableOn::Taggable
|
|
|
153
163
|
end
|
|
154
164
|
|
|
155
165
|
joins << tagging_join
|
|
156
|
-
|
|
166
|
+
unless any == 'distinct' # Fix issue #544
|
|
167
|
+
group = "#{table_name}.#{primary_key}"
|
|
168
|
+
select_clause << group
|
|
169
|
+
end
|
|
157
170
|
else
|
|
158
171
|
tags = ActsAsTaggableOn::Tag.named_any(tag_list)
|
|
159
172
|
|
|
@@ -166,7 +179,10 @@ module ActsAsTaggableOn::Taggable
|
|
|
166
179
|
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}" +
|
|
167
180
|
" AND #{taggings_alias}.tag_id = #{quote_value(tag.id, nil)}"
|
|
168
181
|
|
|
169
|
-
tagging_join <<
|
|
182
|
+
tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
|
|
183
|
+
tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
|
|
184
|
+
|
|
185
|
+
tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
|
|
170
186
|
|
|
171
187
|
if owned_by
|
|
172
188
|
tagging_join << ' AND ' +
|
|
@@ -183,7 +199,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
183
199
|
|
|
184
200
|
group ||= [] # Rails interprets this as a no-op in the group() call below
|
|
185
201
|
if options.delete(:order_by_matching_tag_count)
|
|
186
|
-
select_clause
|
|
202
|
+
select_clause << "#{table_name}.*, COUNT(#{taggings_alias}.tag_id) AS #{taggings_alias}_count"
|
|
187
203
|
group_columns = ActsAsTaggableOn::Utils.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
|
|
188
204
|
group = group_columns
|
|
189
205
|
order_by << "#{taggings_alias}_count DESC"
|
|
@@ -194,7 +210,9 @@ module ActsAsTaggableOn::Taggable
|
|
|
194
210
|
" ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" \
|
|
195
211
|
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
|
|
196
212
|
|
|
197
|
-
joins <<
|
|
213
|
+
joins << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
|
|
214
|
+
joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
|
|
215
|
+
joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
|
|
198
216
|
|
|
199
217
|
group_columns = ActsAsTaggableOn::Utils.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
|
|
200
218
|
group = group_columns
|
|
@@ -203,8 +221,9 @@ module ActsAsTaggableOn::Taggable
|
|
|
203
221
|
|
|
204
222
|
order_by << options[:order] if options[:order].present?
|
|
205
223
|
|
|
206
|
-
|
|
207
|
-
.
|
|
224
|
+
query = self
|
|
225
|
+
query = self.select(select_clause.join(',')) unless select_clause.empty?
|
|
226
|
+
query.joins(joins.join(' '))
|
|
208
227
|
.where(conditions.join(' AND '))
|
|
209
228
|
.group(group)
|
|
210
229
|
.having(having)
|
|
@@ -259,7 +278,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
259
278
|
if instance_variable_get(variable_name)
|
|
260
279
|
instance_variable_get(variable_name)
|
|
261
280
|
elsif cached_tag_list_on(context) && self.class.caching_tag_list_on?(context)
|
|
262
|
-
instance_variable_set(variable_name, ActsAsTaggableOn::
|
|
281
|
+
instance_variable_set(variable_name, ActsAsTaggableOn::TagListParser.parse(cached_tag_list_on(context)))
|
|
263
282
|
else
|
|
264
283
|
instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(tags_on(context).map(&:name)))
|
|
265
284
|
end
|
|
@@ -309,7 +328,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
309
328
|
variable_name = "@#{context.to_s.singularize}_list"
|
|
310
329
|
process_dirty_object(context, new_list) unless custom_contexts.include?(context.to_s)
|
|
311
330
|
|
|
312
|
-
instance_variable_set(variable_name, ActsAsTaggableOn::
|
|
331
|
+
instance_variable_set(variable_name, ActsAsTaggableOn::TagListParser.parse(new_list))
|
|
313
332
|
end
|
|
314
333
|
|
|
315
334
|
def tagging_contexts
|
|
@@ -325,8 +344,12 @@ module ActsAsTaggableOn::Taggable
|
|
|
325
344
|
old = changed_attributes[attrib]
|
|
326
345
|
changed_attributes.delete(attrib) if old.to_s == value.to_s
|
|
327
346
|
else
|
|
328
|
-
old = tag_list_on(context)
|
|
329
|
-
|
|
347
|
+
old = tag_list_on(context)
|
|
348
|
+
if self.class.preserve_tag_order
|
|
349
|
+
changed_attributes[attrib] = old if old.to_s != value.to_s
|
|
350
|
+
else
|
|
351
|
+
changed_attributes[attrib] = old.to_s if old.sort != ActsAsTaggableOn::TagListParser.parse(value).sort
|
|
352
|
+
end
|
|
330
353
|
end
|
|
331
354
|
end
|
|
332
355
|
|
|
@@ -383,7 +406,7 @@ module ActsAsTaggableOn::Taggable
|
|
|
383
406
|
|
|
384
407
|
# Destroy old taggings:
|
|
385
408
|
if old_tags.present?
|
|
386
|
-
|
|
409
|
+
taggings.not_owned.by_context(context).destroy_all(tag_id: old_tags)
|
|
387
410
|
end
|
|
388
411
|
|
|
389
412
|
# Create new taggings:
|
|
@@ -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
|
|
94
|
-
include
|
|
95
|
-
include
|
|
96
|
-
include
|
|
97
|
-
include
|
|
98
|
-
include
|
|
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
|