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 CHANGED
@@ -1 +1 @@
1
- 1.3.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 : tags.scoped.merge(tag_type.scoping(*args))
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.has_many_name) do |*args|
75
- tags.scoped.merge(tag_type.scoping(*args)).tap do |scope|
76
- if args.empty? &&
77
- (loaded_parent_scope = tag_type.expand_tag_types(:parents).map { |t| send(t.has_many_name) }.detect(&:loaded?))
78
-
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
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).where("#{Tagtical::Tagging.table_name}.tagger_id IS NULL").all
253
- end
254
-
255
- def reload(*args)
256
- tag_types.each do |tag_type|
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
- super(*args)
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(tag_type)
317
- [nil, :all].each do |prefix|
318
- ivar = tag_type.tag_list_ivar(prefix)
319
- remove_instance_variable(ivar) if instance_variable_defined?(ivar)
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
- tags.where(find_tag_type!(input).finder_type_condition(*args))
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
- instance_variable_set(tag_type.tag_list_ivar(:owned), nil)
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)
@@ -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
- 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
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.3.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: &2161694760 !ruby/object:Gem::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: *2161694760
24
+ version_requirements: *2152083200
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &2161694280 !ruby/object:Gem::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: *2161694280
35
+ version_requirements: *2152082720
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sqlite3-ruby
38
- requirement: &2161693800 !ruby/object:Gem::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: *2161693800
46
+ version_requirements: *2152082240
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: mysql
49
- requirement: &2161693320 !ruby/object:Gem::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: *2161693320
57
+ version_requirements: *2152081760
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: jeweler
60
- requirement: &2161692840 !ruby/object:Gem::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: *2161692840
68
+ version_requirements: *2152081280
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rcov
71
- requirement: &2161692360 !ruby/object:Gem::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: *2161692360
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.