acts-as-taggable-on 2.0.0.rc2 → 2.0.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.
- 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:
|