acts-as-taggable-on 1.0.6 → 1.0.7

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 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