tagtical 1.3.1 → 1.4.0

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/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.