acts-as-taggable-on 2.0.0.pre4 → 2.0.0.pre5

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,4 +1,20 @@
1
1
  begin
2
+ # Rspec 1.3.0
3
+ require 'spec/rake/spectask'
4
+
5
+ desc 'Default: run specs'
6
+ task :default => :spec
7
+ Spec::Rake::SpecTask.new do |t|
8
+ t.spec_files = FileList["spec/**/*_spec.rb"]
9
+ end
10
+
11
+ Spec::Rake::SpecTask.new('rcov') do |t|
12
+ t.spec_files = FileList["spec/**/*_spec.rb"]
13
+ t.rcov = true
14
+ t.rcov_opts = ['--exclude', 'spec']
15
+ end
16
+
17
+ rescue LoadError
2
18
  # Rspec 2.0
3
19
  require 'rspec/core/rake_task'
4
20
 
@@ -12,23 +28,8 @@ begin
12
28
  t.pattern = "spec/**/*_spec.rb"
13
29
  t.rcov = true
14
30
  t.rcov_opts = ['--exclude', 'spec']
15
- end
16
-
17
- rescue LoadError
18
- # Rspec 1.3.0
19
- require 'spec/rake/spectask'
20
-
21
- desc 'Default: run specs'
22
- task :default => :spec
23
- Spec::Rake::SpecTask.new do |t|
24
- t.spec_files = FileList["spec/**/*_spec.rb"]
25
31
  end
26
32
 
27
- Spec::Rake::SpecTask.new('rcov') do |t|
28
- t.spec_files = FileList["spec/**/*_spec.rb"]
29
- t.rcov = true
30
- t.rcov_opts = ['--exclude', 'spec']
31
- end
32
33
  rescue LoadError
33
34
  puts "Rspec not available. Install it with: gem install rspec"
34
35
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0.pre4
1
+ 2.0.0.pre5
@@ -1,15 +1,13 @@
1
1
  require "active_record"
2
2
  require "action_view"
3
3
 
4
- if ActiveRecord::VERSION::MAJOR < 3
5
- require "acts_as_taggable_on/compatibility/active_record_backports"
6
- require "acts_as_taggable_on/compatibility/tag"
7
- require "acts_as_taggable_on/compatibility/tagging"
8
- end
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+
6
+ require "acts_as_taggable_on/compatibility/active_record_backports" if ActiveRecord::VERSION::MAJOR < 3
9
7
 
10
8
  require "acts_as_taggable_on/acts_as_taggable_on"
11
9
  require "acts_as_taggable_on/acts_as_taggable_on/core"
12
- require "acts_as_taggable_on/acts_as_taggable_on/aggregate"
10
+ require "acts_as_taggable_on/acts_as_taggable_on/collection"
13
11
  require "acts_as_taggable_on/acts_as_taggable_on/cache"
14
12
  require "acts_as_taggable_on/acts_as_taggable_on/ownership"
15
13
  require "acts_as_taggable_on/acts_as_taggable_on/related"
@@ -20,6 +18,8 @@ require "acts_as_taggable_on/tag_list"
20
18
  require "acts_as_taggable_on/tags_helper"
21
19
  require "acts_as_taggable_on/tagging"
22
20
 
21
+ $LOAD_PATH.shift
22
+
23
23
  if defined?(ActiveRecord::Base)
24
24
  ActiveRecord::Base.extend ActsAsTaggableOn::Taggable
25
25
  ActiveRecord::Base.send :include, ActsAsTaggableOn::Tagger
@@ -17,7 +17,7 @@ module ActsAsTaggableOn
17
17
  if ::ActiveRecord::VERSION::MAJOR < 3
18
18
  include ActsAsTaggableOn::ActiveRecord::Backports
19
19
  end
20
-
20
+
21
21
  write_inheritable_attribute(:tag_types, tag_types)
22
22
  class_inheritable_reader(:tag_types)
23
23
 
@@ -28,14 +28,14 @@ module ActsAsTaggableOn
28
28
  def self.taggable?
29
29
  true
30
30
  end
31
+
32
+ include ActsAsTaggableOn::Taggable::Core
33
+ include ActsAsTaggableOn::Taggable::Collection
34
+ include ActsAsTaggableOn::Taggable::Cache
35
+ include ActsAsTaggableOn::Taggable::Ownership
36
+ include ActsAsTaggableOn::Taggable::Related
31
37
  end
32
38
  end
33
-
34
- include ActsAsTaggableOn::Taggable::Core
35
- include ActsAsTaggableOn::Taggable::Aggregate
36
- include ActsAsTaggableOn::Taggable::Cache
37
- include ActsAsTaggableOn::Taggable::Ownership
38
- include ActsAsTaggableOn::Taggable::Related
39
39
  end
40
40
  end
41
41
  end
@@ -11,26 +11,43 @@ module ActsAsTaggableOn::Taggable
11
11
  before_save :save_cached_tag_list
12
12
  end
13
13
 
14
- base.tag_types.map(&:to_s).each do |tag_type|
15
- base.class_eval %(
16
- def self.caching_#{tag_type.singularize}_list?
17
- caching_tag_list_on?("#{tag_type}")
18
- end
19
- )
20
- end
14
+ base.intialize_acts_as_taggable_on_cache
21
15
  end
22
16
 
23
17
  module ClassMethods
18
+ def intialize_acts_as_taggable_on_cache
19
+ tag_types.map(&:to_s).each do |tag_type|
20
+ class_eval %(
21
+ def self.caching_#{tag_type.singularize}_list?
22
+ caching_tag_list_on?("#{tag_type}")
23
+ end
24
+ )
25
+ end
26
+ end
27
+
28
+ def acts_as_taggable_on(*args)
29
+ super(*args)
30
+ intialize_acts_as_taggable_on_cache
31
+ end
32
+
24
33
  def caching_tag_list_on?(context)
25
34
  column_names.include?("cached_#{context.to_s.singularize}_list")
26
35
  end
27
36
  end
28
37
 
29
38
  module InstanceMethods
39
+ def tag_list_cache_set_on(context)
40
+ variable_name = "@#{context.to_s.singularize}_list"
41
+ !instance_variable_get(variable_name).nil?
42
+ end
43
+
30
44
  def save_cached_tag_list
31
45
  tag_types.map(&:to_s).each do |tag_type|
32
46
  if self.class.send("caching_#{tag_type.singularize}_list?")
33
- self["cached_#{tag_type.singularize}_list"] = tag_list_cache_on(tag_type.singularize).to_a.flatten.compact.join(', ')
47
+ if tag_list_cache_set_on(tag_type)
48
+ list = tag_list_cache_on(tag_type.singularize).to_a.flatten.compact.join(', ')
49
+ self["cached_#{tag_type.singularize}_list"] = list
50
+ end
34
51
  end
35
52
  end
36
53
  end
@@ -1,35 +1,41 @@
1
1
  module ActsAsTaggableOn::Taggable
2
- module Aggregate
2
+ module Collection
3
3
  def self.included(base)
4
- unless base.ancestors.include?(ActsAsTaggableOn::Taggable::Aggregate::InstanceMethods)
5
- base.send :include, ActsAsTaggableOn::Taggable::Aggregate::InstanceMethods
6
- base.extend ActsAsTaggableOn::Taggable::Aggregate::ClassMethods
7
- end
8
-
9
- base.tag_types.map(&:to_s).each do |tag_type|
10
- base.class_eval %(
11
- def #{tag_type.singularize}_counts(options = {})
12
- tag_counts_on('#{tag_type}', options)
13
- end
14
-
15
- def top_#{tag_type}(limit = 10)
16
- tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
17
- end
18
-
19
- def self.top_#{tag_type}(limit = 10)
20
- tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
21
- end
22
- )
23
- end
4
+ base.send :include, ActsAsTaggableOn::Taggable::Collection::InstanceMethods
5
+ base.extend ActsAsTaggableOn::Taggable::Collection::ClassMethods
6
+ base.initialize_acts_as_taggable_on_collection
24
7
  end
25
8
 
26
9
  module ClassMethods
27
- def tag_counts_on(context, options = {})
28
- find_for_tag_counts(options.merge({:on => context.to_s}))
29
- end
10
+ def initialize_acts_as_taggable_on_collection
11
+ tag_types.map(&:to_s).each do |tag_type|
12
+ class_eval %(
13
+ def self.#{tag_type.singularize}_counts(options={})
14
+ tag_counts_on('#{tag_type}', options)
15
+ end
30
16
 
31
- def all_tag_counts(options = {})
32
- find_for_tag_counts(options)
17
+ def #{tag_type.singularize}_counts(options = {})
18
+ tag_counts_on('#{tag_type}', options)
19
+ end
20
+
21
+ def top_#{tag_type}(limit = 10)
22
+ tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
23
+ end
24
+
25
+ def self.top_#{tag_type}(limit = 10)
26
+ tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
27
+ end
28
+ )
29
+ end
30
+ end
31
+
32
+ def acts_as_taggable_on(*args)
33
+ super(*args)
34
+ initialize_acts_as_taggable_on_collection
35
+ end
36
+
37
+ def tag_counts_on(context, options = {})
38
+ all_tag_counts(options.merge({:on => context.to_s}))
33
39
  end
34
40
 
35
41
  # Calculate the tag counts for all tags.
@@ -43,7 +49,7 @@ module ActsAsTaggableOn::Taggable
43
49
  # :at_least - Exclude tags with a frequency less than the given value
44
50
  # :at_most - Exclude tags with a frequency greater than the given value
45
51
  # :on - Scope the find to only include a certain context
46
- def find_for_tag_counts(options = {})
52
+ def all_tag_counts(options = {})
47
53
  options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :on, :id
48
54
 
49
55
  start_at = sanitize_sql(["#{Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
@@ -79,7 +85,6 @@ module ActsAsTaggableOn::Taggable
79
85
  group_by << " AND #{having}" unless having.blank?
80
86
 
81
87
  Tag.select("#{Tag.table_name}.*, COUNT(*) AS count").joins(joins.join(" ")).where(conditions).group(group_by).limit(options[:limit]).order(options[:order])
82
-
83
88
  end
84
89
  end
85
90
 
@@ -6,51 +6,51 @@ module ActsAsTaggableOn::Taggable
6
6
 
7
7
  base.class_eval do
8
8
  attr_writer :custom_contexts
9
-
10
9
  after_save :save_tags
11
-
12
- def self.tagged_with(*args)
13
- find_options_for_find_tagged_with(*args)
14
- end
15
10
  end
16
11
 
17
- base.tag_types.map(&:to_s).each do |tag_type|
18
- context_taggings = "#{tag_type.singularize}_taggings".to_sym
19
- context_tags = tag_type.to_sym
20
-
21
- base.class_eval do
22
- has_many context_taggings, :as => :taggable, :dependent => :destroy, :include => :tag,
23
- :conditions => ['#{Tagging.table_name}.context = ?', tag_type], :class_name => "Tagging"
24
- has_many context_tags, :through => context_taggings, :source => :tag
25
- end
26
-
27
- base.class_eval %(
28
- def self.#{tag_type.singularize}_counts(options={})
29
- tag_counts_on('#{tag_type}', options)
12
+ base.initialize_acts_as_taggable_on_core
13
+ end
14
+
15
+ module ClassMethods
16
+ def initialize_acts_as_taggable_on_core
17
+ tag_types.map(&:to_s).each do |tag_type|
18
+ context_taggings = "#{tag_type.singularize}_taggings".to_sym
19
+ context_tags = tag_type.to_sym
20
+
21
+ class_eval do
22
+ has_many context_taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "Tagging",
23
+ :conditions => ['#{Tagging.table_name}.tagger_id IS NULL AND #{Tagging.table_name}.context = ?', tag_type]
24
+ has_many context_tags, :through => context_taggings, :source => :tag
30
25
  end
31
26
 
32
- def #{tag_type.singularize}_list
33
- tag_list_on('#{tag_type}')
34
- end
27
+ class_eval %(
28
+ def #{tag_type.singularize}_list
29
+ tag_list_on('#{tag_type}')
30
+ end
35
31
 
36
- def #{tag_type.singularize}_list=(new_tags)
37
- set_tag_list_on('#{tag_type}', new_tags)
38
- end
39
-
40
- def all_#{tag_type}_list
41
- all_tags_list_on('#{tag_type}')
42
- end
43
- )
32
+ def #{tag_type.singularize}_list=(new_tags)
33
+ set_tag_list_on('#{tag_type}', new_tags)
34
+ end
35
+
36
+ def all_#{tag_type}_list
37
+ all_tags_list_on('#{tag_type}')
38
+ end
39
+ )
40
+ end
44
41
  end
45
- end
46
-
47
- module ClassMethods
42
+
43
+ def acts_as_taggable_on(*args)
44
+ super(*args)
45
+ initialize_acts_as_taggable_on_core
46
+ end
47
+
48
48
  # all column names are necessary for PostgreSQL group clause
49
49
  def grouped_column_names_for(object)
50
50
  object.column_names.map { |column| "#{object.table_name}.#{column}" }.join(", ")
51
51
  end
52
52
 
53
- def find_options_for_find_tagged_with(tags, options = {})
53
+ def tagged_with(tags, options = {})
54
54
  tag_list = TagList.from(tags)
55
55
 
56
56
  return {} if tag_list.empty?
@@ -98,19 +98,19 @@ module ActsAsTaggableOn::Taggable
98
98
  group = "#{grouped_column_names_for(self)} HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
99
99
  end
100
100
 
101
+
101
102
  joins(joins.join(" ")).group(group).where(conditions.join(" AND ")).readonly(false)
102
103
  end
103
104
 
104
105
  def is_taggable?
105
106
  true
106
107
  end
107
-
108
108
  end
109
109
 
110
110
  module InstanceMethods
111
111
  # all column names are necessary for PostgreSQL group clause
112
112
  def grouped_column_names_for(object)
113
- object.column_names.map { |column| "#{object.table_name}.#{column}" }.join(", ")
113
+ self.class.grouped_column_names_for(object)
114
114
  end
115
115
 
116
116
  def custom_contexts
@@ -156,7 +156,13 @@ module ActsAsTaggableOn::Taggable
156
156
  ##
157
157
  # Returns all tags that are not owned of a given context
158
158
  def tags_on(context)
159
- base_tags.where(["#{Tagging.table_name}.context = ? AND #{Tagging.table_name}.tagger_id IS NULL", context.to_s]).all
159
+ if respond_to?(context)
160
+ # If the association is available, use it:
161
+ send(context).all
162
+ else
163
+ # If the association is not available, query it the old fashioned way
164
+ base_tags.where(["#{Tagging.table_name}.context = ? AND #{Tagging.table_name}.tagger_id IS NULL", context.to_s]).all
165
+ end
160
166
  end
161
167
 
162
168
  def set_tag_list_on(context, new_list)
@@ -180,7 +186,7 @@ module ActsAsTaggableOn::Taggable
180
186
  end
181
187
 
182
188
  def save_tags
183
- transaction do
189
+ transaction do
184
190
  tagging_contexts.each do |context|
185
191
  tag_list = tag_list_cache_on(context).uniq
186
192
 
@@ -192,11 +198,13 @@ module ActsAsTaggableOn::Taggable
192
198
  new_tags = tag_list - current_tags
193
199
 
194
200
  # Find taggings to remove:
195
- old_taggings = Tagging.where(:taggable_id => self.id, :taggable_type => self.class.base_class.to_s,
196
- :tagger_type => nil, :tagger_id => nil,
197
- :context => context.to_s, :tag_id => old_tags)
201
+ old_taggings = taggings.where(:tagger_type => nil, :tagger_id => nil,
202
+ :context => context.to_s, :tag_id => old_tags).all
198
203
 
199
- Tagging.destroy_all :id => old_taggings.map(&:id)
204
+ if old_taggings.present?
205
+ # Destroy old taggings:
206
+ Tagging.destroy_all :id => old_taggings.map(&:id)
207
+ end
200
208
 
201
209
  # Create new taggings:
202
210
  new_tags.each do |tag|
@@ -0,0 +1,29 @@
1
+ module ActsAsTaggableOn::Taggable
2
+ module Dirty
3
+ def self.included(base)
4
+ include ActsAsTaggableOn::Taggable::Dirty::InstanceMethods
5
+
6
+ base.tag_types.map(&:to_s).each do |tag_type|
7
+ base.class_eval %(
8
+ def #{tag_type.singularize}_list_changed?
9
+ tag_list_changed_on?('#{tag_type}')
10
+ tag_list_on('#{tag_type}')
11
+ end
12
+
13
+ def #{tag_type.singularize}_list=(new_tags)
14
+ change_tag_list_on('#{tag_type}', new_tags)
15
+ super(new_tags)
16
+ end
17
+ )
18
+ end
19
+ end
20
+
21
+ module InstanceMethods
22
+ def tag_list_changed_on?(context)
23
+ end
24
+
25
+ def change_tag_list_on(context, new_tags)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -6,18 +6,26 @@ module ActsAsTaggableOn::Taggable
6
6
 
7
7
  base.class_eval do
8
8
  after_save :save_owned_tags
9
- end
10
-
11
- base.tag_types.map(&:to_s).each do |tag_type|
12
- base.class_eval %(
13
- def #{tag_type}_from(owner)
14
- owner_tag_list_on(owner, '#{tag_type}')
15
- end
16
- )
17
9
  end
10
+
11
+ base.initialize_acts_as_taggable_on_ownership
18
12
  end
19
13
 
20
14
  module ClassMethods
15
+ def acts_as_taggable_on(*args)
16
+ initialize_acts_as_taggable_on_ownership
17
+ super(*args)
18
+ end
19
+
20
+ def initialize_acts_as_taggable_on_ownership
21
+ tag_types.map(&:to_s).each do |tag_type|
22
+ class_eval %(
23
+ def #{tag_type}_from(owner)
24
+ owner_tag_list_on(owner, '#{tag_type}')
25
+ end
26
+ )
27
+ end
28
+ end
21
29
  end
22
30
 
23
31
  module InstanceMethods
@@ -65,16 +73,15 @@ module ActsAsTaggableOn::Taggable
65
73
  # Find existing tags or create non-existing tags:
66
74
  tag_list = Tag.find_or_create_all_with_like_by_name(tag_list.uniq)
67
75
 
68
- owned_tags = owner_tags_on(owner, context)
69
-
70
- old_tags = owned_tags - tag_list
71
- new_tags = tag_list - owned_tags
76
+ owned_tags = owner_tags_on(owner, context)
77
+ old_tags = owned_tags - tag_list
78
+ new_tags = tag_list - owned_tags
72
79
 
73
80
  # Find all taggings that belong to the taggable (self), are owned by the owner,
74
81
  # have the correct context, and are removed from the list.
75
82
  old_taggings = Tagging.where(:taggable_id => id, :taggable_type => self.class.base_class.to_s,
76
83
  :tagger_type => owner.class.to_s, :tagger_id => owner.id,
77
- :tag_id => old_tags, :context => context)
84
+ :tag_id => old_tags, :context => context).all
78
85
 
79
86
  if old_taggings.present?
80
87
  # Destroy old taggings:
@@ -1,75 +1,63 @@
1
1
  module ActsAsTaggableOn::Taggable
2
2
  module Related
3
3
  def self.included(base)
4
- unless base.ancestors.include?(ActsAsTaggableOn::Taggable::Related::InstanceMethods)
5
- base.send :include, ActsAsTaggableOn::Taggable::Related::InstanceMethods
6
- base.extend ActsAsTaggableOn::Taggable::Related::ClassMethods
7
- end
8
-
9
- base.tag_types.map(&:to_s).each do |tag_type|
10
- base.class_eval %(
11
- def find_related_#{tag_type}(options = {})
12
- related_tags_for('#{tag_type}', self.class, options)
13
- end
14
- alias_method :find_related_on_#{tag_type}, :find_related_#{tag_type}
4
+ base.send :include, ActsAsTaggableOn::Taggable::Related::InstanceMethods
5
+ base.extend ActsAsTaggableOn::Taggable::Related::ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def initialize_acts_as_taggable_on_related
10
+ tag_types.map(&:to_s).each do |tag_type|
11
+ class_eval %(
12
+ def find_related_#{tag_type}(options = {})
13
+ related_tags_for('#{tag_type}', self.class, options)
14
+ end
15
+ alias_method :find_related_on_#{tag_type}, :find_related_#{tag_type}
15
16
 
16
- def find_related_#{tag_type}_for(klass, options = {})
17
- related_tags_for('#{tag_type}', klass, options)
18
- end
17
+ def find_related_#{tag_type}_for(klass, options = {})
18
+ related_tags_for('#{tag_type}', klass, options)
19
+ end
19
20
 
20
- def find_matching_contexts(search_context, result_context, options = {})
21
- matching_contexts_for(search_context.to_s, result_context.to_s, self.class, options)
22
- end
21
+ def find_matching_contexts(search_context, result_context, options = {})
22
+ matching_contexts_for(search_context.to_s, result_context.to_s, self.class, options)
23
+ end
23
24
 
24
- def find_matching_contexts_for(klass, search_context, result_context, options = {})
25
- matching_contexts_for(search_context.to_s, result_context.to_s, klass, options)
26
- end
27
- )
25
+ def find_matching_contexts_for(klass, search_context, result_context, options = {})
26
+ matching_contexts_for(search_context.to_s, result_context.to_s, klass, options)
27
+ end
28
+ )
29
+ end
30
+ end
31
+
32
+ def acts_as_taggable_on(*args)
33
+ super(*args)
34
+ initialize_acts_as_taggable_on_related
28
35
  end
29
- end
30
-
31
- module ClassMethods
32
36
  end
33
37
 
34
38
  module InstanceMethods
35
39
  def matching_contexts_for(search_context, result_context, klass, options = {})
36
- search_conditions = matching_context_search_options(search_context, result_context, klass, options)
37
-
38
- # klass.select(search_conditions[:select]).from(search_conditions[:from]).where(search_conditions[:conditions]).group(search_conditions[:group]).order(search_conditions[:order])
39
- klass.scoped(search_conditions)
40
- end
41
-
42
- def matching_context_search_options(search_context, result_context, klass, options = {})
43
40
  tags_to_find = tags_on(search_context).collect { |t| t.name }
44
41
 
45
42
  exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
46
-
47
- { :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
48
- :from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
49
- :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],
50
- :group => grouped_column_names_for(klass),
51
- :order => "count DESC"
52
- }.update(options)
43
+
44
+ klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
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))
53
49
  end
54
50
 
55
51
  def related_tags_for(context, klass, options = {})
56
- search_conditions = related_search_options(context, klass, options)
57
-
58
- # klass.select(search_conditions[:select]).from(search_conditions[:from]).where(search_conditions[:conditions]).group(search_conditions[:group]).order(search_conditions[:order])
59
- klass.scoped(search_conditions)
60
- end
61
-
62
- def related_search_options(context, klass, options = {})
63
52
  tags_to_find = tags_on(context).collect { |t| t.name }
64
53
 
65
54
  exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
66
55
 
67
- { :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
68
- :from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
69
- :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],
70
- :group => grouped_column_names_for(klass),
71
- :order => "count DESC"
72
- }.update(options)
56
+ klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
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))
73
61
  end
74
62
  end
75
63
  end
@@ -6,10 +6,10 @@ module ActsAsTaggableOn
6
6
  named_scope :where, lambda { |conditions| { :conditions => conditions } }
7
7
  named_scope :joins, lambda { |joins| { :joins => joins } }
8
8
  named_scope :group, lambda { |group| { :group => group } }
9
- named_scope :readonly, lambda { |readonly| { :readonly => readonly } }
10
9
  named_scope :order, lambda { |order| { :order => order } }
11
10
  named_scope :select, lambda { |select| { :select => select } }
12
- named_scope :limit, lambda { |limit| { :limit => limit } }
11
+ named_scope :limit, lambda { |limit| { :limit => limit } }
12
+ named_scope :readonly, lambda { |readonly| { :readonly => readonly } }
13
13
  end
14
14
  end
15
15
  end
@@ -1,5 +1,6 @@
1
1
  class Tag < ActiveRecord::Base
2
-
2
+ include ActsAsTaggableOn::ActiveRecord::Backports if ActiveRecord::VERSION::MAJOR < 3
3
+
3
4
  attr_accessible :name
4
5
 
5
6
  ### ASSOCIATIONS:
@@ -1,4 +1,5 @@
1
1
  class Tagging < ActiveRecord::Base #:nodoc:
2
+ include ActsAsTaggableOn::ActiveRecord::Backports if ActiveRecord::VERSION::MAJOR < 3
2
3
 
3
4
  attr_accessible :tag,
4
5
  :tag_id,
@@ -35,19 +35,10 @@ describe "Acts As Taggable On" do
35
35
  @taggable.should respond_to(:tags, :skills, :languages)
36
36
  end
37
37
 
38
- it "should generate a cached column checker for each tag type" do
39
- TaggableModel.should respond_to(:caching_tag_list?, :caching_skill_list?, :caching_language_list?)
40
- end
41
-
42
38
  it "should add tagged_with and tag_counts to singleton" do
43
39
  TaggableModel.should respond_to(:tagged_with, :tag_counts)
44
40
  end
45
41
 
46
- it "should add saving of tag lists and cached tag lists to the instance" do
47
- @taggable.should respond_to(:save_cached_tag_list)
48
- @taggable.should respond_to(:save_tags)
49
- end
50
-
51
42
  it "should generate a tag_list accessor/setter for each tag type" do
52
43
  @taggable.should respond_to(:tag_list, :skill_list, :language_list)
53
44
  @taggable.should respond_to(:tag_list=, :skill_list=, :language_list=)
@@ -215,5 +206,61 @@ describe "Acts As Taggable On" do
215
206
  }.should_not raise_error
216
207
  end
217
208
  end
209
+
210
+ describe 'Caching' do
211
+ before(:each) do
212
+ @taggable = CachedModel.new(:name => "Bob Jones")
213
+ end
214
+
215
+ it "should add saving of tag lists and cached tag lists to the instance" do
216
+ @taggable.should respond_to(:save_cached_tag_list)
217
+ @taggable.should respond_to(:save_tags)
218
+ end
219
+
220
+ it "should generate a cached column checker for each tag type" do
221
+ CachedModel.should respond_to(:caching_tag_list?)
222
+ end
223
+
224
+ it 'should not have cached tags' do
225
+ @taggable.cached_tag_list.should be_blank
226
+ end
227
+
228
+ it 'should cache tags' do
229
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
230
+ @taggable.cached_tag_list.should == 'awesome, epic'
231
+ end
232
+
233
+ it 'should keep the cache' do
234
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
235
+ @taggable = CachedModel.find(@taggable)
236
+ @taggable.save!
237
+ @taggable.cached_tag_list.should == 'awesome, epic'
238
+ end
239
+
240
+ it 'should update the cache' do
241
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
242
+ @taggable.update_attributes(:tag_list => 'awesome')
243
+ @taggable.cached_tag_list.should == 'awesome'
244
+ end
245
+
246
+ it 'should remove the cache' do
247
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
248
+ @taggable.update_attributes(:tag_list => '')
249
+ @taggable.cached_tag_list.should be_blank
250
+ end
251
+
252
+ it 'should have a tag list' do
253
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
254
+ @taggable = CachedModel.find(@taggable.id)
255
+ @taggable.tag_list.sort.should == %w(awesome epic).sort
256
+ end
257
+
258
+ it 'should keep the tag list' do
259
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
260
+ @taggable = CachedModel.find(@taggable.id)
261
+ @taggable.save!
262
+ @taggable.tag_list.sort.should == %w(awesome epic).sort
263
+ end
264
+ end
218
265
 
219
266
  end
@@ -88,7 +88,7 @@ describe "Taggable" do
88
88
  frank = TaggableModel.create(:name => "Frank", :tag_list => "Ruby")
89
89
 
90
90
  Tag.find(:all).size.should == 1
91
- TaggableModel.tagged_with("ruby").all.should == TaggableModel.tagged_with("Ruby").all
91
+ TaggableModel.tagged_with("ruby").to_a.should == TaggableModel.tagged_with("Ruby").to_a
92
92
  end
93
93
 
94
94
  it "should be able to get tag counts on model as a whole" do
@@ -108,10 +108,20 @@ describe "Taggable" do
108
108
  TaggableModel.all_tag_counts.first.count.should == 3 # ruby
109
109
  end
110
110
 
111
- it "should not return read-only records" do
112
- TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
113
-
114
- TaggableModel.tagged_with("ruby").first.should_not be_readonly
111
+ if ActiveRecord::VERSION::MAJOR >= 3
112
+ it "should not return read-only records" do
113
+ TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
114
+ TaggableModel.tagged_with("ruby").first.should_not be_readonly
115
+ end
116
+ else
117
+ xit "should not return read-only records" do
118
+ # apparantly, there is no way to set readonly to false in a scope if joins are made
119
+ end
120
+
121
+ it "should be possible to return writable records" do
122
+ TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
123
+ TaggableModel.tagged_with("ruby").first(:readonly => false).should_not be_readonly
124
+ end
115
125
  end
116
126
 
117
127
  it "should be able to get scoped tag counts" do
@@ -145,9 +155,9 @@ describe "Taggable" do
145
155
  frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
146
156
  steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, ruby')
147
157
 
148
- TaggableModel.tagged_with("ruby", :order => 'taggable_models.name').all.should == [bob, frank, steve]
149
- TaggableModel.tagged_with("ruby, rails", :order => 'taggable_models.name').all.should == [bob, frank]
150
- TaggableModel.tagged_with(["ruby", "rails"], :order => 'taggable_models.name').all.should == [bob, frank]
158
+ TaggableModel.tagged_with("ruby", :order => 'taggable_models.name').to_a.should == [bob, frank, steve]
159
+ TaggableModel.tagged_with("ruby, rails", :order => 'taggable_models.name').to_a.should == [bob, frank]
160
+ TaggableModel.tagged_with(["ruby", "rails"], :order => 'taggable_models.name').to_a.should == [bob, frank]
151
161
  end
152
162
 
153
163
  it "should be able to find tagged with any tag" do
@@ -155,9 +165,9 @@ describe "Taggable" do
155
165
  frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
156
166
  steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, ruby')
157
167
 
158
- TaggableModel.tagged_with(["ruby", "java"], :order => 'taggable_models.name', :any => true).all.should == [bob, frank, steve]
159
- TaggableModel.tagged_with(["c++", "fitter"], :order => 'taggable_models.name', :any => true).all.should == [bob, steve]
160
- TaggableModel.tagged_with(["depressed", "css"], :order => 'taggable_models.name', :any => true).all.should == [bob, frank]
168
+ TaggableModel.tagged_with(["ruby", "java"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank, steve]
169
+ TaggableModel.tagged_with(["c++", "fitter"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, steve]
170
+ TaggableModel.tagged_with(["depressed", "css"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank]
161
171
  end
162
172
 
163
173
  it "should be able to find tagged on a custom tag context" do
@@ -166,7 +176,7 @@ describe "Taggable" do
166
176
  bob.tag_list_on(:rotors).should == ["spinning","jumping"]
167
177
  bob.save
168
178
 
169
- TaggableModel.tagged_with("spinning", :on => :rotors).all.should == [bob]
179
+ TaggableModel.tagged_with("spinning", :on => :rotors).to_a.should == [bob]
170
180
  end
171
181
 
172
182
  it "should be able to use named scopes to chain tag finds" do
@@ -175,10 +185,10 @@ describe "Taggable" do
175
185
  steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, python')
176
186
 
177
187
  # Let's only find those productive Rails developers
178
- TaggableModel.tagged_with('rails', :on => :skills, :order => 'taggable_models.name').all.should == [bob, frank]
179
- TaggableModel.tagged_with('happier', :on => :tags, :order => 'taggable_models.name').all.should == [bob, steve]
180
- TaggableModel.tagged_with('rails', :on => :skills).tagged_with('happier', :on => :tags).all.should == [bob]
181
- TaggableModel.tagged_with('rails').tagged_with('happier', :on => :tags).all.should == [bob]
188
+ TaggableModel.tagged_with('rails', :on => :skills, :order => 'taggable_models.name').to_a.should == [bob, frank]
189
+ TaggableModel.tagged_with('happier', :on => :tags, :order => 'taggable_models.name').to_a.should == [bob, steve]
190
+ TaggableModel.tagged_with('rails', :on => :skills).tagged_with('happier', :on => :tags).to_a.should == [bob]
191
+ TaggableModel.tagged_with('rails').tagged_with('happier', :on => :tags).to_a.should == [bob]
182
192
  end
183
193
 
184
194
  it "should be able to find tagged with only the matching tags" do
@@ -186,7 +196,7 @@ describe "Taggable" do
186
196
  frank = TaggableModel.create(:name => "Frank", :tag_list => "fitter, happier, inefficient")
187
197
  steve = TaggableModel.create(:name => 'Steve', :tag_list => "fitter, happier")
188
198
 
189
- TaggableModel.tagged_with("fitter, happier", :match_all => true).all.should == [steve]
199
+ TaggableModel.tagged_with("fitter, happier", :match_all => true).to_a.should == [steve]
190
200
  end
191
201
 
192
202
  it "should be able to find tagged with some excluded tags" do
@@ -194,7 +204,7 @@ describe "Taggable" do
194
204
  frank = TaggableModel.create(:name => "Frank", :tag_list => "happier")
195
205
  steve = TaggableModel.create(:name => 'Steve', :tag_list => "happier")
196
206
 
197
- TaggableModel.tagged_with("lazy", :exclude => true).all.should == [frank, steve]
207
+ TaggableModel.tagged_with("lazy", :exclude => true).to_a.should == [frank, steve]
198
208
  end
199
209
 
200
210
  it "should not create duplicate taggings" do
@@ -212,7 +222,7 @@ describe "Taggable" do
212
222
  end
213
223
 
214
224
  it "should return all column names joined for TaggableModel GROUP clause" do
215
- @taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.id, taggable_models.name, taggable_models.type, taggable_models.cached_tag_list"
225
+ @taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.id, taggable_models.name, taggable_models.type"
216
226
  end
217
227
  end
218
228
 
data/spec/bm.rb ADDED
@@ -0,0 +1,52 @@
1
+ require 'active_record'
2
+ require 'action_view'
3
+ require File.expand_path('../../lib/acts-as-taggable-on', __FILE__)
4
+
5
+ if defined?(ActiveRecord::Acts::TaggableOn)
6
+ ActiveRecord::Base.send :include, ActiveRecord::Acts::TaggableOn
7
+ ActiveRecord::Base.send :include, ActiveRecord::Acts::Tagger
8
+ ActionView::Base.send :include, TagsHelper if defined?(ActionView::Base)
9
+ end
10
+
11
+ TEST_DATABASE_FILE = File.join(File.dirname(__FILE__), '..', 'test.sqlite3')
12
+ File.unlink(TEST_DATABASE_FILE) if File.exist?(TEST_DATABASE_FILE)
13
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => TEST_DATABASE_FILE
14
+
15
+ ActiveRecord::Base.silence do
16
+ ActiveRecord::Migration.verbose = false
17
+ ActiveRecord::Schema.define :version => 0 do
18
+ create_table "taggings", :force => true do |t|
19
+ t.integer "tag_id", :limit => 11
20
+ t.integer "taggable_id", :limit => 11
21
+ t.string "taggable_type"
22
+ t.string "context"
23
+ t.datetime "created_at"
24
+ t.integer "tagger_id", :limit => 11
25
+ t.string "tagger_type"
26
+ end
27
+
28
+ add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
29
+ add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context"
30
+
31
+ create_table "tags", :force => true do |t|
32
+ t.string "name"
33
+ end
34
+
35
+ create_table :taggable_models, :force => true do |t|
36
+ t.column :name, :string
37
+ t.column :type, :string
38
+ t.column :cached_tag_list, :string
39
+ end
40
+ end
41
+
42
+ class TaggableModel < ActiveRecord::Base
43
+ acts_as_taggable
44
+ acts_as_taggable_on :languages
45
+ acts_as_taggable_on :skills
46
+ acts_as_taggable_on :needs, :offerings
47
+ end
48
+ end
49
+
50
+ puts Benchmark.measure {
51
+ 1000.times { TaggableModel.create :tag_list => "awesome, epic, neat" }
52
+ }
data/spec/models.rb CHANGED
@@ -5,6 +5,10 @@ class TaggableModel < ActiveRecord::Base
5
5
  acts_as_taggable_on :needs, :offerings
6
6
  end
7
7
 
8
+ class CachedModel < ActiveRecord::Base
9
+ acts_as_taggable
10
+ end
11
+
8
12
  class OtherTaggableModel < ActiveRecord::Base
9
13
  acts_as_taggable_on :tags, :languages
10
14
  acts_as_taggable_on :needs, :offerings
@@ -24,9 +28,9 @@ end
24
28
  class UntaggableModel < ActiveRecord::Base
25
29
  end
26
30
 
27
- if ActiveRecord::VERSION::MAJOR < 3
28
- [TaggableModel, OtherTaggableModel, InheritingTaggableModel,
29
- AlteredInheritingTaggableModel, TaggableUser, UntaggableModel].each do |klass|
30
- klass.send(:include, ActsAsTaggableOn::ActiveRecord::Backports)
31
- end
32
- end
31
+ # if ActiveRecord::VERSION::MAJOR < 3
32
+ # [TaggableModel, CachedModel, OtherTaggableModel, InheritingTaggableModel,
33
+ # AlteredInheritingTaggableModel, TaggableUser, UntaggableModel].each do |klass|
34
+ # klass.send(:include, ActsAsTaggableOn::ActiveRecord::Backports)
35
+ # end
36
+ # end
data/spec/schema.rb CHANGED
@@ -19,6 +19,11 @@ ActiveRecord::Schema.define :version => 0 do
19
19
  create_table :taggable_models, :force => true do |t|
20
20
  t.column :name, :string
21
21
  t.column :type, :string
22
+ end
23
+
24
+ create_table :cached_models, :force => true do |t|
25
+ t.column :name, :string
26
+ t.column :type, :string
22
27
  t.column :cached_tag_list, :string
23
28
  end
24
29
 
@@ -29,7 +34,6 @@ ActiveRecord::Schema.define :version => 0 do
29
34
  create_table :other_taggable_models, :force => true do |t|
30
35
  t.column :name, :string
31
36
  t.column :type, :string
32
- #t.column :cached_tag_list, :string
33
37
  end
34
38
 
35
39
  create_table :untaggable_models, :force => true do |t|
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts-as-taggable-on
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre4
4
+ prerelease: true
5
+ segments:
6
+ - 2
7
+ - 0
8
+ - 0
9
+ - pre5
10
+ version: 2.0.0.pre5
5
11
  platform: ruby
6
12
  authors:
7
13
  - Michael Bleigh
@@ -9,7 +15,7 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-03-22 00:00:00 +01:00
18
+ date: 2010-03-25 00:00:00 +01:00
13
19
  default_executable:
14
20
  dependencies: []
15
21
 
@@ -30,16 +36,15 @@ files:
30
36
  - VERSION
31
37
  - lib/acts-as-taggable-on.rb
32
38
  - lib/acts_as_taggable_on/acts_as_taggable_on.rb
33
- - lib/acts_as_taggable_on/acts_as_taggable_on/aggregate.rb
34
39
  - lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb
40
+ - lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb
35
41
  - lib/acts_as_taggable_on/acts_as_taggable_on/core.rb
42
+ - lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb
36
43
  - lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb
37
44
  - lib/acts_as_taggable_on/acts_as_taggable_on/related.rb
38
45
  - lib/acts_as_taggable_on/acts_as_tagger.rb
39
46
  - lib/acts_as_taggable_on/compatibility/Gemfile
40
47
  - lib/acts_as_taggable_on/compatibility/active_record_backports.rb
41
- - lib/acts_as_taggable_on/compatibility/tag.rb
42
- - lib/acts_as_taggable_on/compatibility/tagging.rb
43
48
  - lib/acts_as_taggable_on/tag.rb
44
49
  - lib/acts_as_taggable_on/tag_list.rb
45
50
  - lib/acts_as_taggable_on/tagging.rb
@@ -55,6 +60,7 @@ files:
55
60
  - spec/acts_as_taggable_on/tagger_spec.rb
56
61
  - spec/acts_as_taggable_on/tagging_spec.rb
57
62
  - spec/acts_as_taggable_on/tags_helper_spec.rb
63
+ - spec/bm.rb
58
64
  - spec/models.rb
59
65
  - spec/schema.rb
60
66
  - spec/spec.opts
@@ -72,18 +78,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
72
78
  requirements:
73
79
  - - ">="
74
80
  - !ruby/object:Gem::Version
81
+ segments:
82
+ - 0
75
83
  version: "0"
76
- version:
77
84
  required_rubygems_version: !ruby/object:Gem::Requirement
78
85
  requirements:
79
86
  - - ">"
80
87
  - !ruby/object:Gem::Version
88
+ segments:
89
+ - 1
90
+ - 3
91
+ - 1
81
92
  version: 1.3.1
82
- version:
83
93
  requirements: []
84
94
 
85
95
  rubyforge_project:
86
- rubygems_version: 1.3.5
96
+ rubygems_version: 1.3.6
87
97
  signing_key:
88
98
  specification_version: 3
89
99
  summary: ActsAsTaggableOn is a tagging plugin for Rails that provides multiple tagging contexts on a single model.
@@ -96,6 +106,7 @@ test_files:
96
106
  - spec/acts_as_taggable_on/tagger_spec.rb
97
107
  - spec/acts_as_taggable_on/tagging_spec.rb
98
108
  - spec/acts_as_taggable_on/tags_helper_spec.rb
109
+ - spec/bm.rb
99
110
  - spec/models.rb
100
111
  - spec/schema.rb
101
112
  - spec/spec_helper.rb
@@ -1,3 +0,0 @@
1
- class Tag < ActiveRecord::Base
2
- include ActsAsTaggableOn::ActiveRecord::Backports
3
- end
@@ -1,3 +0,0 @@
1
- class Tagging < ActiveRecord::Base
2
- include ActsAsTaggableOn::ActiveRecord::Backports
3
- end