tagtical 1.3.1 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/tagtical/tag.rb +13 -0
- data/lib/tagtical/taggable/core.rb +37 -26
- data/lib/tagtical/taggable/ownership.rb +2 -1
- data/lib/tagtical/tagging.rb +13 -5
- data/spec/tagtical/taggable_spec.rb +19 -0
- data/spec/tagtical/tagging_spec.rb +13 -2
- metadata +13 -13
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.4.0
|
data/lib/tagtical/tag.rb
CHANGED
@@ -141,6 +141,11 @@ module Tagtical
|
|
141
141
|
!!tag_types_for_questioner_method(method_id) || super
|
142
142
|
end
|
143
143
|
|
144
|
+
# Carried over from tagging.
|
145
|
+
def has_tagger?
|
146
|
+
!self[:tagger_id].nil?
|
147
|
+
end
|
148
|
+
|
144
149
|
private
|
145
150
|
|
146
151
|
def tag_types_for_questioner_method(method_name)
|
@@ -297,6 +302,14 @@ module Tagtical
|
|
297
302
|
"@#{tag_list_name(*args)}"
|
298
303
|
end
|
299
304
|
|
305
|
+
def all_tag_list_ivar
|
306
|
+
tag_list_ivar(:all)
|
307
|
+
end
|
308
|
+
|
309
|
+
def scope_ivar
|
310
|
+
"@#{scope_name}"
|
311
|
+
end
|
312
|
+
|
300
313
|
# Returns the level from which it extends from Tagtical::Tag
|
301
314
|
def active_record_sti_level
|
302
315
|
@active_record_sti_level ||= begin
|
@@ -15,7 +15,7 @@ module Tagtical::Taggable
|
|
15
15
|
def initialize_tagtical_core
|
16
16
|
has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "Tagtical::Tagging"
|
17
17
|
has_many :tags, :through => :taggings, :source => :tag, :class_name => "Tagtical::Tag",
|
18
|
-
:select => "#{Tagtical::Tag.table_name}.*, #{Tagtical::Tagging.table_name}.relevance" # include the relevance on the tags
|
18
|
+
:select => "#{Tagtical::Tag.table_name}.*, #{Tagtical::Tagging.table_name}.relevance, #{Tagtical::Tagging.table_name}.tagger_id" # include the relevance on the tags
|
19
19
|
|
20
20
|
tag_types.each do |tag_type| # has_many :tags gets created here
|
21
21
|
|
@@ -67,18 +67,21 @@ module Tagtical::Taggable
|
|
67
67
|
define_method("tags_with_finder_type_options") do |*args|
|
68
68
|
bool = args.shift if [true, false].include?(args.first)
|
69
69
|
tags = tags_without_finder_type_options(bool)
|
70
|
-
args.empty? ? tags :
|
70
|
+
args.empty? ? tags : tags_with_type_scoping(tag_type, *args)
|
71
71
|
end
|
72
72
|
alias_method_chain :tags, :finder_type_options
|
73
|
-
else
|
74
|
-
define_method(tag_type.
|
75
|
-
|
76
|
-
|
77
|
-
(
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
73
|
+
else # handle the Tagtical::Tag subclasses
|
74
|
+
define_method(tag_type.scope_name) do |*args|
|
75
|
+
if args.empty?
|
76
|
+
instance_variable_get(tag_type.scope_ivar) || instance_variable_set(tag_type.scope_ivar,
|
77
|
+
tags.scoped.merge(tag_type.scoping).tap do |scope|
|
78
|
+
if (loaded_parent_scope = tag_type.expand_tag_types(:parents).map { |t| tag_scope(t) }.detect(&:loaded?))
|
79
|
+
scope.instance_variable_set(:@loaded, true)
|
80
|
+
scope.instance_variable_set(:@records, loaded_parent_scope.select { |t| t.class <= tag_type.klass })
|
81
|
+
end
|
82
|
+
end)
|
83
|
+
else
|
84
|
+
tags_with_type_scoping(tag_type, *args)
|
82
85
|
end
|
83
86
|
end
|
84
87
|
end
|
@@ -249,16 +252,17 @@ module Tagtical::Taggable
|
|
249
252
|
##
|
250
253
|
# Returns all tags that aren't owned.
|
251
254
|
def tags_on(context, *args)
|
252
|
-
tag_scope(context, *args)
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
instance_variable_set(tag_type.tag_list_ivar, nil)
|
258
|
-
instance_variable_set(tag_type.tag_list_ivar(:all), nil)
|
255
|
+
scope = tag_scope(context, *args)
|
256
|
+
if args.empty?
|
257
|
+
scope.reject(&:has_tagger?)
|
258
|
+
else
|
259
|
+
scope.where("#{Tagtical::Tagging.table_name}.tagger_id IS NULL").all
|
259
260
|
end
|
261
|
+
end
|
260
262
|
|
261
|
-
|
263
|
+
def reload(*)
|
264
|
+
remove_tag_caches_on(tag_types)
|
265
|
+
super
|
262
266
|
end
|
263
267
|
|
264
268
|
def save_tags
|
@@ -279,7 +283,7 @@ module Tagtical::Taggable
|
|
279
283
|
current_tags = tags_on(tag_type, :types => expanded_tag_types, :scope => :parents) # add in the parents because we need them later on down.
|
280
284
|
old_tags = current_tags - tags
|
281
285
|
new_tags = tags - current_tags
|
282
|
-
|
286
|
+
|
283
287
|
unowned_taggings = taggings.where(:tagger_id => nil)
|
284
288
|
|
285
289
|
# If relevances are specified on current tags, make sure to update those
|
@@ -303,7 +307,7 @@ module Tagtical::Taggable
|
|
303
307
|
taggings.create!(:tag_id => tag.id, :taggable => self, :relevance => tag_value_lookup[tag].relevance) # Create new taggings:
|
304
308
|
end
|
305
309
|
end
|
306
|
-
|
310
|
+
|
307
311
|
# Force tag lists to reload to integrate any new tags from inheritance.
|
308
312
|
remove_tag_caches_on(tag_type)
|
309
313
|
end
|
@@ -313,15 +317,17 @@ module Tagtical::Taggable
|
|
313
317
|
|
314
318
|
private
|
315
319
|
|
316
|
-
def remove_tag_caches_on(
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
+
def remove_tag_caches_on(tag_types)
|
321
|
+
Array(tag_types).each do |tag_type|
|
322
|
+
[:all_tag_list_ivar, :tag_list_ivar, :scope_ivar].each do |ivar_method|
|
323
|
+
ivar = tag_type.send(ivar_method)
|
324
|
+
remove_instance_variable(ivar) if instance_variable_defined?(ivar)
|
325
|
+
end
|
320
326
|
end
|
321
327
|
end
|
322
328
|
|
323
329
|
def tag_scope(input, *args)
|
324
|
-
|
330
|
+
send(find_tag_type!(input).scope_name, *args)
|
325
331
|
end
|
326
332
|
|
327
333
|
def find_tag_type!(input)
|
@@ -332,6 +338,11 @@ module Tagtical::Taggable
|
|
332
338
|
(@expand_tag_types ||= {})[[input, args]] ||= find_tag_type!(input).expand_tag_types(*args)
|
333
339
|
end
|
334
340
|
|
341
|
+
|
342
|
+
def tags_with_type_scoping(tag_type, *args)
|
343
|
+
tags.scoped.merge(tag_type.scoping(*args))
|
344
|
+
end
|
345
|
+
|
335
346
|
# Lets say tag class A inherits from B and B has a tag with value "foo". If we tag A with value "foo",
|
336
347
|
# we want B to have only one instance of "foo" and that tag should be an instance of A (a subclass of B).
|
337
348
|
def update_tagging_with_inherited_tag!(tagging, tags, tag_value_lookup)
|
@@ -57,7 +57,8 @@ module Tagtical::Taggable
|
|
57
57
|
|
58
58
|
def reload(*args)
|
59
59
|
tag_types.each do |tag_type|
|
60
|
-
|
60
|
+
ivar = tag_type.tag_list_ivar(:owned)
|
61
|
+
remove_instance_variable(ivar) if instance_variable_defined?(ivar)
|
61
62
|
end
|
62
63
|
|
63
64
|
super(*args)
|
data/lib/tagtical/tagging.rb
CHANGED
@@ -22,13 +22,12 @@ module Tagtical
|
|
22
22
|
belongs_to :tagger, :polymorphic => true
|
23
23
|
else
|
24
24
|
belongs_to :tagger, case Tagtical.config.tagger
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
when Hash then Tagtical.config.tagger
|
26
|
+
when true then {:class_name => "User"} # default to using User class.
|
27
|
+
when String then {:class_name => Tagtical.config.tagger}
|
28
|
+
end
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
31
|
before_create { |record| record.relevance ||= default_relevance }
|
33
32
|
|
34
33
|
class_attribute :default_relevance, :instance_writer => false
|
@@ -38,5 +37,14 @@ module Tagtical
|
|
38
37
|
relevance <=> tagging.relevance
|
39
38
|
end
|
40
39
|
|
40
|
+
def set_tag_target_with_relevance(tag)
|
41
|
+
if tag
|
42
|
+
tag.relevance = relevance
|
43
|
+
tag[:tagger_id] = tagger_id
|
44
|
+
end
|
45
|
+
set_tag_target_without_relevance(tag)
|
46
|
+
end
|
47
|
+
alias_method_chain :set_tag_target, :relevance
|
48
|
+
|
41
49
|
end
|
42
50
|
end
|
@@ -187,6 +187,25 @@ describe Tagtical::Taggable do
|
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
190
|
+
describe "Eager Loading Tags" do
|
191
|
+
before do
|
192
|
+
@taggable.update_attributes!(:craft_list => "foo:0.9, bar, car")
|
193
|
+
|
194
|
+
@taggables = TaggableModel.all(:include => :tags)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should leverage eager loaded tags for tag_list" do
|
198
|
+
ActiveRecord::Base.connection.expects(:execute).never
|
199
|
+
@taggables[0].tag_list
|
200
|
+
@taggables[0].craft_list
|
201
|
+
@taggables[0].skill_list
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should populate relevance on tags" do
|
205
|
+
@taggables[0].craft_list.find("foo").relevance==0.9
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
190
209
|
describe "tag_list scoping behavior" do
|
191
210
|
before do
|
192
211
|
@taggables[0].tag_list = "bob"
|
@@ -4,7 +4,7 @@ describe Tagtical::Tagging do
|
|
4
4
|
before(:each) do
|
5
5
|
clean_database!
|
6
6
|
@klass = Tagtical::Tagging
|
7
|
-
@tagging = @klass.new
|
7
|
+
@tagging = @klass.new(:relevance => 4.0)
|
8
8
|
end
|
9
9
|
subject { @tagging }
|
10
10
|
|
@@ -48,5 +48,16 @@ describe Tagtical::Tagging do
|
|
48
48
|
2.times { @klass.create(:taggable => @taggable, :tag => @tag, :context => 'tags') }
|
49
49
|
}.should change(@klass, :count).by(1)
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
|
+
describe "#set_tag_target" do
|
53
|
+
before do
|
54
|
+
@tag = Tagtical::Tag.new(:value => "foo")
|
55
|
+
@tagging.set_tag_target(@tag)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should set relevance on tag" do
|
59
|
+
@tag.relevance.should==4.0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
52
63
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tagtical
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2011-07-24 00:00:00.000000000Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
|
-
requirement: &
|
16
|
+
requirement: &2152083200 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - <=
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.0.5
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2152083200
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &2152082720 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - <=
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 2.6.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2152082720
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: sqlite3-ruby
|
38
|
-
requirement: &
|
38
|
+
requirement: &2152082240 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *2152082240
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: mysql
|
49
|
-
requirement: &
|
49
|
+
requirement: &2152081760 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *2152081760
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: jeweler
|
60
|
-
requirement: &
|
60
|
+
requirement: &2152081280 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *2152081280
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rcov
|
71
|
-
requirement: &
|
71
|
+
requirement: &2152080800 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,7 +76,7 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *2152080800
|
80
80
|
description: Tagtical allows you do create subclasses for Tag and add additional functionality
|
81
81
|
in an STI fashion. For example. You could do Tag::Color.find_by_name('blue').to_rgb.
|
82
82
|
It also supports storing weights or relevance on the taggings.
|