acts-as-taggable-on 1.0.6 → 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -35,17 +35,15 @@ GemPlugin
35
35
  Acts As Taggable On is also available as a gem plugin using Rails 2.1's gem dependencies.
36
36
  To install the gem, add this to your config/environment.rb:
37
37
 
38
- config.gem "mbleigh-acts-as-taggable-on", :source => "http://gems.github.com", :lib => "acts-as-taggable-on"
38
+ config.gem "acts-as-taggable-on", :source => "http://gemcutter.org"
39
39
 
40
40
  After that, you can run "rake gems:install" to install the gem if you don't already have it.
41
- See http://ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies for
42
- additional details about gem dependencies in Rails.
43
41
 
44
42
  ** NOTE **
45
43
  Some issues have been experienced with "rake gems:install". If that doesn't work to install the gem,
46
44
  try just installing it as a normal gem:
47
45
 
48
- gem install mbleigh-acts-as-taggable-on --source http://gems.github.com
46
+ gem install acts-as-taggable-on --source http://gemcutter.org
49
47
 
50
48
  Post Installation (Rails)
51
49
  -------------------------
data/Rakefile CHANGED
@@ -11,6 +11,7 @@ begin
11
11
  gemspec.authors = ["Michael Bleigh"]
12
12
  gemspec.files = FileList["[A-Z]*", "{lib,spec,rails}/**/*"] - FileList["**/*.log"]
13
13
  end
14
+ Jeweler::GemcutterTasks.new
14
15
  rescue LoadError
15
16
  puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
16
17
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.6
1
+ 1.0.7
@@ -68,6 +68,10 @@ module ActiveRecord
68
68
  def top_#{tag_type}(limit = 10)
69
69
  tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
70
70
  end
71
+
72
+ def self.top_#{tag_type}(limit = 10)
73
+ tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
74
+ end
71
75
  RUBY
72
76
  end
73
77
 
@@ -87,8 +91,8 @@ module ActiveRecord
87
91
  after_save :save_tags
88
92
 
89
93
  if respond_to?(:named_scope)
90
- named_scope :tagged_with, lambda{ |tags, options|
91
- find_options_for_find_tagged_with(tags, options)
94
+ named_scope :tagged_with, lambda{ |*args|
95
+ find_options_for_find_tagged_with(*args)
92
96
  }
93
97
  end
94
98
  end
@@ -126,36 +130,55 @@ module ActiveRecord
126
130
  end
127
131
 
128
132
  def find_options_for_find_tagged_with(tags, options = {})
129
- tags = tags.is_a?(Array) ? TagList.new(tags.map(&:to_s)) : TagList.from(tags)
130
-
133
+ tags = TagList.from(tags)
134
+
131
135
  return {} if tags.empty?
132
-
136
+
137
+ joins = []
133
138
  conditions = []
134
- conditions << sanitize_sql(options.delete(:conditions)) if options[:conditions]
135
139
 
136
- unless (on = options.delete(:on)).nil?
137
- conditions << sanitize_sql(["context = ?",on.to_s])
138
- end
139
-
140
- taggings_alias, tags_alias = "#{table_name}_taggings", "#{table_name}_tags"
140
+ context = options.delete(:on)
141
141
 
142
+
142
143
  if options.delete(:exclude)
143
- tags_conditions = tags.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
144
- conditions << sanitize_sql(["#{table_name}.id NOT IN (SELECT #{Tagging.table_name}.taggable_id FROM #{Tagging.table_name} LEFT OUTER JOIN #{Tag.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id WHERE (#{tags_conditions}) AND #{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})", tags])
145
- else
146
- conditions << tags.map { |t| sanitize_sql(["#{tags_alias}.name LIKE ?", t]) }.join(" OR ")
144
+ tags_conditions = "(" + tags.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ") + ")"
145
+ 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)})"
146
+
147
+ else
148
+ tags.each do |tag|
149
+ safe_tag = tag.gsub(/[^a-zA-Z0-9]/, '')
150
+ prefix = "#{safe_tag}_#{rand(1024)}"
151
+
152
+ taggings_alias = "#{table_name}_taggings_#{prefix}"
153
+ tags_alias = "#{table_name}_tags_#{prefix}"
154
+
155
+ tagging_join = "JOIN #{Tagging.table_name} #{taggings_alias}" +
156
+ " ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
157
+ " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
158
+ tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
159
+
160
+ tag_join = "JOIN #{Tag.table_name} #{tags_alias}" +
161
+ " ON #{tags_alias}.id = #{taggings_alias}.tag_id" +
162
+ " AND " + sanitize_sql(["#{tags_alias}.name like ?", tag])
163
+
164
+ joins << tagging_join
165
+ joins << tag_join
166
+ end
167
+ end
168
+
169
+ taggings_alias, tags_alias = "#{table_name}_taggings_group", "#{table_name}_tags_group"
147
170
 
148
- if options.delete(:match_all)
149
- group = "#{taggings_alias}.taggable_id HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
150
- end
171
+ if options.delete(:match_all)
172
+ joins << "LEFT OUTER JOIN #{Tagging.table_name} #{taggings_alias}" +
173
+ " ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
174
+ " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
175
+
176
+ group = "#{table_name}.#{primary_key} HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
151
177
  end
152
178
 
153
- { :select => "DISTINCT #{table_name}.*",
154
- :joins => "LEFT OUTER JOIN #{Tagging.table_name} #{taggings_alias} ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key} AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)} " +
155
- "LEFT OUTER JOIN #{Tag.table_name} #{tags_alias} ON #{tags_alias}.id = #{taggings_alias}.tag_id",
156
- :conditions => conditions.join(" AND "),
157
- :group => group
158
- }.update(options)
179
+ { :joins => joins.join(" "),
180
+ :group => group,
181
+ :conditions => conditions.join(" AND ") }.update(options)
159
182
  end
160
183
 
161
184
  # Calculate the tag counts for all tags.
@@ -178,6 +201,7 @@ module ActiveRecord
178
201
 
179
202
  taggable_type = sanitize_sql(["#{Tagging.table_name}.taggable_type = ?", base_class.name])
180
203
  taggable_id = sanitize_sql(["#{Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
204
+ options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
181
205
 
182
206
  conditions = [
183
207
  taggable_type,
@@ -192,7 +216,13 @@ module ActiveRecord
192
216
 
193
217
  joins = ["LEFT OUTER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
194
218
  joins << sanitize_sql(["AND #{Tagging.table_name}.context = ?",options.delete(:on).to_s]) unless options[:on].nil?
195
- joins << "LEFT OUTER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
219
+
220
+ joins << " INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
221
+ unless self.descends_from_active_record?
222
+ # Current model is STI descendant, so add type checking to the join condition
223
+ joins << " AND #{table_name}.#{self.inheritance_column} = '#{self.name}'"
224
+ end
225
+
196
226
  joins << scope[:joins] if scope && scope[:joins]
197
227
 
198
228
  at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
@@ -204,7 +234,9 @@ module ActiveRecord
204
234
  { :select => "#{Tag.table_name}.id, #{Tag.table_name}.name, COUNT(*) AS count",
205
235
  :joins => joins.join(" "),
206
236
  :conditions => conditions,
207
- :group => group_by
237
+ :group => group_by,
238
+ :limit => options[:limit],
239
+ :order => options[:order]
208
240
  }
209
241
  end
210
242
 
@@ -73,6 +73,8 @@ class TagList < Array
73
73
  # tag_list = TagList.from("One , Two, Three")
74
74
  # tag_list # ["One", "Two", "Three"]
75
75
  def from(string)
76
+ string = string.join(", ") if string.respond_to?(:join)
77
+
76
78
  returning new do |tag_list|
77
79
  string = string.to_s.dup
78
80
 
@@ -20,6 +20,12 @@ describe TagList do
20
20
  @tag_list.include?("wicked").should be_true
21
21
  end
22
22
 
23
+ it "should be able to add an array of words" do
24
+ @tag_list.add(["cool", "wicked"], :parse => true)
25
+ @tag_list.include?("cool").should be_true
26
+ @tag_list.include?("wicked").should be_true
27
+ end
28
+
23
29
  it "should be able to remove words" do
24
30
  @tag_list.remove("awesome")
25
31
  @tag_list.include?("awesome").should be_false
@@ -30,6 +36,11 @@ describe TagList do
30
36
  @tag_list.should be_empty
31
37
  end
32
38
 
39
+ it "should be able to remove an array of words" do
40
+ @tag_list.remove(["awesome", "radical"], :parse => true)
41
+ @tag_list.should be_empty
42
+ end
43
+
33
44
  it "should give a delimited list of words when converted to string" do
34
45
  @tag_list.to_s.should == "awesome, radical"
35
46
  end
@@ -117,6 +117,22 @@ describe "Taggable" do
117
117
  TaggableModel.tagged_with('rails', :on => :skills).tagged_with('happier', :on => :tags).should == [bob]
118
118
  end
119
119
 
120
+ it "should be able to find tagged with only the matching tags" do
121
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "lazy, happier")
122
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "fitter, happier, inefficient")
123
+ steve = TaggableModel.create(:name => 'Steve', :tag_list => "fitter, happier")
124
+
125
+ TaggableModel.find_tagged_with("fitter, happier", :match_all => true).should == [steve]
126
+ end
127
+
128
+ it "should be able to find tagged with some excluded tags" do
129
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "happier, lazy")
130
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "happier")
131
+ steve = TaggableModel.create(:name => 'Steve', :tag_list => "happier")
132
+
133
+ TaggableModel.find_tagged_with("lazy", :exclude => true).should == [frank, steve]
134
+ end
135
+
120
136
  describe "Single Table Inheritance" do
121
137
  before do
122
138
  [TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
@@ -143,5 +159,16 @@ describe "Taggable" do
143
159
  InheritingTaggableModel.find_tagged_with("fork", :on => :parts).should be_empty
144
160
  AlteredInheritingTaggableModel.find_tagged_with("fork", :on => :parts).first.should == @inherited_different
145
161
  end
162
+
163
+ it "should have different tag_counts_on for inherited models" do
164
+ @inherited_same.tag_list = "bob, kelso"
165
+ @inherited_same.save!
166
+ @inherited_different.tag_list = "fork, spoon"
167
+ @inherited_different.save!
168
+
169
+ InheritingTaggableModel.tag_counts_on(:tags).map(&:name).should == %w(bob kelso)
170
+ AlteredInheritingTaggableModel.tag_counts_on(:tags).map(&:name).should == %w(fork spoon)
171
+ TaggableModel.tag_counts_on(:tags).map(&:name).should == %w(bob kelso fork spoon)
172
+ end
146
173
  end
147
174
  end
@@ -1,7 +1,3 @@
1
1
  --colour
2
- --format
3
- specdoc
4
- --loadby
5
- mtime
6
2
  --reverse
7
3
  --backtrace
@@ -1,7 +1,6 @@
1
1
  # require File.dirname(__FILE__) + '/../../../../spec/spec_helper'
2
2
  require 'rubygems'
3
- require 'active_support'
4
- require 'active_record'
3
+ require 'activerecord'
5
4
  require 'spec'
6
5
 
7
6
  module Spec::Example::ExampleGroupMethods
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts-as-taggable-on
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-14 00:00:00 -04:00
12
+ date: 2009-11-12 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15