yury-acts-as-taggable-on 1.0.3 → 1.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.
- data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +37 -12
- data/lib/acts_as_taggable_on/acts_as_tagger.rb +2 -2
- data/lib/acts_as_taggable_on/tag.rb +24 -3
- data/lib/acts_as_taggable_on/tag_list.rb +6 -2
- data/lib/acts_as_taggable_on/tagging.rb +2 -0
- data/rails/init.rb +1 -3
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +19 -0
- data/spec/acts_as_taggable_on/taggable_spec.rb +45 -0
- metadata +5 -3
@@ -19,9 +19,11 @@ module ActiveRecord
|
|
19
19
|
args.compact! if args
|
20
20
|
for tag_type in args
|
21
21
|
tag_type = tag_type.to_s
|
22
|
+
# use aliased_join_table_name for context condition so that sphix can join multiple
|
23
|
+
# tag references from same model without getting an ambiguous column error
|
22
24
|
self.class_eval do
|
23
25
|
has_many "#{tag_type.singularize}_taggings".to_sym, :as => :taggable, :dependent => :destroy,
|
24
|
-
:include => :tag, :conditions => [
|
26
|
+
:include => :tag, :conditions => ['#{aliased_join_table_name rescue "taggings"}.context = ?',tag_type], :class_name => "Tagging"
|
25
27
|
has_many "#{tag_type}".to_sym, :through => "#{tag_type.singularize}_taggings".to_sym, :source => :tag
|
26
28
|
end
|
27
29
|
|
@@ -45,6 +47,14 @@ module ActiveRecord
|
|
45
47
|
def #{tag_type.singularize}_list=(new_tags)
|
46
48
|
set_tag_list_on('#{tag_type}',new_tags)
|
47
49
|
end
|
50
|
+
|
51
|
+
def #{tag_type.singularize}_list_changed=(changed)
|
52
|
+
instance_variable_set("@#{tag_type.singularize}_list_changed", changed)
|
53
|
+
end
|
54
|
+
|
55
|
+
def #{tag_type.singularize}_list_changed?
|
56
|
+
instance_variable_get("@#{tag_type.singularize}_list_changed") || false
|
57
|
+
end
|
48
58
|
|
49
59
|
def #{tag_type.singularize}_counts(options = {})
|
50
60
|
tag_counts_on('#{tag_type}',options)
|
@@ -183,7 +193,7 @@ module ActiveRecord
|
|
183
193
|
conditions = merge_conditions(conditions, scope[:conditions]) if scope
|
184
194
|
|
185
195
|
joins = ["LEFT OUTER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
|
186
|
-
joins << sanitize_sql(["AND context = ?",options.delete(:on).to_s]) unless options[:on].nil?
|
196
|
+
joins << sanitize_sql(["AND #{Tagging.table_name}.context = ?",options.delete(:on).to_s]) unless options[:on].nil?
|
187
197
|
joins << "LEFT OUTER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
|
188
198
|
joins << scope[:joins] if scope && scope[:joins]
|
189
199
|
|
@@ -226,21 +236,27 @@ module ActiveRecord
|
|
226
236
|
def tag_list_on(context, owner=nil)
|
227
237
|
var_name = context.to_s.singularize + "_list"
|
228
238
|
add_custom_context(context)
|
229
|
-
|
230
|
-
|
231
|
-
if
|
232
|
-
|
233
|
-
|
234
|
-
|
239
|
+
list = instance_variable_get("@#{var_name}")
|
240
|
+
|
241
|
+
if list.nil?
|
242
|
+
if !owner && self.class.caching_tag_list_on?(context) and !(cached_value = cached_tag_list_on(context)).nil?
|
243
|
+
list = instance_variable_set("@#{var_name}", TagList.from(self["cached_#{var_name}"]))
|
244
|
+
else
|
245
|
+
list = instance_variable_set("@#{var_name}", TagList.new(*tags_on(context, owner).map(&:name)))
|
246
|
+
end
|
235
247
|
end
|
248
|
+
|
249
|
+
list.context = context.to_s
|
250
|
+
list.taggable = self
|
251
|
+
list
|
236
252
|
end
|
237
253
|
|
238
254
|
def tags_on(context, owner=nil)
|
239
255
|
if owner
|
240
|
-
opts = {:conditions => ["
|
256
|
+
opts = {:conditions => ["context = ? AND tagger_id = ? AND tagger_type = ?",
|
241
257
|
context.to_s, owner.id, owner.class.to_s]}
|
242
258
|
else
|
243
|
-
opts = {:conditions => ["
|
259
|
+
opts = {:conditions => ["context = ?", context.to_s]}
|
244
260
|
end
|
245
261
|
base_tags.find(:all, opts)
|
246
262
|
end
|
@@ -250,8 +266,13 @@ module ActiveRecord
|
|
250
266
|
end
|
251
267
|
|
252
268
|
def set_tag_list_on(context,new_list, tagger=nil)
|
269
|
+
old_list = instance_variable_get("@#{context.to_s.singularize}_list")
|
253
270
|
instance_variable_set("@#{context.to_s.singularize}_list", TagList.from_owner(tagger, new_list))
|
254
271
|
add_custom_context(context)
|
272
|
+
|
273
|
+
if old_list != instance_variable_get("@#{context.to_s.singularize}_list")
|
274
|
+
instance_variable_set("@#{context.to_s.singularize}_list_changed", true)
|
275
|
+
end
|
255
276
|
end
|
256
277
|
|
257
278
|
def tag_counts_on(context,options={})
|
@@ -266,10 +287,12 @@ module ActiveRecord
|
|
266
287
|
|
267
288
|
def related_search_options(context, klass, options = {})
|
268
289
|
tags_to_find = self.tags_on(context).collect { |t| t.name }
|
290
|
+
|
291
|
+
exclude_self = "#{klass.table_name}.id != #{self.id} AND" if self.class == klass
|
269
292
|
|
270
293
|
{ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
|
271
294
|
:from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
|
272
|
-
:conditions => ["#{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],
|
295
|
+
: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],
|
273
296
|
:group => "#{klass.table_name}.id",
|
274
297
|
:order => "count DESC"
|
275
298
|
}.update(options)
|
@@ -283,7 +306,7 @@ module ActiveRecord
|
|
283
306
|
end
|
284
307
|
end
|
285
308
|
|
286
|
-
def save_tags
|
309
|
+
def save_tags
|
287
310
|
(custom_contexts + self.class.tag_types.map(&:to_s)).each do |tag_type|
|
288
311
|
next unless instance_variable_get("@#{tag_type.singularize}_list")
|
289
312
|
owner = instance_variable_get("@#{tag_type.singularize}_list").owner
|
@@ -298,6 +321,8 @@ module ActiveRecord
|
|
298
321
|
:taggable => self, :tagger => owner)
|
299
322
|
end
|
300
323
|
end
|
324
|
+
|
325
|
+
instance_variable_set("@#{tag_type.singularize}_list_changed", false)
|
301
326
|
end
|
302
327
|
|
303
328
|
true
|
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
def acts_as_tagger(opts={})
|
10
10
|
has_many :owned_taggings, opts.merge(:as => :tagger, :dependent => :destroy,
|
11
11
|
:include => :tag, :class_name => "Tagging")
|
12
|
-
has_many :owned_tags, :through => :owned_taggings, :source => :tag
|
12
|
+
has_many :owned_tags, :through => :owned_taggings, :source => :tag, :uniq => true
|
13
13
|
include ActiveRecord::Acts::Tagger::InstanceMethods
|
14
14
|
extend ActiveRecord::Acts::Tagger::SingletonMethods
|
15
15
|
end
|
@@ -49,4 +49,4 @@ module ActiveRecord
|
|
49
49
|
|
50
50
|
end
|
51
51
|
end
|
52
|
-
end
|
52
|
+
end
|
@@ -1,12 +1,29 @@
|
|
1
|
-
class Tag < ActiveRecord::Base
|
1
|
+
class Tag < ActiveRecord::Base
|
2
2
|
has_many :taggings
|
3
3
|
|
4
4
|
validates_presence_of :name
|
5
5
|
validates_uniqueness_of :name
|
6
6
|
|
7
|
+
def self.cleanup(name)
|
8
|
+
n = name.to_s.downcase.gsub(/[^a-z0-9_-]+/, '').strip
|
9
|
+
n.blank? ? nil : n
|
10
|
+
end
|
11
|
+
|
12
|
+
def name=(name)
|
13
|
+
#strip any non-alphanumeric and downcase
|
14
|
+
self["name"] = Tag.cleanup(name)
|
15
|
+
end
|
16
|
+
|
7
17
|
# LIKE is used for cross-database case-insensitivity
|
8
18
|
def self.find_or_create_with_like_by_name(name)
|
9
|
-
|
19
|
+
name = Tag.cleanup(name)
|
20
|
+
|
21
|
+
begin
|
22
|
+
find(:first, :conditions => ["name LIKE ?", name]) || create(:name => name)
|
23
|
+
rescue ActiveRecord::StatementInvalid
|
24
|
+
# If we can't insert the tag (perhaps a dupe), try to refetch.
|
25
|
+
find(:first, :conditions => ["name LIKE ?", name])
|
26
|
+
end
|
10
27
|
end
|
11
28
|
|
12
29
|
def ==(object)
|
@@ -17,7 +34,11 @@ class Tag < ActiveRecord::Base
|
|
17
34
|
name
|
18
35
|
end
|
19
36
|
|
20
|
-
def count
|
37
|
+
def count
|
21
38
|
read_attribute(:count).to_i
|
22
39
|
end
|
40
|
+
|
41
|
+
def self.per_page
|
42
|
+
10
|
43
|
+
end
|
23
44
|
end
|
@@ -7,6 +7,8 @@ class TagList < Array
|
|
7
7
|
end
|
8
8
|
|
9
9
|
attr_accessor :owner
|
10
|
+
attr :context, true
|
11
|
+
attr :taggable, true
|
10
12
|
|
11
13
|
# Add tags to the tag_list. Duplicate or blank tags will be ignored.
|
12
14
|
#
|
@@ -16,6 +18,7 @@ class TagList < Array
|
|
16
18
|
#
|
17
19
|
# tag_list.add("Fun, Happy", :parse => true)
|
18
20
|
def add(*names)
|
21
|
+
taggable.send("#{context.singularize}_list_changed=", true) if (taggable && context)
|
19
22
|
extract_and_apply_options!(names)
|
20
23
|
concat(names)
|
21
24
|
clean!
|
@@ -30,6 +33,7 @@ class TagList < Array
|
|
30
33
|
#
|
31
34
|
# tag_list.remove("Sad, Lonely", :parse => true)
|
32
35
|
def remove(*names)
|
36
|
+
taggable.send("#{context.singularize}_list_changed=", true) if (taggable && context)
|
33
37
|
extract_and_apply_options!(names)
|
34
38
|
delete_if { |name| names.include?(name) }
|
35
39
|
self
|
@@ -77,8 +81,8 @@ class TagList < Array
|
|
77
81
|
string = string.to_s.dup
|
78
82
|
|
79
83
|
# Parse the quoted tags
|
80
|
-
string.gsub!(/"(.*?)"\s*#{delimiter}?\s*/) { tag_list
|
81
|
-
string.gsub!(/'(.*?)'\s*#{delimiter}?\s*/) { tag_list
|
84
|
+
string.gsub!(/"(.*?)"\s*#{delimiter}?\s*/) { tag_list.add $1; "" }
|
85
|
+
string.gsub!(/'(.*?)'\s*#{delimiter}?\s*/) { tag_list.add $1; "" }
|
82
86
|
|
83
87
|
tag_list.add(string.split(delimiter))
|
84
88
|
end
|
data/rails/init.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
require 'acts-as-taggable-on'
|
2
2
|
|
3
3
|
ActiveRecord::Base.send :include, ActiveRecord::Acts::TaggableOn
|
4
|
-
ActiveRecord::Base.send :include, ActiveRecord::Acts::Tagger
|
5
|
-
|
6
|
-
RAILS_DEFAULT_LOGGER.info "** acts_as_taggable_on: initialized properly."
|
4
|
+
ActiveRecord::Base.send :include, ActiveRecord::Acts::Tagger
|
@@ -40,6 +40,11 @@ describe "Acts As Taggable On" do
|
|
40
40
|
@taggable.should respond_to(:tag_list, :skill_list, :language_list)
|
41
41
|
@taggable.should respond_to(:tag_list=, :skill_list=, :language_list=)
|
42
42
|
end
|
43
|
+
|
44
|
+
it "should generate a dirty field tracking accessor/setter for each tag type" do
|
45
|
+
@taggable.should respond_to(:tag_list_changed?, :skill_list_changed?, :language_list_changed?)
|
46
|
+
@taggable.should respond_to(:tag_list_changed=, :skill_list_changed=, :language_list_changed=)
|
47
|
+
end
|
43
48
|
end
|
44
49
|
|
45
50
|
describe "Single Table Inheritance" do
|
@@ -103,6 +108,20 @@ describe "Acts As Taggable On" do
|
|
103
108
|
taggable1.find_related_tags_for(OtherTaggableModel).should include(taggable3)
|
104
109
|
taggable1.find_related_tags_for(OtherTaggableModel).should_not include(taggable2)
|
105
110
|
end
|
111
|
+
|
112
|
+
it "should not include the object itself in the list of related objects" do
|
113
|
+
taggable1 = TaggableModel.create!(:name => "Taggable 1")
|
114
|
+
taggable2 = TaggableModel.create!(:name => "Taggable 2")
|
115
|
+
|
116
|
+
taggable1.tag_list = "one"
|
117
|
+
taggable1.save
|
118
|
+
|
119
|
+
taggable2.tag_list = "one, two"
|
120
|
+
taggable2.save
|
121
|
+
|
122
|
+
taggable1.find_related_tags.should include(taggable2)
|
123
|
+
taggable1.find_related_tags.should_not include(taggable1)
|
124
|
+
end
|
106
125
|
end
|
107
126
|
|
108
127
|
describe 'Tagging Contexts' do
|
@@ -106,6 +106,51 @@ describe "Taggable" do
|
|
106
106
|
TaggableModel.find_tagged_with("spinning", :on => :rotors).should_not be_empty
|
107
107
|
end
|
108
108
|
|
109
|
+
it "should support dirty field tracking on assign" do
|
110
|
+
@taggable.tag_list_changed?.should == false
|
111
|
+
|
112
|
+
@taggable.tag_list = "bob, charlie"
|
113
|
+
@taggable.tag_list_changed?.should == true
|
114
|
+
|
115
|
+
@taggable.save
|
116
|
+
@taggable.tag_list_changed?.should == false
|
117
|
+
|
118
|
+
@taggable.tag_list = "bob, charlie"
|
119
|
+
@taggable.tag_list_changed?.should == false
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should support dirty field tracking on append" do
|
123
|
+
@taggable.tag_list_changed?.should == false
|
124
|
+
|
125
|
+
@taggable.tag_list.add "bob, charlie"
|
126
|
+
@taggable.tag_list_changed?.should == true
|
127
|
+
|
128
|
+
@taggable.save
|
129
|
+
@taggable.tag_list_changed?.should == false
|
130
|
+
|
131
|
+
@taggable.tag_list.add "bob, charlie"
|
132
|
+
@taggable.tag_list_changed?.should == false
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should support dirty field tracking on append" do
|
136
|
+
@taggable.tag_list_changed?.should == false
|
137
|
+
|
138
|
+
@taggable.tag_list = "bob, charlie"
|
139
|
+
@taggable.tag_list_changed?.should == true
|
140
|
+
|
141
|
+
@taggable.save
|
142
|
+
@taggable.tag_list_changed?.should == false
|
143
|
+
|
144
|
+
@taggable.tag_list.remove "bob"
|
145
|
+
@taggable.tag_list_changed?.should == true
|
146
|
+
|
147
|
+
@taggable.save
|
148
|
+
@taggable.tag_list_changed?.should == false
|
149
|
+
|
150
|
+
@taggable.tag_list.remove "bob"
|
151
|
+
@taggable.tag_list_changed?.should == false
|
152
|
+
end
|
153
|
+
|
109
154
|
describe "Single Table Inheritance" do
|
110
155
|
before do
|
111
156
|
[TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yury-acts-as-taggable-on
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Bleigh
|
@@ -9,12 +9,12 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-03-06 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
16
|
description: Acts As Taggable On provides the ability to have multiple tag contexts on a single model in ActiveRecord. It also has support for tag clouds, related items, taggers, and more.
|
17
|
-
email:
|
17
|
+
email: michael@intridea.com
|
18
18
|
executables: []
|
19
19
|
|
20
20
|
extensions: []
|
@@ -28,6 +28,7 @@ files:
|
|
28
28
|
- generators/acts_as_taggable_on_migration
|
29
29
|
- generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb
|
30
30
|
- generators/acts_as_taggable_on_migration/templates
|
31
|
+
- generators/acts_as_taggable_on_migration/templates/add_users_migration.rb
|
31
32
|
- generators/acts_as_taggable_on_migration/templates/migration.rb
|
32
33
|
- init.rb
|
33
34
|
- lib/acts-as-taggable-on.rb
|
@@ -45,6 +46,7 @@ files:
|
|
45
46
|
- spec/acts_as_taggable_on/taggable_spec.rb
|
46
47
|
- spec/acts_as_taggable_on/tagger_spec.rb
|
47
48
|
- spec/acts_as_taggable_on/tagging_spec.rb
|
49
|
+
- spec/debug.log
|
48
50
|
- spec/schema.rb
|
49
51
|
- spec/spec_helper.rb
|
50
52
|
- uninstall.rb
|