litmus-acts-as-taggable-on 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
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