acts-as-taggable-on 2.0.0.rc2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +3 -1
- data/VERSION +1 -1
- data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +16 -4
- data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +10 -9
- data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +18 -1
- data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +8 -8
- data/lib/acts_as_taggable_on/acts_as_tagger.rb +23 -3
- data/lib/acts_as_taggable_on/tag_list.rb +31 -31
- metadata +5 -8
data/README.rdoc
CHANGED
@@ -71,7 +71,9 @@ rake spec:plugins
|
|
71
71
|
= Usage
|
72
72
|
|
73
73
|
class User < ActiveRecord::Base
|
74
|
-
acts_as_taggable_on :tags
|
74
|
+
# Alias for <tt>acts_as_taggable_on :tags</tt>:
|
75
|
+
acts_as_taggable
|
76
|
+
acts_as_taggable_on :skills, :interests
|
75
77
|
end
|
76
78
|
|
77
79
|
@user = User.new(:name => "Bobby")
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0.0
|
1
|
+
2.0.0
|
@@ -4,20 +4,32 @@ module ActsAsTaggableOn
|
|
4
4
|
false
|
5
5
|
end
|
6
6
|
|
7
|
+
##
|
8
|
+
# This is an alias for calling <tt>acts_as_taggable_on :tags</tt>.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
# class Book < ActiveRecord::Base
|
12
|
+
# acts_as_taggable
|
13
|
+
# end
|
7
14
|
def acts_as_taggable
|
8
15
|
acts_as_taggable_on :tags
|
9
16
|
end
|
10
17
|
|
18
|
+
##
|
19
|
+
# Make a model taggable on specified contexts.
|
20
|
+
#
|
21
|
+
# @param [Array] tag_types An array of taggable contexts
|
22
|
+
#
|
23
|
+
# Example:
|
24
|
+
# class User < ActiveRecord::Base
|
25
|
+
# acts_as_taggable_on :languages, :skills
|
26
|
+
# end
|
11
27
|
def acts_as_taggable_on(*tag_types)
|
12
28
|
tag_types = tag_types.to_a.flatten.compact.map(&:to_sym)
|
13
29
|
|
14
30
|
if taggable?
|
15
31
|
write_inheritable_attribute(:tag_types, (self.tag_types + tag_types).uniq)
|
16
32
|
else
|
17
|
-
if ::ActiveRecord::VERSION::MAJOR < 3
|
18
|
-
include ActsAsTaggableOn::ActiveRecord::Backports
|
19
|
-
end
|
20
|
-
|
21
33
|
write_inheritable_attribute(:tag_types, tag_types)
|
22
34
|
class_inheritable_reader(:tag_types)
|
23
35
|
|
@@ -38,17 +38,18 @@ module ActsAsTaggableOn::Taggable
|
|
38
38
|
all_tag_counts(options.merge({:on => context.to_s}))
|
39
39
|
end
|
40
40
|
|
41
|
+
##
|
41
42
|
# Calculate the tag counts for all tags.
|
42
43
|
#
|
43
|
-
# Options:
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
44
|
+
# @param [Hash] options Options:
|
45
|
+
# * :start_at - Restrict the tags to those created after a certain time
|
46
|
+
# * :end_at - Restrict the tags to those created before a certain time
|
47
|
+
# * :conditions - A piece of SQL conditions to add to the query
|
48
|
+
# * :limit - The maximum number of tags to return
|
49
|
+
# * :order - A piece of SQL to order by. Eg 'tags.count desc' or 'taggings.created_at desc'
|
50
|
+
# * :at_least - Exclude tags with a frequency less than the given value
|
51
|
+
# * :at_most - Exclude tags with a frequency greater than the given value
|
52
|
+
# * :on - Scope the find to only include a certain context
|
52
53
|
def all_tag_counts(options = {})
|
53
54
|
options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :on, :id
|
54
55
|
|
@@ -51,6 +51,20 @@ module ActsAsTaggableOn::Taggable
|
|
51
51
|
object.column_names.map { |column| "#{object.table_name}.#{column}" }.join(", ")
|
52
52
|
end
|
53
53
|
|
54
|
+
##
|
55
|
+
# Return a scope of objects that are tagged with the specified tags.
|
56
|
+
#
|
57
|
+
# @param tags The tags that we want to query for
|
58
|
+
# @param [Hash] options A hash of options to alter you query:
|
59
|
+
# * <tt>:exclude</tt> - if set to true, return objects that are *NOT* tagged with the specified tags
|
60
|
+
# * <tt>:any</tt> - if set to true, return objects that are tagged with *ANY* of the specified tags
|
61
|
+
# * <tt>:match_all</tt> - if set to true, return objects that are *ONLY* tagged with the specified tags
|
62
|
+
#
|
63
|
+
# Example:
|
64
|
+
# User.tagged_with("awesome", "cool") # Users that are tagged with awesome and cool
|
65
|
+
# User.tagged_with("awesome", "cool", :exclude => true) # Users that are not tagged with awesome or cool
|
66
|
+
# User.tagged_with("awesome", "cool", :any => true) # Users that are tagged with awesome or cool
|
67
|
+
# User.tagged_with("awesome", "cool", :match_all => true) # Users that are tagged with just awesome and cool
|
54
68
|
def tagged_with(tags, options = {})
|
55
69
|
tag_list = TagList.from(tags)
|
56
70
|
|
@@ -100,7 +114,10 @@ module ActsAsTaggableOn::Taggable
|
|
100
114
|
end
|
101
115
|
|
102
116
|
|
103
|
-
|
117
|
+
scoped(:joins => joins.join(" "),
|
118
|
+
:group => group,
|
119
|
+
:conditions => conditions.join(" AND "),
|
120
|
+
:readonly => false)
|
104
121
|
end
|
105
122
|
|
106
123
|
def is_taggable?
|
@@ -42,10 +42,10 @@ module ActsAsTaggableOn::Taggable
|
|
42
42
|
exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
|
43
43
|
|
44
44
|
klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
:from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
|
46
|
+
:conditions => ["#{exclude_self} #{klass.table_name}.id = #{Tagging.table_name}.taggable_id AND #{Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND #{Tag.table_name}.name IN (?) AND #{Tagging.table_name}.context = ?", tags_to_find, result_context],
|
47
|
+
:group => grouped_column_names_for(klass),
|
48
|
+
:order => "count DESC" }.update(options))
|
49
49
|
end
|
50
50
|
|
51
51
|
def related_tags_for(context, klass, options = {})
|
@@ -54,10 +54,10 @@ module ActsAsTaggableOn::Taggable
|
|
54
54
|
exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
|
55
55
|
|
56
56
|
klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
57
|
+
:from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
|
58
|
+
:conditions => ["#{exclude_self} #{klass.table_name}.id = #{Tagging.table_name}.taggable_id AND #{Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND #{Tag.table_name}.name IN (?)", tags_to_find],
|
59
|
+
:group => grouped_column_names_for(klass),
|
60
|
+
:order => "count DESC" }.update(options))
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
@@ -5,10 +5,20 @@ module ActsAsTaggableOn
|
|
5
5
|
end
|
6
6
|
|
7
7
|
module ClassMethods
|
8
|
+
##
|
9
|
+
# Make a model a tagger. This allows an instance of a model to claim ownership
|
10
|
+
# of tags.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# class User < ActiveRecord::Base
|
14
|
+
# acts_as_tagger
|
15
|
+
# end
|
8
16
|
def acts_as_tagger(opts={})
|
9
|
-
|
10
|
-
|
11
|
-
|
17
|
+
class_eval do
|
18
|
+
has_many :owned_taggings, opts.merge(:as => :tagger, :dependent => :destroy,
|
19
|
+
:include => :tag, :class_name => "Tagging")
|
20
|
+
has_many :owned_tags, :through => :owned_taggings, :source => :tag, :uniq => true
|
21
|
+
end
|
12
22
|
|
13
23
|
include ActsAsTaggableOn::Tagger::InstanceMethods
|
14
24
|
extend ActsAsTaggableOn::Tagger::SingletonMethods
|
@@ -20,6 +30,16 @@ module ActsAsTaggableOn
|
|
20
30
|
end
|
21
31
|
|
22
32
|
module InstanceMethods
|
33
|
+
##
|
34
|
+
# Tag a taggable model with tags that are owned by the tagger.
|
35
|
+
#
|
36
|
+
# @param taggable The object that will be tagged
|
37
|
+
# @param [Hash] options An hash with options. Available options are:
|
38
|
+
# * <tt>:with</tt> - The tags that you want to
|
39
|
+
# * <tt>:on</tt> - The context on which you want to tag
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
# @user.tag(@photo, :with => "paris, normandy", :on => :locations)
|
23
43
|
def tag(taggable, opts={})
|
24
44
|
opts.reverse_merge!(:force => true)
|
25
45
|
|
@@ -1,21 +1,40 @@
|
|
1
1
|
class TagList < Array
|
2
2
|
|
3
3
|
cattr_accessor :delimiter
|
4
|
-
|
5
4
|
self.delimiter = ','
|
6
5
|
|
6
|
+
attr_accessor :owner
|
7
|
+
|
7
8
|
def initialize(*args)
|
8
9
|
add(*args)
|
9
10
|
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Returns a new TagList using the given tag string.
|
14
|
+
#
|
15
|
+
# Example:
|
16
|
+
# tag_list = TagList.from("One , Two, Three")
|
17
|
+
# tag_list # ["One", "Two", "Three"]
|
18
|
+
def self.from(string)
|
19
|
+
string = string.join(", ") if string.respond_to?(:join)
|
10
20
|
|
11
|
-
|
21
|
+
new.tap do |tag_list|
|
22
|
+
string = string.to_s.dup
|
23
|
+
|
24
|
+
# Parse the quoted tags
|
25
|
+
string.gsub!(/(\A|#{delimiter})\s*"(.*?)"\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
|
26
|
+
string.gsub!(/(\A|#{delimiter})\s*'(.*?)'\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
|
12
27
|
|
28
|
+
tag_list.add(string.split(delimiter))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
13
33
|
# Add tags to the tag_list. Duplicate or blank tags will be ignored.
|
14
|
-
#
|
15
|
-
# tag_list.add("Fun", "Happy")
|
16
|
-
#
|
17
34
|
# Use the <tt>:parse</tt> option to add an unparsed tag string.
|
18
35
|
#
|
36
|
+
# Example:
|
37
|
+
# tag_list.add("Fun", "Happy")
|
19
38
|
# tag_list.add("Fun, Happy", :parse => true)
|
20
39
|
def add(*names)
|
21
40
|
extract_and_apply_options!(names)
|
@@ -24,12 +43,12 @@ class TagList < Array
|
|
24
43
|
self
|
25
44
|
end
|
26
45
|
|
46
|
+
##
|
27
47
|
# Remove specific tags from the tag_list.
|
48
|
+
# Use the <tt>:parse</tt> option to add an unparsed tag string.
|
28
49
|
#
|
50
|
+
# Example:
|
29
51
|
# tag_list.remove("Sad", "Lonely")
|
30
|
-
#
|
31
|
-
# Like #add, the <tt>:parse</tt> option can be used to remove multiple tags in a string.
|
32
|
-
#
|
33
52
|
# tag_list.remove("Sad, Lonely", :parse => true)
|
34
53
|
def remove(*names)
|
35
54
|
extract_and_apply_options!(names)
|
@@ -37,9 +56,11 @@ class TagList < Array
|
|
37
56
|
self
|
38
57
|
end
|
39
58
|
|
59
|
+
##
|
40
60
|
# Transform the tag_list into a tag string suitable for edting in a form.
|
41
61
|
# The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
|
42
62
|
#
|
63
|
+
# Example:
|
43
64
|
# tag_list = TagList.new("Round", "Square,Cube")
|
44
65
|
# tag_list.to_s # 'Round, "Square,Cube"'
|
45
66
|
def to_s
|
@@ -51,7 +72,8 @@ class TagList < Array
|
|
51
72
|
end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
|
52
73
|
end
|
53
74
|
|
54
|
-
|
75
|
+
private
|
76
|
+
|
55
77
|
# Remove whitespace, duplicates, and blanks.
|
56
78
|
def clean!
|
57
79
|
reject!(&:blank?)
|
@@ -70,26 +92,4 @@ class TagList < Array
|
|
70
92
|
args.flatten!
|
71
93
|
end
|
72
94
|
|
73
|
-
class << self
|
74
|
-
|
75
|
-
# Returns a new TagList using the given tag string.
|
76
|
-
#
|
77
|
-
# tag_list = TagList.from("One , Two, Three")
|
78
|
-
# tag_list # ["One", "Two", "Three"]
|
79
|
-
def from(string)
|
80
|
-
string = string.join(", ") if string.respond_to?(:join)
|
81
|
-
|
82
|
-
new.tap do |tag_list|
|
83
|
-
string = string.to_s.dup
|
84
|
-
|
85
|
-
# Parse the quoted tags
|
86
|
-
string.gsub!(/(\A|#{delimiter})\s*"(.*?)"\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
|
87
|
-
string.gsub!(/(\A|#{delimiter})\s*'(.*?)'\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
|
88
|
-
|
89
|
-
tag_list.add(string.split(delimiter))
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
95
|
end
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts-as-taggable-on
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
4
|
+
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 2
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
|
10
|
-
version: 2.0.0.rc2
|
9
|
+
version: 2.0.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Michael Bleigh
|
@@ -84,13 +83,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
83
|
version: "0"
|
85
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
85
|
requirements:
|
87
|
-
- - "
|
86
|
+
- - ">="
|
88
87
|
- !ruby/object:Gem::Version
|
89
88
|
segments:
|
90
|
-
-
|
91
|
-
|
92
|
-
- 1
|
93
|
-
version: 1.3.1
|
89
|
+
- 0
|
90
|
+
version: "0"
|
94
91
|
requirements: []
|
95
92
|
|
96
93
|
rubyforge_project:
|