litmus-acts-as-taggable-on 2.0.4

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.
Files changed (39) hide show
  1. data/CHANGELOG +25 -0
  2. data/Gemfile +6 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +227 -0
  5. data/Rakefile +59 -0
  6. data/VERSION +1 -0
  7. data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +7 -0
  8. data/generators/acts_as_taggable_on_migration/templates/migration.rb +29 -0
  9. data/lib/acts-as-taggable-on.rb +32 -0
  10. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +53 -0
  11. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +53 -0
  12. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +130 -0
  13. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +237 -0
  14. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +101 -0
  15. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +65 -0
  16. data/lib/acts_as_taggable_on/acts_as_tagger.rb +67 -0
  17. data/lib/acts_as_taggable_on/compatibility/Gemfile +6 -0
  18. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +17 -0
  19. data/lib/acts_as_taggable_on/tag.rb +67 -0
  20. data/lib/acts_as_taggable_on/tag_list.rb +95 -0
  21. data/lib/acts_as_taggable_on/tagging.rb +23 -0
  22. data/lib/acts_as_taggable_on/tags_helper.rb +17 -0
  23. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +31 -0
  24. data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +28 -0
  25. data/rails/init.rb +1 -0
  26. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +266 -0
  27. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +114 -0
  28. data/spec/acts_as_taggable_on/tag_list_spec.rb +70 -0
  29. data/spec/acts_as_taggable_on/tag_spec.rb +115 -0
  30. data/spec/acts_as_taggable_on/taggable_spec.rb +306 -0
  31. data/spec/acts_as_taggable_on/tagger_spec.rb +91 -0
  32. data/spec/acts_as_taggable_on/tagging_spec.rb +31 -0
  33. data/spec/acts_as_taggable_on/tags_helper_spec.rb +28 -0
  34. data/spec/bm.rb +52 -0
  35. data/spec/models.rb +31 -0
  36. data/spec/schema.rb +43 -0
  37. data/spec/spec.opts +2 -0
  38. data/spec/spec_helper.rb +53 -0
  39. metadata +115 -0
@@ -0,0 +1,53 @@
1
+ module ActsAsTaggableOn::Taggable
2
+ module Cache
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") }
6
+
7
+ base.send :include, ActsAsTaggableOn::Taggable::Cache::InstanceMethods
8
+ base.extend ActsAsTaggableOn::Taggable::Cache::ClassMethods
9
+
10
+ base.class_eval do
11
+ before_save :save_cached_tag_list
12
+ end
13
+
14
+ base.intialize_acts_as_taggable_on_cache
15
+ end
16
+
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
+
33
+ def caching_tag_list_on?(context)
34
+ column_names.include?("cached_#{context.to_s.singularize}_list")
35
+ end
36
+ end
37
+
38
+ module InstanceMethods
39
+ def save_cached_tag_list
40
+ tag_types.map(&:to_s).each do |tag_type|
41
+ if self.class.send("caching_#{tag_type.singularize}_list?")
42
+ if tag_list_cache_set_on(tag_type)
43
+ list = tag_list_cache_on(tag_type.singularize).to_a.flatten.compact.join(', ')
44
+ self["cached_#{tag_type.singularize}_list"] = list
45
+ end
46
+ end
47
+ end
48
+
49
+ true
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,130 @@
1
+ module ActsAsTaggableOn::Taggable
2
+ module Collection
3
+ def self.included(base)
4
+ base.send :include, ActsAsTaggableOn::Taggable::Collection::InstanceMethods
5
+ base.extend ActsAsTaggableOn::Taggable::Collection::ClassMethods
6
+ base.initialize_acts_as_taggable_on_collection
7
+ end
8
+
9
+ module ClassMethods
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
16
+
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}))
39
+ end
40
+
41
+ ##
42
+ # Calculate the tag counts for all tags.
43
+ #
44
+ # @param [Hash] options Options:
45
+ # * :start_at - Restrict the tags to those created after a certain time
46
+ # * :end_at - Restrict the tags to those created before a certain time
47
+ # * :conditions - A piece of SQL conditions to add to the query
48
+ # * :limit - The maximum number of tags to return
49
+ # * :order - A piece of SQL to order by. Eg 'tags.count desc' or 'taggings.created_at desc'
50
+ # * :at_least - Exclude tags with a frequency less than the given value
51
+ # * :at_most - Exclude tags with a frequency greater than the given value
52
+ # * :on - Scope the find to only include a certain context
53
+ def all_tag_counts(options = {})
54
+ options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :on, :id
55
+
56
+ scope = if ActiveRecord::VERSION::MAJOR >= 3
57
+ {}
58
+ else
59
+ scope(:find) || {}
60
+ end
61
+
62
+ ## Generate conditions:
63
+ options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
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]
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]
70
+
71
+ conditions = [
72
+ taggable_conditions,
73
+ options[:conditions],
74
+ scope[:conditions],
75
+ start_at_conditions,
76
+ end_at_conditions
77
+ ].compact.reverse
78
+
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]
82
+
83
+ taggable_join = "INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
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
+
86
+ joins = [
87
+ tagging_join,
88
+ taggable_join,
89
+ scope[:joins]
90
+ ].compact.reverse
91
+
92
+
93
+ ## Generate scope:
94
+ scope = Tag.scoped(:select => "#{Tag.table_name}.*, COUNT(*) AS count").order(options[:order]).limit(options[:limit])
95
+
96
+ # Joins and conditions
97
+ joins.each { |join| scope = scope.joins(join) }
98
+ conditions.each { |condition| scope = scope.where(condition) }
99
+
100
+ # GROUP BY and HAVING clauses:
101
+ at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
102
+ at_most = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
103
+ having = [at_least, at_most].compact.join(' AND ')
104
+
105
+ if ActiveRecord::VERSION::MAJOR >= 3
106
+ # Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore:
107
+ scoped_select = "#{table_name}.#{primary_key}"
108
+ scope = scope.where("#{Tagging.table_name}.taggable_id IN(#{select(scoped_select).to_sql})")
109
+
110
+ # We have having() in RoR 3.0 so use it:
111
+ having = having.blank? ? "COUNT(*) > 0" : "COUNT(*) > 0 AND #{having}"
112
+ scope = scope.group(grouped_column_names_for(Tag)).having(having)
113
+ else
114
+ # Having is not available in 2.3.x:
115
+ group_by = "#{grouped_column_names_for(Tag)} HAVING COUNT(*) > 0"
116
+ group_by << " AND #{having}" unless having.blank?
117
+ scope = scope.group(group_by)
118
+ end
119
+
120
+ scope
121
+ end
122
+ end
123
+
124
+ module InstanceMethods
125
+ def tag_counts_on(context, options={})
126
+ self.class.tag_counts_on(context, options.merge(:id => id))
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,237 @@
1
+ module ActsAsTaggableOn::Taggable
2
+ module Core
3
+ def self.included(base)
4
+ base.send :include, ActsAsTaggableOn::Taggable::Core::InstanceMethods
5
+ base.extend ActsAsTaggableOn::Taggable::Core::ClassMethods
6
+
7
+ base.class_eval do
8
+ attr_writer :custom_contexts
9
+ after_save :save_tags
10
+ end
11
+
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 |tags_type|
18
+ tag_type = tags_type.to_s.singularize
19
+ context_taggings = "#{tag_type}_taggings".to_sym
20
+ context_tags = tags_type.to_sym
21
+
22
+ class_eval do
23
+ has_many context_taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "Tagging",
24
+ :conditions => ['#{Tagging.table_name}.tagger_id IS NULL AND #{Tagging.table_name}.context = ?', tags_type]
25
+ has_many context_tags, :through => context_taggings, :source => :tag
26
+ end
27
+
28
+ class_eval %(
29
+ def #{tag_type}_list
30
+ tag_list_on('#{tags_type}')
31
+ end
32
+
33
+ def #{tag_type}_list=(new_tags)
34
+ set_tag_list_on('#{tags_type}', new_tags)
35
+ end
36
+
37
+ def all_#{tags_type}_list
38
+ all_tags_list_on('#{tags_type}')
39
+ end
40
+ )
41
+ end
42
+ end
43
+
44
+ def acts_as_taggable_on(*args)
45
+ super(*args)
46
+ initialize_acts_as_taggable_on_core
47
+ end
48
+
49
+ # all column names are necessary for PostgreSQL group clause
50
+ def grouped_column_names_for(object)
51
+ object.column_names.map { |column| "#{object.table_name}.#{column}" }.join(", ")
52
+ end
53
+
54
+ ##
55
+ # Return a scope of objects that are tagged with the specified tags.
56
+ #
57
+ # @param tags The tags that we want to query for
58
+ # @param [Hash] options A hash of options to alter you query:
59
+ # * <tt>:exclude</tt> - if set to true, return objects that are *NOT* tagged with the specified tags
60
+ # * <tt>:any</tt> - if set to true, return objects that are tagged with *ANY* of the specified tags
61
+ # * <tt>:match_all</tt> - if set to true, return objects that are *ONLY* tagged with the specified tags
62
+ #
63
+ # Example:
64
+ # User.tagged_with("awesome", "cool") # Users that are tagged with awesome and cool
65
+ # User.tagged_with("awesome", "cool", :exclude => true) # Users that are not tagged with awesome or cool
66
+ # User.tagged_with("awesome", "cool", :any => true) # Users that are tagged with awesome or cool
67
+ # User.tagged_with("awesome", "cool", :match_all => true) # Users that are tagged with just awesome and cool
68
+ def tagged_with(tags, options = {})
69
+ tag_list = TagList.from(tags)
70
+
71
+ return {} if tag_list.empty?
72
+
73
+ joins = []
74
+ conditions = []
75
+
76
+ context = options.delete(:on)
77
+
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)})"
81
+
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)})"
85
+
86
+ else
87
+ tags = Tag.named_any(tag_list)
88
+ return where("1 = 0") unless tags.length == tag_list.length
89
+
90
+ tags.each do |tag|
91
+ safe_tag = tag.name.gsub(/[^a-zA-Z0-9]/, '')
92
+ prefix = "#{safe_tag}_#{rand(1024)}"
93
+
94
+ taggings_alias = "#{table_name}_taggings_#{prefix}"
95
+
96
+ tagging_join = "JOIN #{Tagging.table_name} #{taggings_alias}" +
97
+ " ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
98
+ " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}" +
99
+ " AND #{taggings_alias}.tag_id = #{tag.id}"
100
+ tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
101
+
102
+ joins << tagging_join
103
+ end
104
+ end
105
+
106
+ taggings_alias, tags_alias = "#{table_name}_taggings_group", "#{table_name}_tags_group"
107
+
108
+ if options.delete(:match_all)
109
+ joins << "LEFT OUTER JOIN #{Tagging.table_name} #{taggings_alias}" +
110
+ " ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
111
+ " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
112
+
113
+ group = "#{grouped_column_names_for(self)} HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
114
+ end
115
+
116
+
117
+ scoped(:joins => joins.join(" "),
118
+ :group => group,
119
+ :conditions => conditions.join(" AND "),
120
+ :readonly => false)
121
+ end
122
+
123
+ def is_taggable?
124
+ true
125
+ end
126
+ end
127
+
128
+ module InstanceMethods
129
+ # all column names are necessary for PostgreSQL group clause
130
+ def grouped_column_names_for(object)
131
+ self.class.grouped_column_names_for(object)
132
+ end
133
+
134
+ def custom_contexts
135
+ @custom_contexts ||= []
136
+ end
137
+
138
+ def is_taggable?
139
+ self.class.is_taggable?
140
+ end
141
+
142
+ def add_custom_context(value)
143
+ custom_contexts << value.to_s unless custom_contexts.include?(value.to_s) or self.class.tag_types.map(&:to_s).include?(value.to_s)
144
+ end
145
+
146
+ def cached_tag_list_on(context)
147
+ self["cached_#{context.to_s.singularize}_list"]
148
+ end
149
+
150
+ def tag_list_cache_set_on(context)
151
+ variable_name = "@#{context.to_s.singularize}_list"
152
+ !instance_variable_get(variable_name).nil?
153
+ end
154
+
155
+ def tag_list_cache_on(context)
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)))
158
+ end
159
+
160
+ def tag_list_on(context)
161
+ add_custom_context(context)
162
+ tag_list_cache_on(context)
163
+ end
164
+
165
+ def all_tags_list_on(context)
166
+ variable_name = "@all_#{context.to_s.singularize}_list"
167
+ return instance_variable_get(variable_name) if instance_variable_get(variable_name)
168
+
169
+ instance_variable_set(variable_name, TagList.new(all_tags_on(context).map(&:name)).freeze)
170
+ end
171
+
172
+ ##
173
+ # Returns all tags of a given context
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
177
+ end
178
+
179
+ ##
180
+ # Returns all tags that are not owned of a given context
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
183
+ end
184
+
185
+ def set_tag_list_on(context, new_list)
186
+ add_custom_context(context)
187
+
188
+ variable_name = "@#{context.to_s.singularize}_list"
189
+ instance_variable_set(variable_name, TagList.from(new_list))
190
+ end
191
+
192
+ def tagging_contexts
193
+ custom_contexts + self.class.tag_types.map(&:to_s)
194
+ end
195
+
196
+ def reload
197
+ self.class.tag_types.each do |context|
198
+ instance_variable_set("@#{context.to_s.singularize}_list", nil)
199
+ instance_variable_set("@all_#{context.to_s.singularize}_list", nil)
200
+ end
201
+
202
+ super
203
+ end
204
+
205
+ def save_tags
206
+ tagging_contexts.each do |context|
207
+ next unless tag_list_cache_set_on(context)
208
+
209
+ tag_list = tag_list_cache_on(context).uniq
210
+
211
+ # Find existing tags or create non-existing tags:
212
+ tag_list = Tag.find_or_create_all_with_like_by_name(tag_list)
213
+
214
+ current_tags = tags_on(context)
215
+ old_tags = current_tags - tag_list
216
+ new_tags = tag_list - current_tags
217
+
218
+ # Find taggings to remove:
219
+ old_taggings = taggings.where(:tagger_type => nil, :tagger_id => nil,
220
+ :context => context.to_s, :tag_id => old_tags).all
221
+
222
+ if old_taggings.present?
223
+ # Destroy old taggings:
224
+ Tagging.destroy_all :id => old_taggings.map(&:id)
225
+ end
226
+
227
+ # Create new taggings:
228
+ new_tags.each do |tag|
229
+ taggings.create!(:tag_id => tag.id, :context => context.to_s, :taggable => self)
230
+ end
231
+ end
232
+
233
+ true
234
+ end
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,101 @@
1
+ module ActsAsTaggableOn::Taggable
2
+ module Ownership
3
+ def self.included(base)
4
+ base.send :include, ActsAsTaggableOn::Taggable::Ownership::InstanceMethods
5
+ base.extend ActsAsTaggableOn::Taggable::Ownership::ClassMethods
6
+
7
+ base.class_eval do
8
+ after_save :save_owned_tags
9
+ end
10
+
11
+ base.initialize_acts_as_taggable_on_ownership
12
+ end
13
+
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
29
+ end
30
+
31
+ module InstanceMethods
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
36
+ end
37
+
38
+ def cached_owned_tag_list_on(context)
39
+ variable_name = "@owned_#{context}_list"
40
+ cache = instance_variable_get(variable_name) || instance_variable_set(variable_name, {})
41
+ end
42
+
43
+ def owner_tag_list_on(owner, context)
44
+ add_custom_context(context)
45
+
46
+ cache = cached_owned_tag_list_on(context)
47
+ cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
48
+
49
+ cache[owner] ||= TagList.new(*owner_tags_on(owner, context).map(&:name))
50
+ end
51
+
52
+ def set_owner_tag_list_on(owner, context, new_list)
53
+ add_custom_context(context)
54
+
55
+ cache = cached_owned_tag_list_on(context)
56
+ cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
57
+
58
+ cache[owner] = TagList.from(new_list)
59
+ end
60
+
61
+ def reload
62
+ self.class.tag_types.each do |context|
63
+ instance_variable_set("@owned_#{context}_list", nil)
64
+ end
65
+
66
+ super
67
+ end
68
+
69
+ def save_owned_tags
70
+ tagging_contexts.each do |context|
71
+ cached_owned_tag_list_on(context).each do |owner, tag_list|
72
+ # Find existing tags or create non-existing tags:
73
+ tag_list = Tag.find_or_create_all_with_like_by_name(tag_list.uniq)
74
+
75
+ owned_tags = owner_tags_on(owner, context)
76
+ old_tags = owned_tags - tag_list
77
+ new_tags = tag_list - owned_tags
78
+
79
+ # Find all taggings that belong to the taggable (self), are owned by the owner,
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
+ :tagger_type => owner.class.to_s, :tagger_id => owner.id,
83
+ :tag_id => old_tags, :context => context).all
84
+
85
+ if old_taggings.present?
86
+ # Destroy old taggings:
87
+ Tagging.destroy_all(:id => old_taggings.map(&:id))
88
+ end
89
+
90
+ # Create new taggings:
91
+ new_tags.each do |tag|
92
+ taggings.create!(:tag_id => tag.id, :context => context.to_s, :tagger => owner, :taggable => self)
93
+ end
94
+ end
95
+ end
96
+
97
+ true
98
+ end
99
+ end
100
+ end
101
+ end