acts-as-taggable-on 2.0.4 → 2.0.5
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/Gemfile +5 -3
- data/README.rdoc +11 -11
- data/VERSION +1 -1
- data/lib/acts-as-taggable-on.rb +1 -1
- data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +2 -2
- data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +3 -3
- data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +11 -11
- data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +21 -21
- data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +12 -12
- data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +6 -6
- data/lib/acts_as_taggable_on/acts_as_tagger.rb +2 -2
- data/lib/acts_as_taggable_on/tag.rb +46 -44
- data/lib/acts_as_taggable_on/tag_list.rb +79 -78
- data/lib/acts_as_taggable_on/tagging.rb +19 -18
- data/lib/acts_as_taggable_on/tags_helper.rb +12 -12
- data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +2 -1
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +2 -2
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +3 -3
- data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -3
- data/spec/acts_as_taggable_on/tag_spec.rb +21 -21
- data/spec/acts_as_taggable_on/taggable_spec.rb +11 -6
- data/spec/acts_as_taggable_on/tagger_spec.rb +5 -5
- data/spec/acts_as_taggable_on/tagging_spec.rb +7 -7
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +3 -3
- data/spec/spec_helper.rb +22 -18
- metadata +3 -4
- data/spec/spec.opts +0 -2
data/Gemfile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
source :gemcutter
|
2
2
|
|
3
3
|
# Rails 3.0
|
4
|
-
gem 'rails', '3.0.0.
|
5
|
-
gem 'rspec', '2.0.0.beta.
|
6
|
-
gem 'sqlite3-ruby',
|
4
|
+
gem 'rails', '3.0.0.beta3'
|
5
|
+
gem 'rspec', '2.0.0.beta.8'
|
6
|
+
gem 'sqlite3-ruby', :require => 'sqlite3'
|
7
|
+
gem 'jeweler'
|
8
|
+
gem 'rcov'
|
data/README.rdoc
CHANGED
@@ -38,7 +38,7 @@ After that, you can run "rake gems:install" to install the gem if you don't alre
|
|
38
38
|
1. script/generate acts_as_taggable_on_migration
|
39
39
|
2. rake db:migrate
|
40
40
|
|
41
|
-
|
41
|
+
=== Rails 3.0
|
42
42
|
|
43
43
|
Acts As Taggable On is now useable in Rails 3.0, thanks to the excellent work of Szymon Nowak
|
44
44
|
and Jelle Vandebeeck.
|
@@ -47,12 +47,12 @@ To use it, add it to your Gemfile:
|
|
47
47
|
|
48
48
|
gem 'acts-as-taggable-on'
|
49
49
|
|
50
|
-
|
50
|
+
==== Post Installation
|
51
51
|
|
52
52
|
1. rails generate acts_as_taggable_on:migration
|
53
53
|
2. rake db:migrate
|
54
54
|
|
55
|
-
|
55
|
+
== Testing
|
56
56
|
|
57
57
|
Acts As Taggable On uses RSpec for its test coverage. Inside the plugin
|
58
58
|
directory, you can run the specs for RoR 3.0.0 with:
|
@@ -68,7 +68,7 @@ If you already have RSpec on your application, the specs will run while using:
|
|
68
68
|
rake spec:plugins
|
69
69
|
|
70
70
|
|
71
|
-
|
71
|
+
== Usage
|
72
72
|
|
73
73
|
class User < ActiveRecord::Base
|
74
74
|
# Alias for <tt>acts_as_taggable_on :tags</tt>:
|
@@ -89,7 +89,7 @@ rake spec:plugins
|
|
89
89
|
User.skill_counts # => [<Tag name="joking" count=2>,<Tag name="clowning" count=1>...]
|
90
90
|
@frankie.skill_counts
|
91
91
|
|
92
|
-
|
92
|
+
=== Finding Tagged Objects
|
93
93
|
|
94
94
|
Acts As Taggable On utilizes named_scopes to create an association for tags.
|
95
95
|
This way you can mix and match to filter down your results, and it also improves
|
@@ -109,7 +109,7 @@ compatibility with the will_paginate gem:
|
|
109
109
|
# Find a user with any of the tags:
|
110
110
|
User.tagged_with(["awesome", "cool"], :any => true)
|
111
111
|
|
112
|
-
|
112
|
+
=== Relationships
|
113
113
|
|
114
114
|
You can find objects of the same type based on similar tags on certain contexts.
|
115
115
|
Also, objects will be returned in descending order based on the total number of
|
@@ -128,7 +128,7 @@ matched tags.
|
|
128
128
|
@bobby.find_related_skills # => [<User name="Tom">]
|
129
129
|
@frankie.find_related_skills # => [<User name="Tom">]
|
130
130
|
|
131
|
-
|
131
|
+
=== Dynamic Tag Contexts
|
132
132
|
|
133
133
|
In addition to the generated tag contexts in the definition, it is also possible
|
134
134
|
to allow for dynamic tag contexts (this could be user generated tag contexts!)
|
@@ -141,7 +141,7 @@ to allow for dynamic tag contexts (this could be user generated tag contexts!)
|
|
141
141
|
@user.tag_counts_on(:customs)
|
142
142
|
User.find_tagged_with("same", :on => :customs) # => [@user]
|
143
143
|
|
144
|
-
|
144
|
+
=== Tag Ownership
|
145
145
|
|
146
146
|
Tags can have owners:
|
147
147
|
|
@@ -158,7 +158,7 @@ Tags can have owners:
|
|
158
158
|
@some_user.owned_tags
|
159
159
|
@some_photo.locations_from(@some_user)
|
160
160
|
|
161
|
-
|
161
|
+
=== Tag cloud calculations
|
162
162
|
|
163
163
|
To construct tag clouds, the frequency of each tag needs to be calculated.
|
164
164
|
Because we specified +acts_as_taggable_on+ on the <tt>User</tt> class, we can
|
@@ -198,7 +198,7 @@ CSS:
|
|
198
198
|
.css3 { font-size: 1.4em; }
|
199
199
|
.css4 { font-size: 1.6em; }
|
200
200
|
|
201
|
-
|
201
|
+
== Contributors
|
202
202
|
|
203
203
|
* TomEric (i76) - Maintainer
|
204
204
|
* Michael Bleigh - Original Author
|
@@ -208,7 +208,7 @@ CSS:
|
|
208
208
|
* Pradeep Elankumaran - Taggers
|
209
209
|
* Sinclair Bain - Patch King
|
210
210
|
|
211
|
-
|
211
|
+
=== Patch Contributors
|
212
212
|
|
213
213
|
* tristanzdunn - Related objects of other classes
|
214
214
|
* azabaj - Fixed migrate down
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0.
|
1
|
+
2.0.5
|
data/lib/acts-as-taggable-on.rb
CHANGED
@@ -34,8 +34,8 @@ module ActsAsTaggableOn
|
|
34
34
|
class_inheritable_reader(:tag_types)
|
35
35
|
|
36
36
|
class_eval do
|
37
|
-
has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
|
38
|
-
has_many :base_tags, :
|
37
|
+
has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "ActsAsTaggableOn::Tagging"
|
38
|
+
has_many :base_tags, :through => :taggings, :source => :tag, :class_name => "ActsAsTaggableOn::Tag"
|
39
39
|
|
40
40
|
def self.taggable?
|
41
41
|
true
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module ActsAsTaggableOn::Taggable
|
2
2
|
module Cache
|
3
3
|
def self.included(base)
|
4
|
-
# Skip adding caching capabilities if no cache columns exist
|
5
|
-
return unless base.tag_types.any? { |context| base.column_names.include?("cached_#{context.to_s.singularize}_list") }
|
4
|
+
# Skip adding caching capabilities if table not exists or no cache columns exist
|
5
|
+
return unless base.table_exists? && base.tag_types.any? { |context| base.column_names.include?("cached_#{context.to_s.singularize}_list") }
|
6
6
|
|
7
7
|
base.send :include, ActsAsTaggableOn::Taggable::Cache::InstanceMethods
|
8
8
|
base.extend ActsAsTaggableOn::Taggable::Cache::ClassMethods
|
@@ -50,4 +50,4 @@ module ActsAsTaggableOn::Taggable
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
53
|
-
end
|
53
|
+
end
|
@@ -62,11 +62,11 @@ module ActsAsTaggableOn::Taggable
|
|
62
62
|
## Generate conditions:
|
63
63
|
options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
|
64
64
|
|
65
|
-
start_at_conditions = sanitize_sql(["#{Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
|
66
|
-
end_at_conditions = sanitize_sql(["#{Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
|
65
|
+
start_at_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
|
66
|
+
end_at_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
|
67
67
|
|
68
|
-
taggable_conditions = sanitize_sql(["#{Tagging.table_name}.taggable_type = ?", base_class.name])
|
69
|
-
taggable_conditions << sanitize_sql([" AND #{Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
|
68
|
+
taggable_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.taggable_type = ?", base_class.name])
|
69
|
+
taggable_conditions << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
|
70
70
|
|
71
71
|
conditions = [
|
72
72
|
taggable_conditions,
|
@@ -77,10 +77,10 @@ module ActsAsTaggableOn::Taggable
|
|
77
77
|
].compact.reverse
|
78
78
|
|
79
79
|
## Generate joins:
|
80
|
-
tagging_join = "LEFT OUTER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"
|
81
|
-
tagging_join << sanitize_sql([" AND #{Tagging.table_name}.context = ?", options.delete(:on).to_s]) if options[:on]
|
80
|
+
tagging_join = "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} ON #{ActsAsTaggableOn::Tag.table_name}.id = #{ActsAsTaggableOn::Tagging.table_name}.tag_id"
|
81
|
+
tagging_join << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", options.delete(:on).to_s]) if options[:on]
|
82
82
|
|
83
|
-
taggable_join = "INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
|
83
|
+
taggable_join = "INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id"
|
84
84
|
taggable_join << " AND #{table_name}.#{inheritance_column} = '#{name}'" unless descends_from_active_record? # Current model is STI descendant, so add type checking to the join condition
|
85
85
|
|
86
86
|
joins = [
|
@@ -91,7 +91,7 @@ module ActsAsTaggableOn::Taggable
|
|
91
91
|
|
92
92
|
|
93
93
|
## Generate scope:
|
94
|
-
scope = Tag.scoped(:select => "#{Tag.table_name}.*, COUNT(*) AS count").order(options[:order]).limit(options[:limit])
|
94
|
+
scope = ActsAsTaggableOn::Tag.scoped(:select => "#{ActsAsTaggableOn::Tag.table_name}.*, COUNT(*) AS count").order(options[:order]).limit(options[:limit])
|
95
95
|
|
96
96
|
# Joins and conditions
|
97
97
|
joins.each { |join| scope = scope.joins(join) }
|
@@ -105,14 +105,14 @@ module ActsAsTaggableOn::Taggable
|
|
105
105
|
if ActiveRecord::VERSION::MAJOR >= 3
|
106
106
|
# Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore:
|
107
107
|
scoped_select = "#{table_name}.#{primary_key}"
|
108
|
-
scope = scope.where("#{Tagging.table_name}.taggable_id IN(#{select(scoped_select).to_sql})")
|
108
|
+
scope = scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{select(scoped_select).to_sql})")
|
109
109
|
|
110
110
|
# We have having() in RoR 3.0 so use it:
|
111
111
|
having = having.blank? ? "COUNT(*) > 0" : "COUNT(*) > 0 AND #{having}"
|
112
|
-
scope = scope.group(grouped_column_names_for(Tag)).having(having)
|
112
|
+
scope = scope.group(grouped_column_names_for(ActsAsTaggableOn::Tag)).having(having)
|
113
113
|
else
|
114
114
|
# Having is not available in 2.3.x:
|
115
|
-
group_by = "#{grouped_column_names_for(Tag)} HAVING COUNT(*) > 0"
|
115
|
+
group_by = "#{grouped_column_names_for(ActsAsTaggableOn::Tag)} HAVING COUNT(*) > 0"
|
116
116
|
group_by << " AND #{having}" unless having.blank?
|
117
117
|
scope = scope.group(group_by)
|
118
118
|
end
|
@@ -20,9 +20,9 @@ module ActsAsTaggableOn::Taggable
|
|
20
20
|
context_tags = tags_type.to_sym
|
21
21
|
|
22
22
|
class_eval do
|
23
|
-
has_many context_taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "Tagging",
|
24
|
-
:conditions => [
|
25
|
-
has_many context_tags, :through => context_taggings, :source => :tag
|
23
|
+
has_many context_taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "ActsAsTaggableOn::Tagging",
|
24
|
+
:conditions => ["#{ActsAsTaggableOn::Tagging.table_name}.tagger_id IS NULL AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_type]
|
25
|
+
has_many context_tags, :through => context_taggings, :source => :tag, :class_name => "ActsAsTaggableOn::Tag"
|
26
26
|
end
|
27
27
|
|
28
28
|
class_eval %(
|
@@ -66,7 +66,7 @@ module ActsAsTaggableOn::Taggable
|
|
66
66
|
# User.tagged_with("awesome", "cool", :any => true) # Users that are tagged with awesome or cool
|
67
67
|
# User.tagged_with("awesome", "cool", :match_all => true) # Users that are tagged with just awesome and cool
|
68
68
|
def tagged_with(tags, options = {})
|
69
|
-
tag_list = TagList.from(tags)
|
69
|
+
tag_list = ActsAsTaggableOn::TagList.from(tags)
|
70
70
|
|
71
71
|
return {} if tag_list.empty?
|
72
72
|
|
@@ -76,15 +76,15 @@ module ActsAsTaggableOn::Taggable
|
|
76
76
|
context = options.delete(:on)
|
77
77
|
|
78
78
|
if options.delete(:exclude)
|
79
|
-
tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
|
80
|
-
conditions << "#{table_name}.#{primary_key} NOT IN (SELECT #{Tagging.table_name}.taggable_id FROM #{Tagging.table_name} JOIN #{Tag.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND (#{tags_conditions}) WHERE #{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
|
79
|
+
tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
|
80
|
+
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}.id AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
|
81
81
|
|
82
82
|
elsif options.delete(:any)
|
83
|
-
tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
|
84
|
-
conditions << "#{table_name}.#{primary_key} IN (SELECT #{Tagging.table_name}.taggable_id FROM #{Tagging.table_name} JOIN #{Tag.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND (#{tags_conditions}) WHERE #{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
|
83
|
+
tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
|
84
|
+
conditions << "#{table_name}.#{primary_key} 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}.id AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
|
85
85
|
|
86
86
|
else
|
87
|
-
tags = Tag.named_any(tag_list)
|
87
|
+
tags = ActsAsTaggableOn::Tag.named_any(tag_list)
|
88
88
|
return scoped(:conditions => "1 = 0") unless tags.length == tag_list.length
|
89
89
|
|
90
90
|
tags.each do |tag|
|
@@ -93,7 +93,7 @@ module ActsAsTaggableOn::Taggable
|
|
93
93
|
|
94
94
|
taggings_alias = "#{table_name}_taggings_#{prefix}"
|
95
95
|
|
96
|
-
tagging_join = "JOIN #{Tagging.table_name} #{taggings_alias}" +
|
96
|
+
tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
|
97
97
|
" ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
|
98
98
|
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}" +
|
99
99
|
" AND #{taggings_alias}.tag_id = #{tag.id}"
|
@@ -106,7 +106,7 @@ module ActsAsTaggableOn::Taggable
|
|
106
106
|
taggings_alias, tags_alias = "#{table_name}_taggings_group", "#{table_name}_tags_group"
|
107
107
|
|
108
108
|
if options.delete(:match_all)
|
109
|
-
joins << "LEFT OUTER JOIN #{Tagging.table_name} #{taggings_alias}" +
|
109
|
+
joins << "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
|
110
110
|
" ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
|
111
111
|
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
|
112
112
|
|
@@ -154,7 +154,7 @@ module ActsAsTaggableOn::Taggable
|
|
154
154
|
|
155
155
|
def tag_list_cache_on(context)
|
156
156
|
variable_name = "@#{context.to_s.singularize}_list"
|
157
|
-
instance_variable_get(variable_name) || instance_variable_set(variable_name, TagList.new(tags_on(context).map(&:name)))
|
157
|
+
instance_variable_get(variable_name) || instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(tags_on(context).map(&:name)))
|
158
158
|
end
|
159
159
|
|
160
160
|
def tag_list_on(context)
|
@@ -166,40 +166,40 @@ module ActsAsTaggableOn::Taggable
|
|
166
166
|
variable_name = "@all_#{context.to_s.singularize}_list"
|
167
167
|
return instance_variable_get(variable_name) if instance_variable_get(variable_name)
|
168
168
|
|
169
|
-
instance_variable_set(variable_name, TagList.new(all_tags_on(context).map(&:name)).freeze)
|
169
|
+
instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(all_tags_on(context).map(&:name)).freeze)
|
170
170
|
end
|
171
171
|
|
172
172
|
##
|
173
173
|
# Returns all tags of a given context
|
174
174
|
def all_tags_on(context)
|
175
|
-
opts = ["#{Tagging.table_name}.context = ?", context.to_s]
|
176
|
-
base_tags.where(opts).order("#{Tagging.table_name}.created_at").group("#{Tagging.table_name}.tag_id").all
|
175
|
+
opts = ["#{ActsAsTaggableOn::Tagging.table_name}.context = ?", context.to_s]
|
176
|
+
base_tags.where(opts).order("#{ActsAsTaggableOn::Tagging.table_name}.created_at").group("#{ActsAsTaggableOn::Tagging.table_name}.tag_id").all
|
177
177
|
end
|
178
178
|
|
179
179
|
##
|
180
180
|
# Returns all tags that are not owned of a given context
|
181
181
|
def tags_on(context)
|
182
|
-
base_tags.where(["#{Tagging.table_name}.context = ? AND #{Tagging.table_name}.tagger_id IS NULL", context.to_s]).all
|
182
|
+
base_tags.where(["#{ActsAsTaggableOn::Tagging.table_name}.context = ? AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id IS NULL", context.to_s]).all
|
183
183
|
end
|
184
184
|
|
185
185
|
def set_tag_list_on(context, new_list)
|
186
186
|
add_custom_context(context)
|
187
187
|
|
188
188
|
variable_name = "@#{context.to_s.singularize}_list"
|
189
|
-
instance_variable_set(variable_name, TagList.from(new_list))
|
189
|
+
instance_variable_set(variable_name, ActsAsTaggableOn::TagList.from(new_list))
|
190
190
|
end
|
191
191
|
|
192
192
|
def tagging_contexts
|
193
193
|
custom_contexts + self.class.tag_types.map(&:to_s)
|
194
194
|
end
|
195
195
|
|
196
|
-
def reload
|
196
|
+
def reload(*args)
|
197
197
|
self.class.tag_types.each do |context|
|
198
198
|
instance_variable_set("@#{context.to_s.singularize}_list", nil)
|
199
199
|
instance_variable_set("@all_#{context.to_s.singularize}_list", nil)
|
200
200
|
end
|
201
201
|
|
202
|
-
super
|
202
|
+
super(*args)
|
203
203
|
end
|
204
204
|
|
205
205
|
def save_tags
|
@@ -209,7 +209,7 @@ module ActsAsTaggableOn::Taggable
|
|
209
209
|
tag_list = tag_list_cache_on(context).uniq
|
210
210
|
|
211
211
|
# Find existing tags or create non-existing tags:
|
212
|
-
tag_list = Tag.find_or_create_all_with_like_by_name(tag_list)
|
212
|
+
tag_list = ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name(tag_list)
|
213
213
|
|
214
214
|
current_tags = tags_on(context)
|
215
215
|
old_tags = current_tags - tag_list
|
@@ -221,7 +221,7 @@ module ActsAsTaggableOn::Taggable
|
|
221
221
|
|
222
222
|
if old_taggings.present?
|
223
223
|
# Destroy old taggings:
|
224
|
-
Tagging.destroy_all :id => old_taggings.map(&:id)
|
224
|
+
ActsAsTaggableOn::Tagging.destroy_all :id => old_taggings.map(&:id)
|
225
225
|
end
|
226
226
|
|
227
227
|
# Create new taggings:
|
@@ -30,9 +30,9 @@ module ActsAsTaggableOn::Taggable
|
|
30
30
|
|
31
31
|
module InstanceMethods
|
32
32
|
def owner_tags_on(owner, context)
|
33
|
-
base_tags.where([%(#{Tagging.table_name}.context = ? AND
|
34
|
-
#{Tagging.table_name}.tagger_id = ? AND
|
35
|
-
#{Tagging.table_name}.tagger_type = ?), context.to_s, owner.id, owner.class.to_s]).all
|
33
|
+
base_tags.where([%(#{ActsAsTaggableOn::Tagging.table_name}.context = ? AND
|
34
|
+
#{ActsAsTaggableOn::Tagging.table_name}.tagger_id = ? AND
|
35
|
+
#{ActsAsTaggableOn::Tagging.table_name}.tagger_type = ?), context.to_s, owner.id, owner.class.to_s]).all
|
36
36
|
end
|
37
37
|
|
38
38
|
def cached_owned_tag_list_on(context)
|
@@ -46,7 +46,7 @@ module ActsAsTaggableOn::Taggable
|
|
46
46
|
cache = cached_owned_tag_list_on(context)
|
47
47
|
cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
|
48
48
|
|
49
|
-
cache[owner] ||= TagList.new(*owner_tags_on(owner, context).map(&:name))
|
49
|
+
cache[owner] ||= ActsAsTaggableOn::TagList.new(*owner_tags_on(owner, context).map(&:name))
|
50
50
|
end
|
51
51
|
|
52
52
|
def set_owner_tag_list_on(owner, context, new_list)
|
@@ -55,22 +55,22 @@ module ActsAsTaggableOn::Taggable
|
|
55
55
|
cache = cached_owned_tag_list_on(context)
|
56
56
|
cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
|
57
57
|
|
58
|
-
cache[owner] = TagList.from(new_list)
|
58
|
+
cache[owner] = ActsAsTaggableOn::TagList.from(new_list)
|
59
59
|
end
|
60
60
|
|
61
|
-
def reload
|
61
|
+
def reload(*args)
|
62
62
|
self.class.tag_types.each do |context|
|
63
63
|
instance_variable_set("@owned_#{context}_list", nil)
|
64
64
|
end
|
65
65
|
|
66
|
-
super
|
66
|
+
super(*args)
|
67
67
|
end
|
68
68
|
|
69
69
|
def save_owned_tags
|
70
70
|
tagging_contexts.each do |context|
|
71
71
|
cached_owned_tag_list_on(context).each do |owner, tag_list|
|
72
72
|
# Find existing tags or create non-existing tags:
|
73
|
-
tag_list = Tag.find_or_create_all_with_like_by_name(tag_list.uniq)
|
73
|
+
tag_list = ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name(tag_list.uniq)
|
74
74
|
|
75
75
|
owned_tags = owner_tags_on(owner, context)
|
76
76
|
old_tags = owned_tags - tag_list
|
@@ -78,13 +78,13 @@ module ActsAsTaggableOn::Taggable
|
|
78
78
|
|
79
79
|
# Find all taggings that belong to the taggable (self), are owned by the owner,
|
80
80
|
# have the correct context, and are removed from the list.
|
81
|
-
old_taggings = Tagging.where(:taggable_id => id, :taggable_type => self.class.base_class.to_s,
|
82
|
-
|
83
|
-
|
81
|
+
old_taggings = ActsAsTaggableOn::Tagging.where(:taggable_id => id, :taggable_type => self.class.base_class.to_s,
|
82
|
+
:tagger_type => owner.class.to_s, :tagger_id => owner.id,
|
83
|
+
:tag_id => old_tags, :context => context).all
|
84
84
|
|
85
85
|
if old_taggings.present?
|
86
86
|
# Destroy old taggings:
|
87
|
-
Tagging.destroy_all(:id => old_taggings.map(&:id))
|
87
|
+
ActsAsTaggableOn::Tagging.destroy_all(:id => old_taggings.map(&:id))
|
88
88
|
end
|
89
89
|
|
90
90
|
# Create new taggings:
|
@@ -42,9 +42,9 @@ module ActsAsTaggableOn::Taggable
|
|
42
42
|
|
43
43
|
exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
|
44
44
|
|
45
|
-
klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
|
46
|
-
:from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
|
47
|
-
: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],
|
45
|
+
klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{ActsAsTaggableOn::Tag.table_name}.id) AS count",
|
46
|
+
:from => "#{klass.table_name}, #{ActsAsTaggableOn::Tag.table_name}, #{ActsAsTaggableOn::Tagging.table_name}",
|
47
|
+
:conditions => ["#{exclude_self} #{klass.table_name}.id = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id AND #{ActsAsTaggableOn::Tag.table_name}.name IN (?) AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_to_find, result_context],
|
48
48
|
:group => grouped_column_names_for(klass),
|
49
49
|
:order => "count DESC" }.update(options))
|
50
50
|
end
|
@@ -54,9 +54,9 @@ module ActsAsTaggableOn::Taggable
|
|
54
54
|
|
55
55
|
exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
|
56
56
|
|
57
|
-
klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
|
58
|
-
:from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
|
59
|
-
: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],
|
57
|
+
klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{ActsAsTaggableOn::Tag.table_name}.id) AS count",
|
58
|
+
:from => "#{klass.table_name}, #{ActsAsTaggableOn::Tag.table_name}, #{ActsAsTaggableOn::Tagging.table_name}",
|
59
|
+
:conditions => ["#{exclude_self} #{klass.table_name}.id = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id AND #{ActsAsTaggableOn::Tag.table_name}.name IN (?)", tags_to_find],
|
60
60
|
:group => grouped_column_names_for(klass),
|
61
61
|
:order => "count DESC" }.update(options))
|
62
62
|
end
|
@@ -16,8 +16,8 @@ module ActsAsTaggableOn
|
|
16
16
|
def acts_as_tagger(opts={})
|
17
17
|
class_eval do
|
18
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
|
19
|
+
:include => :tag, :class_name => "ActsAsTaggableOn::Tagging")
|
20
|
+
has_many :owned_tags, :through => :owned_taggings, :source => :tag, :uniq => true, :class_name => "ActsAsTaggableOn::Tag"
|
21
21
|
end
|
22
22
|
|
23
23
|
include ActsAsTaggableOn::Tagger::InstanceMethods
|
@@ -1,65 +1,67 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module ActsAsTaggableOn
|
2
|
+
class Tag < ::ActiveRecord::Base
|
3
|
+
include ActsAsTaggableOn::ActiveRecord::Backports if ::ActiveRecord::VERSION::MAJOR < 3
|
3
4
|
|
4
|
-
|
5
|
+
attr_accessible :name
|
5
6
|
|
6
|
-
|
7
|
+
### ASSOCIATIONS:
|
7
8
|
|
8
|
-
|
9
|
+
has_many :taggings, :dependent => :destroy, :class_name => 'ActsAsTaggableOn::Tagging'
|
9
10
|
|
10
|
-
|
11
|
+
### VALIDATIONS:
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
validates_presence_of :name
|
14
|
+
validates_uniqueness_of :name
|
14
15
|
|
15
|
-
|
16
|
+
### SCOPES:
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
def self.named(name)
|
19
|
+
where(["name LIKE ?", name])
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
def self.named_any(list)
|
23
|
+
where(list.map { |tag| sanitize_sql(["name LIKE ?", tag.to_s]) }.join(" OR "))
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
def self.named_like(name)
|
27
|
+
where(["name LIKE ?", "%#{name}%"])
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
def self.named_like_any(list)
|
31
|
+
where(list.map { |tag| sanitize_sql(["name LIKE ?", "%#{tag.to_s}%"]) }.join(" OR "))
|
32
|
+
end
|
32
33
|
|
33
|
-
|
34
|
+
### CLASS METHODS:
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
def self.find_or_create_with_like_by_name(name)
|
37
|
+
named_like(name).first || create(:name => name)
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
|
40
|
+
def self.find_or_create_all_with_like_by_name(*list)
|
41
|
+
list = [list].flatten
|
41
42
|
|
42
|
-
|
43
|
+
return [] if list.empty?
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
existing_tags = Tag.named_any(list).all
|
46
|
+
new_tag_names = list.reject { |name| existing_tags.any? { |tag| tag.name.mb_chars.downcase == name.mb_chars.downcase } }
|
47
|
+
created_tags = new_tag_names.map { |name| Tag.create(:name => name) }
|
47
48
|
|
48
|
-
|
49
|
-
|
49
|
+
existing_tags + created_tags
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
+
### INSTANCE METHODS:
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
def ==(object)
|
55
|
+
super || (object.is_a?(Tag) && name == object.name)
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
58
|
+
def to_s
|
59
|
+
name
|
60
|
+
end
|
60
61
|
|
61
|
-
|
62
|
-
|
63
|
-
|
62
|
+
def count
|
63
|
+
read_attribute(:count).to_i
|
64
|
+
end
|
64
65
|
|
65
|
-
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,95 +1,96 @@
|
|
1
|
-
|
1
|
+
module ActsAsTaggableOn
|
2
|
+
class TagList < Array
|
3
|
+
cattr_accessor :delimiter
|
4
|
+
self.delimiter = ','
|
2
5
|
|
3
|
-
|
4
|
-
self.delimiter = ','
|
6
|
+
attr_accessor :owner
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
add(*args)
|
10
|
-
end
|
8
|
+
def initialize(*args)
|
9
|
+
add(*args)
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
+
glue = delimiter.ends_with?(" ") ? delimiter : "#{delimiter} "
|
20
|
+
string = string.join(glue) if string.respond_to?(:join)
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
new.tap do |tag_list|
|
23
|
+
string = string.to_s.dup
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
# Parse the quoted tags
|
26
|
+
string.gsub!(/(\A|#{delimiter})\s*"(.*?)"\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
|
27
|
+
string.gsub!(/(\A|#{delimiter})\s*'(.*?)'\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
|
27
28
|
|
28
|
-
|
29
|
+
tag_list.add(string.split(delimiter))
|
30
|
+
end
|
29
31
|
end
|
30
|
-
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
33
|
+
##
|
34
|
+
# Add tags to the tag_list. Duplicate or blank tags will be ignored.
|
35
|
+
# Use the <tt>:parse</tt> option to add an unparsed tag string.
|
36
|
+
#
|
37
|
+
# Example:
|
38
|
+
# tag_list.add("Fun", "Happy")
|
39
|
+
# tag_list.add("Fun, Happy", :parse => true)
|
40
|
+
def add(*names)
|
41
|
+
extract_and_apply_options!(names)
|
42
|
+
concat(names)
|
43
|
+
clean!
|
44
|
+
self
|
45
|
+
end
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
47
|
+
##
|
48
|
+
# Remove specific tags from the tag_list.
|
49
|
+
# Use the <tt>:parse</tt> option to add an unparsed tag string.
|
50
|
+
#
|
51
|
+
# Example:
|
52
|
+
# tag_list.remove("Sad", "Lonely")
|
53
|
+
# tag_list.remove("Sad, Lonely", :parse => true)
|
54
|
+
def remove(*names)
|
55
|
+
extract_and_apply_options!(names)
|
56
|
+
delete_if { |name| names.include?(name) }
|
57
|
+
self
|
58
|
+
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
60
|
+
##
|
61
|
+
# Transform the tag_list into a tag string suitable for edting in a form.
|
62
|
+
# The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
|
63
|
+
#
|
64
|
+
# Example:
|
65
|
+
# tag_list = TagList.new("Round", "Square,Cube")
|
66
|
+
# tag_list.to_s # 'Round, "Square,Cube"'
|
67
|
+
def to_s
|
68
|
+
tags = frozen? ? self.dup : self
|
69
|
+
tags.send(:clean!)
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
tags.map do |name|
|
72
|
+
name.include?(delimiter) ? "\"#{name}\"" : name
|
73
|
+
end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
|
74
|
+
end
|
74
75
|
|
75
|
-
|
76
|
+
private
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
78
|
+
# Remove whitespace, duplicates, and blanks.
|
79
|
+
def clean!
|
80
|
+
reject!(&:blank?)
|
81
|
+
map!(&:strip)
|
82
|
+
uniq!
|
83
|
+
end
|
83
84
|
|
84
|
-
|
85
|
-
|
86
|
-
|
85
|
+
def extract_and_apply_options!(args)
|
86
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
87
|
+
options.assert_valid_keys :parse
|
87
88
|
|
88
|
-
|
89
|
-
|
90
|
-
|
89
|
+
if options[:parse]
|
90
|
+
args.map! { |a| self.class.from(a) }
|
91
|
+
end
|
91
92
|
|
92
|
-
|
93
|
+
args.flatten!
|
94
|
+
end
|
93
95
|
end
|
94
|
-
|
95
|
-
end
|
96
|
+
end
|
@@ -1,23 +1,24 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module ActsAsTaggableOn
|
2
|
+
class Tagging < ::ActiveRecord::Base #:nodoc:
|
3
|
+
include ActsAsTaggableOn::ActiveRecord::Backports if ::ActiveRecord::VERSION::MAJOR < 3
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
attr_accessible :tag,
|
6
|
+
:tag_id,
|
7
|
+
:context,
|
8
|
+
:taggable,
|
9
|
+
:taggable_type,
|
10
|
+
:taggable_id,
|
11
|
+
:tagger,
|
12
|
+
:tagger_type,
|
13
|
+
:tagger_id
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
belongs_to :tag, :class_name => 'ActsAsTaggableOn::Tag'
|
16
|
+
belongs_to :taggable, :polymorphic => true
|
17
|
+
belongs_to :tagger, :polymorphic => true
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
validates_uniqueness_of :tag_id, :scope => [ :taggable_type, :taggable_id, :context, :tagger_id, :tagger_type ]
|
19
|
+
validates_presence_of :context
|
20
|
+
validates_presence_of :tag_id
|
22
21
|
|
22
|
+
validates_uniqueness_of :tag_id, :scope => [ :taggable_type, :taggable_id, :context, :tagger_id, :tagger_type ]
|
23
|
+
end
|
23
24
|
end
|
@@ -1,17 +1,17 @@
|
|
1
|
-
module
|
1
|
+
module ActsAsTaggableOn
|
2
|
+
module TagsHelper
|
3
|
+
# See the README for an example using tag_cloud.
|
4
|
+
def tag_cloud(tags, classes)
|
5
|
+
tags = tags.all if tags.respond_to?(:all)
|
2
6
|
|
3
|
-
|
4
|
-
def tag_cloud(tags, classes)
|
5
|
-
tags = tags.all if tags.respond_to?(:all)
|
7
|
+
return [] if tags.empty?
|
6
8
|
|
7
|
-
|
9
|
+
max_count = tags.sort_by(&:count).last.count.to_f
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
yield tag, classes[index]
|
11
|
+
tags.each do |tag|
|
12
|
+
index = ((tag.count / max_count) * (classes.size - 1)).round
|
13
|
+
yield tag, classes[index]
|
14
|
+
end
|
14
15
|
end
|
15
16
|
end
|
16
|
-
|
17
|
-
end
|
17
|
+
end
|
@@ -11,7 +11,7 @@ module ActsAsTaggableOn
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def self.source_root
|
14
|
-
File.join(File.dirname(__FILE__), 'templates', orm)
|
14
|
+
File.join(File.dirname(__FILE__), 'templates', (orm.to_s unless orm.class.eql?(String)) )
|
15
15
|
end
|
16
16
|
|
17
17
|
def self.orm_has_migration?
|
@@ -29,3 +29,4 @@ module ActsAsTaggableOn
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
2
|
|
3
3
|
describe "Acts As Taggable On" do
|
4
4
|
before(:each) do
|
@@ -11,7 +11,7 @@ describe "Acts As Taggable On" do
|
|
11
11
|
|
12
12
|
describe "Taggable Method Generation" do
|
13
13
|
before(:each) do
|
14
|
-
|
14
|
+
clean_database!
|
15
15
|
@taggable = TaggableModel.new(:name => "Bob Jones")
|
16
16
|
end
|
17
17
|
|
@@ -1,11 +1,11 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
2
|
|
3
3
|
describe "acts_as_tagger" do
|
4
4
|
before(:each) do
|
5
5
|
clean_database!
|
6
6
|
end
|
7
7
|
|
8
|
-
|
8
|
+
describe "Tagger Method Generation" do
|
9
9
|
before(:each) do
|
10
10
|
@tagger = TaggableUser.new()
|
11
11
|
end
|
@@ -84,7 +84,7 @@ describe "acts_as_tagger" do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
|
87
|
+
describe "when called by multiple tagger's" do
|
88
88
|
before(:each) do
|
89
89
|
@user_x = TaggableUser.create(:name => "User X")
|
90
90
|
@user_y = TaggableUser.create(:name => "User Y")
|
@@ -1,8 +1,8 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
2
|
|
3
|
-
describe TagList do
|
3
|
+
describe ActsAsTaggableOn::TagList do
|
4
4
|
before(:each) do
|
5
|
-
@tag_list = TagList.new("awesome","radical")
|
5
|
+
@tag_list = ActsAsTaggableOn::TagList.new("awesome","radical")
|
6
6
|
end
|
7
7
|
|
8
8
|
it "should be an array" do
|
@@ -1,20 +1,20 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
2
|
|
3
|
-
describe Tag do
|
3
|
+
describe ActsAsTaggableOn::Tag do
|
4
4
|
before(:each) do
|
5
5
|
clean_database!
|
6
|
-
@tag = Tag.new
|
6
|
+
@tag = ActsAsTaggableOn::Tag.new
|
7
7
|
@user = TaggableModel.create(:name => "Pablo")
|
8
8
|
end
|
9
9
|
|
10
10
|
describe "named like any" do
|
11
11
|
before(:each) do
|
12
|
-
Tag.create(:name => "awesome")
|
13
|
-
Tag.create(:name => "epic")
|
12
|
+
ActsAsTaggableOn::Tag.create(:name => "awesome")
|
13
|
+
ActsAsTaggableOn::Tag.create(:name => "epic")
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should find both tags" do
|
17
|
-
Tag.named_like_any(["awesome", "epic"]).should have(2).items
|
17
|
+
ActsAsTaggableOn::Tag.named_like_any(["awesome", "epic"]).should have(2).items
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -25,17 +25,17 @@ describe Tag do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
it "should find by name" do
|
28
|
-
Tag.find_or_create_with_like_by_name("awesome").should == @tag
|
28
|
+
ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("awesome").should == @tag
|
29
29
|
end
|
30
30
|
|
31
31
|
it "should find by name case insensitive" do
|
32
|
-
Tag.find_or_create_with_like_by_name("AWESOME").should == @tag
|
32
|
+
ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("AWESOME").should == @tag
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should create by name" do
|
36
36
|
lambda {
|
37
|
-
Tag.find_or_create_with_like_by_name("epic")
|
38
|
-
}.should change(Tag, :count).by(1)
|
37
|
+
ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("epic")
|
38
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(1)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -46,27 +46,27 @@ describe Tag do
|
|
46
46
|
end
|
47
47
|
|
48
48
|
it "should find by name" do
|
49
|
-
Tag.find_or_create_all_with_like_by_name("awesome").should == [@tag]
|
49
|
+
ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name("awesome").should == [@tag]
|
50
50
|
end
|
51
51
|
|
52
52
|
it "should find by name case insensitive" do
|
53
|
-
Tag.find_or_create_all_with_like_by_name("AWESOME").should == [@tag]
|
53
|
+
ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name("AWESOME").should == [@tag]
|
54
54
|
end
|
55
55
|
|
56
56
|
it "should create by name" do
|
57
57
|
lambda {
|
58
|
-
Tag.find_or_create_all_with_like_by_name("epic")
|
59
|
-
}.should change(Tag, :count).by(1)
|
58
|
+
ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name("epic")
|
59
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(1)
|
60
60
|
end
|
61
61
|
|
62
62
|
it "should find or create by name" do
|
63
63
|
lambda {
|
64
|
-
Tag.find_or_create_all_with_like_by_name("awesome", "epic").map(&:name).should == ["awesome", "epic"]
|
65
|
-
}.should change(Tag, :count).by(1)
|
64
|
+
ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name("awesome", "epic").map(&:name).should == ["awesome", "epic"]
|
65
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(1)
|
66
66
|
end
|
67
67
|
|
68
68
|
it "should return an empty array if no tags are specified" do
|
69
|
-
Tag.find_or_create_all_with_like_by_name([]).should == []
|
69
|
+
ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name([]).should == []
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -91,7 +91,7 @@ describe Tag do
|
|
91
91
|
|
92
92
|
it "should equal a tag with the same name" do
|
93
93
|
@tag.name = "awesome"
|
94
|
-
new_tag = Tag.new(:name => "awesome")
|
94
|
+
new_tag = ActsAsTaggableOn::Tag.new(:name => "awesome")
|
95
95
|
new_tag.should == @tag
|
96
96
|
end
|
97
97
|
|
@@ -103,13 +103,13 @@ describe Tag do
|
|
103
103
|
it "have named_scope named(something)" do
|
104
104
|
@tag.name = "cool"
|
105
105
|
@tag.save!
|
106
|
-
Tag.named('cool').should include(@tag)
|
106
|
+
ActsAsTaggableOn::Tag.named('cool').should include(@tag)
|
107
107
|
end
|
108
108
|
|
109
109
|
it "have named_scope named_like(something)" do
|
110
110
|
@tag.name = "cool"
|
111
111
|
@tag.save!
|
112
|
-
@another_tag = Tag.create!(:name => "coolip")
|
113
|
-
Tag.named_like('cool').should include(@tag, @another_tag)
|
112
|
+
@another_tag = ActsAsTaggableOn::Tag.create!(:name => "coolip")
|
113
|
+
ActsAsTaggableOn::Tag.named_like('cool').should include(@tag, @another_tag)
|
114
114
|
end
|
115
115
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
2
|
|
3
3
|
describe "Taggable" do
|
4
4
|
before(:each) do
|
@@ -26,11 +26,11 @@ describe "Taggable" do
|
|
26
26
|
|
27
27
|
it "should be able to create tags" do
|
28
28
|
@taggable.skill_list = "ruby, rails, css"
|
29
|
-
@taggable.instance_variable_get("@skill_list").instance_of?(TagList).should be_true
|
29
|
+
@taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
|
30
30
|
|
31
31
|
lambda {
|
32
32
|
@taggable.save
|
33
|
-
}.should change(Tag, :count).by(3)
|
33
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(3)
|
34
34
|
|
35
35
|
@taggable.reload
|
36
36
|
@taggable.skill_list.sort.should == %w(ruby rails css).sort
|
@@ -90,7 +90,7 @@ describe "Taggable" do
|
|
90
90
|
bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby")
|
91
91
|
frank = TaggableModel.create(:name => "Frank", :tag_list => "Ruby")
|
92
92
|
|
93
|
-
Tag.find(:all).size.should == 1
|
93
|
+
ActsAsTaggableOn::Tag.find(:all).size.should == 1
|
94
94
|
TaggableModel.tagged_with("ruby").to_a.should == TaggableModel.tagged_with("Ruby").to_a
|
95
95
|
end
|
96
96
|
|
@@ -178,6 +178,11 @@ describe "Taggable" do
|
|
178
178
|
TaggableModel.tagged_with(["ruby", "rails"], :order => 'taggable_models.name').to_a.should == [bob, frank]
|
179
179
|
end
|
180
180
|
|
181
|
+
it "should be able to find tagged with quotation marks" do
|
182
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive, 'I love the ,comma,'")
|
183
|
+
TaggableModel.tagged_with("'I love the ,comma,'").should include(bob)
|
184
|
+
end
|
185
|
+
|
181
186
|
it "should be able to find tagged with invalid tags" do
|
182
187
|
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive")
|
183
188
|
TaggableModel.tagged_with("sad, happier").should_not include(bob)
|
@@ -236,7 +241,7 @@ describe "Taggable" do
|
|
236
241
|
bob.tag_list << "happier"
|
237
242
|
bob.tag_list << "happier"
|
238
243
|
bob.save
|
239
|
-
}.should change(Tagging, :count).by(1)
|
244
|
+
}.should change(ActsAsTaggableOn::Tagging, :count).by(1)
|
240
245
|
end
|
241
246
|
|
242
247
|
describe "Associations" do
|
@@ -253,7 +258,7 @@ describe "Taggable" do
|
|
253
258
|
|
254
259
|
describe "grouped_column_names_for method" do
|
255
260
|
it "should return all column names joined for Tag GROUP clause" do
|
256
|
-
@taggable.grouped_column_names_for(Tag).should == "tags.id, tags.name"
|
261
|
+
@taggable.grouped_column_names_for(ActsAsTaggableOn::Tag).should == "tags.id, tags.name"
|
257
262
|
end
|
258
263
|
|
259
264
|
it "should return all column names joined for TaggableModel GROUP clause" do
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
2
|
|
3
3
|
describe "Tagger" do
|
4
4
|
before(:each) do
|
@@ -22,7 +22,7 @@ describe "Tagger" do
|
|
22
22
|
lambda{
|
23
23
|
@user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
|
24
24
|
@user2.tag(@taggable, :with => 'java, python, lisp, ruby', :on => :tags)
|
25
|
-
}.should change(Tagging, :count).by(6)
|
25
|
+
}.should change(ActsAsTaggableOn::Tagging, :count).by(6)
|
26
26
|
|
27
27
|
[@user, @user2, @taggable].each(&:reload)
|
28
28
|
|
@@ -43,7 +43,7 @@ describe "Tagger" do
|
|
43
43
|
|
44
44
|
lambda {
|
45
45
|
@user2.tag(@taggable, :with => 'java, python, lisp', :on => :tags)
|
46
|
-
}.should change(Tagging, :count).by(-1)
|
46
|
+
}.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
|
47
47
|
|
48
48
|
[@user, @user2, @taggable].each(&:reload)
|
49
49
|
|
@@ -62,7 +62,7 @@ describe "Tagger" do
|
|
62
62
|
|
63
63
|
lambda {
|
64
64
|
@user2.tag(@taggable, :with => 'epic', :on => :tags)
|
65
|
-
}.should change(Tagging, :count).by(-1)
|
65
|
+
}.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
|
66
66
|
|
67
67
|
@taggable.reload
|
68
68
|
@taggable.all_tags_list.should include('awesome')
|
@@ -79,7 +79,7 @@ describe "Tagger" do
|
|
79
79
|
|
80
80
|
lambda {
|
81
81
|
@taggable.update_attributes(:tag_list => "")
|
82
|
-
}.should change(Tagging, :count).by(-1)
|
82
|
+
}.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
|
83
83
|
|
84
84
|
@taggable.tag_list.should == []
|
85
85
|
@taggable.all_tags_list.sort.should == %w(ruby scheme).sort
|
@@ -1,14 +1,14 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
2
|
|
3
|
-
describe Tagging do
|
3
|
+
describe ActsAsTaggableOn::Tagging do
|
4
4
|
before(:each) do
|
5
5
|
clean_database!
|
6
|
-
@tagging = Tagging.new
|
6
|
+
@tagging = ActsAsTaggableOn::Tagging.new
|
7
7
|
end
|
8
8
|
|
9
9
|
it "should not be valid with a invalid tag" do
|
10
10
|
@tagging.taggable = TaggableModel.create(:name => "Bob Jones")
|
11
|
-
@tagging.tag = Tag.new(:name => "")
|
11
|
+
@tagging.tag = ActsAsTaggableOn::Tag.new(:name => "")
|
12
12
|
@tagging.context = "tags"
|
13
13
|
|
14
14
|
@tagging.should_not be_valid
|
@@ -22,10 +22,10 @@ describe Tagging do
|
|
22
22
|
|
23
23
|
it "should not create duplicate taggings" do
|
24
24
|
@taggable = TaggableModel.create(:name => "Bob Jones")
|
25
|
-
@tag = Tag.create(:name => "awesome")
|
25
|
+
@tag = ActsAsTaggableOn::Tag.create(:name => "awesome")
|
26
26
|
|
27
27
|
lambda {
|
28
|
-
2.times { Tagging.create(:taggable => @taggable, :tag => @tag, :context => 'tags') }
|
29
|
-
}.should change(Tagging, :count).by(1)
|
28
|
+
2.times { ActsAsTaggableOn::Tagging.create(:taggable => @taggable, :tag => @tag, :context => 'tags') }
|
29
|
+
}.should change(ActsAsTaggableOn::Tagging, :count).by(1)
|
30
30
|
end
|
31
31
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
2
|
|
3
|
-
describe TagsHelper do
|
3
|
+
describe ActsAsTaggableOn::TagsHelper do
|
4
4
|
before(:each) do
|
5
5
|
clean_database!
|
6
6
|
|
@@ -9,7 +9,7 @@ describe TagsHelper do
|
|
9
9
|
@eve = TaggableModel.create(:name => "Eve Nodd", :language_list => "ruby, c++")
|
10
10
|
|
11
11
|
@helper = class Helper
|
12
|
-
include TagsHelper
|
12
|
+
include ActsAsTaggableOn::TagsHelper
|
13
13
|
end.new
|
14
14
|
end
|
15
15
|
|
data/spec/spec_helper.rb
CHANGED
@@ -1,27 +1,31 @@
|
|
1
|
+
$LOAD_PATH << "." unless $LOAD_PATH.include?(".")
|
2
|
+
|
1
3
|
begin
|
2
|
-
|
3
|
-
require File.expand_path("../.bundle/environment", __FILE__)
|
4
|
-
rescue LoadError
|
5
|
-
# Fall back on doing an unlocked resolve at runtime.
|
6
|
-
require "rubygems" unless RUBY_VERSION >= "1.9"
|
4
|
+
require "rubygems"
|
7
5
|
require "bundler"
|
6
|
+
|
7
|
+
if Gem::Version.new(Bundler::VERSION) <= Gem::Version.new("0.9.5")
|
8
|
+
raise RuntimeError, "Your bundler version is too old." +
|
9
|
+
"Run `gem install bundler` to upgrade."
|
10
|
+
end
|
11
|
+
|
12
|
+
# Set up load paths for all bundled gems
|
8
13
|
Bundler.setup
|
14
|
+
rescue Bundler::GemNotFound
|
15
|
+
raise RuntimeError, "Bundler couldn't find some gems." +
|
16
|
+
"Did you run `bundle install`?"
|
9
17
|
end
|
10
18
|
|
11
19
|
Bundler.require
|
12
20
|
require File.expand_path('../../lib/acts-as-taggable-on', __FILE__)
|
13
21
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def freq
|
22
|
-
k=Hash.new(0)
|
23
|
-
each {|e| k[e]+=1}
|
24
|
-
k
|
22
|
+
unless [].respond_to?(:freq)
|
23
|
+
class Array
|
24
|
+
def freq
|
25
|
+
k=Hash.new(0)
|
26
|
+
each {|e| k[e]+=1}
|
27
|
+
k
|
28
|
+
end
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
@@ -43,10 +47,10 @@ ActiveRecord::Base.silence do
|
|
43
47
|
end
|
44
48
|
|
45
49
|
def clean_database!
|
46
|
-
models = [Tag, Tagging, TaggableModel, OtherTaggableModel, InheritingTaggableModel,
|
50
|
+
models = [ActsAsTaggableOn::Tag, ActsAsTaggableOn::Tagging, TaggableModel, OtherTaggableModel, InheritingTaggableModel,
|
47
51
|
AlteredInheritingTaggableModel, TaggableUser, UntaggableModel]
|
48
52
|
models.each do |model|
|
49
|
-
model.
|
53
|
+
ActiveRecord::Base.connection.execute "DELETE FROM #{model.table_name}"
|
50
54
|
end
|
51
55
|
end
|
52
56
|
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 2
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 2.0.
|
8
|
+
- 5
|
9
|
+
version: 2.0.5
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Michael Bleigh
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-30 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -63,7 +63,6 @@ files:
|
|
63
63
|
- spec/bm.rb
|
64
64
|
- spec/models.rb
|
65
65
|
- spec/schema.rb
|
66
|
-
- spec/spec.opts
|
67
66
|
- spec/spec_helper.rb
|
68
67
|
has_rdoc: true
|
69
68
|
homepage: http://github.com/mbleigh/acts-as-taggable-on
|
data/spec/spec.opts
DELETED