acts-as-taggable-on 1.0.13 → 1.0.17

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 CHANGED
@@ -151,6 +151,12 @@ A helper is included to assist with generating tag clouds.
151
151
 
152
152
  Here is an example that generates a tag cloud.
153
153
 
154
+ Helper:
155
+
156
+ module PostsHelper
157
+ include TagsHelper
158
+ end
159
+
154
160
  Controller:
155
161
 
156
162
  class PostController < ApplicationController
@@ -160,7 +166,8 @@ Controller:
160
166
  end
161
167
 
162
168
  View:
163
- <% tag_cloud @tags, %w(css1 css2 css3 css4) do |tag, css_class| %>
169
+
170
+ <% tag_cloud(@tags, %w(css1 css2 css3 css4)) do |tag, css_class| %>
164
171
  <%= link_to tag.name, { :action => :tag, :id => tag.name }, :class => css_class %>
165
172
  <% end %>
166
173
 
@@ -187,5 +194,6 @@ CSS:
187
194
  * slainer68 - STI fix
188
195
  * harrylove - migration instructions and fix-ups
189
196
  * lawrencepit - cached tag work
197
+ * sobrinho - fixed tag_cloud helper
190
198
 
191
199
  Copyright (c) 2007-2009 Michael Bleigh (http://mbleigh.com/) and Intridea Inc. (http://intridea.com/), released under the MIT license
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ begin
9
9
  gemspec.email = "michael@intridea.com"
10
10
  gemspec.homepage = "http://github.com/mbleigh/acts-as-taggable-on"
11
11
  gemspec.authors = ["Michael Bleigh"]
12
- gemspec.files = FileList["[A-Z]*", "{lib,spec,rails}/**/*"] - FileList["**/*.log"]
12
+ gemspec.files = FileList["[A-Z]*", "{generators,lib,spec,rails}/**/*"] - FileList["**/*.log"]
13
13
  end
14
14
  Jeweler::GemcutterTasks.new
15
15
  rescue LoadError
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.13
1
+ 1.0.17
@@ -0,0 +1,7 @@
1
+ class ActsAsTaggableOnMigrationGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.migration_template 'migration.rb', 'db/migrate', :migration_file_name => "acts_as_taggable_on_migration"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ class ActsAsTaggableOnMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :tags do |t|
4
+ t.column :name, :string
5
+ end
6
+
7
+ create_table :taggings do |t|
8
+ t.column :tag_id, :integer
9
+ t.column :taggable_id, :integer
10
+ t.column :tagger_id, :integer
11
+ t.column :tagger_type, :string
12
+
13
+ # You should make sure that the column created is
14
+ # long enough to store the required class names.
15
+ t.column :taggable_type, :string
16
+ t.column :context, :string
17
+
18
+ t.column :created_at, :datetime
19
+ end
20
+
21
+ add_index :taggings, :tag_id
22
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
23
+ end
24
+
25
+ def self.down
26
+ drop_table :taggings
27
+ drop_table :tags
28
+ end
29
+ end
@@ -139,9 +139,9 @@ module ActiveRecord
139
139
  end
140
140
 
141
141
  def find_options_for_find_tagged_with(tags, options = {})
142
- tags = TagList.from(tags)
142
+ tag_list = TagList.from(tags)
143
143
 
144
- return {} if tags.empty?
144
+ return {} if tag_list.empty?
145
145
 
146
146
  joins = []
147
147
  conditions = []
@@ -150,28 +150,26 @@ module ActiveRecord
150
150
 
151
151
 
152
152
  if options.delete(:exclude)
153
- tags_conditions = tags.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
153
+ tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
154
154
  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)})"
155
155
 
156
156
  else
157
+ tags = Tag.named_like_any(tag_list)
158
+ return { :conditions => "1 = 0" } unless tags.length == tag_list.length
159
+
157
160
  tags.each do |tag|
158
- safe_tag = tag.gsub(/[^a-zA-Z0-9]/, '')
161
+ safe_tag = tag.name.gsub(/[^a-zA-Z0-9]/, '')
159
162
  prefix = "#{safe_tag}_#{rand(1024)}"
160
163
 
161
164
  taggings_alias = "#{table_name}_taggings_#{prefix}"
162
- tags_alias = "#{table_name}_tags_#{prefix}"
163
165
 
164
166
  tagging_join = "JOIN #{Tagging.table_name} #{taggings_alias}" +
165
167
  " ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
166
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
168
+ " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}" +
169
+ " AND #{taggings_alias}.tag_id = #{tag.id}"
167
170
  tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
168
171
 
169
- tag_join = "JOIN #{Tag.table_name} #{tags_alias}" +
170
- " ON #{tags_alias}.id = #{taggings_alias}.tag_id" +
171
- " AND " + sanitize_sql(["#{tags_alias}.name like ?", tag])
172
-
173
172
  joins << tagging_join
174
- joins << tag_join
175
173
  end
176
174
  end
177
175
 
@@ -225,14 +223,32 @@ module ActiveRecord
225
223
 
226
224
  joins = ["LEFT OUTER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
227
225
  joins << sanitize_sql(["AND #{Tagging.table_name}.context = ?",options.delete(:on).to_s]) unless options[:on].nil?
228
-
229
226
  joins << " INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
227
+
230
228
  unless self.descends_from_active_record?
231
229
  # Current model is STI descendant, so add type checking to the join condition
232
230
  joins << " AND #{table_name}.#{self.inheritance_column} = '#{self.name}'"
233
231
  end
234
232
 
235
- joins << scope[:joins] if scope && scope[:joins]
233
+ # Based on a proposed patch by donV to ActiveRecord Base
234
+ # This is needed because merge_joins and construct_join are private in ActiveRecord Base
235
+ if scope && scope[:joins]
236
+ case scope[:joins]
237
+ when Array
238
+ scope_joins = scope[:joins].flatten
239
+ strings = scope_joins.select{|j| j.is_a? String}
240
+ joins << strings.join(' ') + " "
241
+ symbols = scope_joins - strings
242
+ join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, symbols, nil)
243
+ joins << " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} "
244
+ joins.flatten!
245
+ when Symbol, Hash
246
+ join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, scope[:joins], nil)
247
+ joins << " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} "
248
+ when String
249
+ joins << scope[:joins]
250
+ end
251
+ end
236
252
 
237
253
  at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
238
254
  at_most = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
@@ -6,6 +6,7 @@ class Tag < ActiveRecord::Base
6
6
 
7
7
  named_scope :named, lambda { |name| { :conditions => ["name = ?", name] } }
8
8
  named_scope :named_like, lambda { |name| { :conditions => ["name LIKE ?", "%#{name}%"] } }
9
+ named_scope :named_like_any, lambda { |list| { :conditions => list.map { |tag| sanitize_sql(["name LIKE ?", tag.to_s]) }.join(" OR ") } }
9
10
 
10
11
  # LIKE is used for cross-database case-insensitivity
11
12
  def self.find_or_create_with_like_by_name(name)
@@ -79,8 +79,8 @@ class TagList < Array
79
79
  string = string.to_s.dup
80
80
 
81
81
  # Parse the quoted tags
82
- string.gsub!(/"(.*?)"\s*#{delimiter}?\s*/) { tag_list << $1; "" }
83
- string.gsub!(/'(.*?)'\s*#{delimiter}?\s*/) { tag_list << $1; "" }
82
+ string.gsub!(/(\A|#{delimiter})\s*"(.*?)"\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
83
+ string.gsub!(/(\A|#{delimiter})\s*'(.*?)'\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
84
84
 
85
85
  tag_list.add(string.split(delimiter))
86
86
  end
@@ -5,4 +5,6 @@ class Tagging < ActiveRecord::Base #:nodoc:
5
5
 
6
6
  validates_presence_of :context
7
7
  validates_presence_of :tag_id
8
+
9
+ validates_uniqueness_of :tag_id, :scope => [:taggable_type, :taggable_id, :context]
8
10
  end
@@ -1,6 +1,8 @@
1
1
  module TagsHelper
2
2
  # See the README for an example using tag_cloud.
3
3
  def tag_cloud(tags, classes)
4
+ return [] if tags.empty?
5
+
4
6
  max_count = tags.sort_by(&:count).last.count.to_f
5
7
 
6
8
  tags.each do |tag|
data/rails/init.rb CHANGED
@@ -2,6 +2,4 @@ require 'acts-as-taggable-on'
2
2
 
3
3
  ActiveRecord::Base.send :include, ActiveRecord::Acts::TaggableOn
4
4
  ActiveRecord::Base.send :include, ActiveRecord::Acts::Tagger
5
- ActionView::Base.send :include, TagsHelper if defined?(ActionView::Base)
6
-
7
- RAILS_DEFAULT_LOGGER.info "** acts_as_taggable_on: initialized properly."
5
+ ActionView::Base.send :include, TagsHelper if defined?(ActionView::Base)
@@ -176,16 +176,6 @@ describe "Acts As Taggable On" do
176
176
  end
177
177
 
178
178
  describe 'Tagging Contexts' do
179
- before(:all) do
180
- class Array
181
- def freq
182
- k=Hash.new(0)
183
- self.each {|e| k[e]+=1}
184
- k
185
- end
186
- end
187
- end
188
-
189
179
  it 'should eliminate duplicate tagging contexts ' do
190
180
  TaggableModel.acts_as_taggable_on(:skills, :skills)
191
181
  TaggableModel.tag_types.freq[:skills].should_not == 3
@@ -212,10 +202,6 @@ describe "Acts As Taggable On" do
212
202
  TaggableModel.acts_as_taggable_on([nil])
213
203
  }.should_not raise_error
214
204
  end
215
-
216
- after(:all) do
217
- class Array; remove_method :freq; end
218
- end
219
205
  end
220
206
 
221
207
  end
@@ -20,6 +20,18 @@ describe TagList do
20
20
  @tag_list.include?("wicked").should be_true
21
21
  end
22
22
 
23
+ it "should be able to add delimited list of words with quoted delimiters" do
24
+ @tag_list.add("'cool, wicked', \"really cool, really wicked\"", :parse => true)
25
+ @tag_list.include?("cool, wicked").should be_true
26
+ @tag_list.include?("really cool, really wicked").should be_true
27
+ end
28
+
29
+ it "should be able to handle other uses of quotation marks correctly" do
30
+ @tag_list.add("john's cool car, mary's wicked toy", :parse => true)
31
+ @tag_list.include?("john's cool car").should be_true
32
+ @tag_list.include?("mary's wicked toy").should be_true
33
+ end
34
+
23
35
  it "should be able to add an array of words" do
24
36
  @tag_list.add(["cool", "wicked"], :parse => true)
25
37
  @tag_list.include?("cool").should be_true
@@ -7,6 +7,17 @@ describe Tag do
7
7
  Tag.delete_all
8
8
  end
9
9
 
10
+ describe "named like any" do
11
+ before(:each) do
12
+ Tag.create(:name => "awesome")
13
+ Tag.create(:name => "epic")
14
+ end
15
+
16
+ it "should find both tags" do
17
+ Tag.named_like_any(["awesome", "epic"]).should have(2).items
18
+ end
19
+ end
20
+
10
21
  describe "find or create by name" do
11
22
  before(:each) do
12
23
  @tag.name = "awesome"
@@ -77,6 +77,8 @@ describe "Taggable" do
77
77
  @taggable.save
78
78
 
79
79
  TaggableModel.tagged_with("ruby").first.should == @taggable
80
+ TaggableModel.tagged_with("ruby, css").first.should == @taggable
81
+ TaggableModel.tagged_with("ruby, nonexistingtag").should be_empty
80
82
  TaggableModel.tagged_with("bob", :on => :skills).first.should_not == @taggable
81
83
  TaggableModel.tagged_with("bob", :on => :tags).first.should == @taggable
82
84
  end
@@ -13,4 +13,13 @@ describe Tagging do
13
13
  @tagging.should_not be_valid
14
14
  @tagging.errors.on(:tag_id).should == "can't be blank"
15
15
  end
16
+
17
+ it "should not create duplicate taggings" do
18
+ @taggable = TaggableModel.create(:name => "Bob Jones")
19
+ @tag = Tag.create(:name => "awesome")
20
+
21
+ lambda {
22
+ 2.times { Tagging.create(:taggable => @taggable, :tag => @tag, :context => 'tags') }
23
+ }.should change(Tagging, :count).by(1)
24
+ end
16
25
  end
data/spec/spec_helper.rb CHANGED
@@ -7,6 +7,14 @@ module Spec::Example::ExampleGroupMethods
7
7
  alias :context :describe
8
8
  end
9
9
 
10
+ class Array
11
+ def freq
12
+ k=Hash.new(0)
13
+ each {|e| k[e]+=1}
14
+ k
15
+ end
16
+ end
17
+
10
18
  TEST_DATABASE_FILE = File.join(File.dirname(__FILE__), '..', 'test.sqlite3')
11
19
 
12
20
  File.unlink(TEST_DATABASE_FILE) if File.exist?(TEST_DATABASE_FILE)
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.13
4
+ version: 1.0.17
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-12-11 00:00:00 +01:00
12
+ date: 2010-01-05 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -27,6 +27,8 @@ files:
27
27
  - README.rdoc
28
28
  - Rakefile
29
29
  - VERSION
30
+ - generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb
31
+ - generators/acts_as_taggable_on_migration/templates/migration.rb
30
32
  - lib/acts-as-taggable-on.rb
31
33
  - lib/acts_as_taggable_on/acts_as_taggable_on.rb
32
34
  - lib/acts_as_taggable_on/acts_as_tagger.rb