acts-as-taggable-on 2.2.2 → 2.3.3
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/.gitignore +4 -1
- data/README.rdoc +54 -15
- data/acts-as-taggable-on.gemspec +2 -2
- data/lib/acts-as-taggable-on/version.rb +1 -1
- data/lib/acts-as-taggable-on.rb +27 -1
- data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +88 -28
- data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +37 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +34 -13
- data/lib/acts_as_taggable_on/tag.rb +5 -11
- data/lib/acts_as_taggable_on/tag_list.rb +18 -13
- data/lib/acts_as_taggable_on/taggable.rb +64 -16
- data/lib/acts_as_taggable_on/tagger.rb +3 -3
- data/lib/acts_as_taggable_on/tagging.rb +1 -1
- data/lib/acts_as_taggable_on/utils.rb +1 -1
- data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +2 -2
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +58 -0
- data/spec/acts_as_taggable_on/tag_list_spec.rb +117 -65
- data/spec/acts_as_taggable_on/tag_spec.rb +29 -30
- data/spec/acts_as_taggable_on/taggable_spec.rb +137 -6
- data/spec/acts_as_taggable_on/tagger_spec.rb +49 -16
- data/spec/models.rb +9 -1
- data/spec/schema.rb +5 -0
- data/spec/spec_helper.rb +3 -1
- metadata +22 -21
|
@@ -15,6 +15,17 @@ module ActsAsTaggableOn
|
|
|
15
15
|
acts_as_taggable_on :tags
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
##
|
|
19
|
+
# This is an alias for calling <tt>acts_as_ordered_taggable_on :tags</tt>.
|
|
20
|
+
#
|
|
21
|
+
# Example:
|
|
22
|
+
# class Book < ActiveRecord::Base
|
|
23
|
+
# acts_as_ordered_taggable
|
|
24
|
+
# end
|
|
25
|
+
def acts_as_ordered_taggable
|
|
26
|
+
acts_as_ordered_taggable_on :tags
|
|
27
|
+
end
|
|
28
|
+
|
|
18
29
|
##
|
|
19
30
|
# Make a model taggable on specified contexts.
|
|
20
31
|
#
|
|
@@ -25,30 +36,67 @@ module ActsAsTaggableOn
|
|
|
25
36
|
# acts_as_taggable_on :languages, :skills
|
|
26
37
|
# end
|
|
27
38
|
def acts_as_taggable_on(*tag_types)
|
|
28
|
-
|
|
39
|
+
taggable_on(false, tag_types)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# Make a model taggable on specified contexts
|
|
45
|
+
# and preserves the order in which tags are created
|
|
46
|
+
#
|
|
47
|
+
# @param [Array] tag_types An array of taggable contexts
|
|
48
|
+
#
|
|
49
|
+
# Example:
|
|
50
|
+
# class User < ActiveRecord::Base
|
|
51
|
+
# acts_as_ordered_taggable_on :languages, :skills
|
|
52
|
+
# end
|
|
53
|
+
def acts_as_ordered_taggable_on(*tag_types)
|
|
54
|
+
taggable_on(true, tag_types)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
# Make a model taggable on specified contexts
|
|
60
|
+
# and optionally preserves the order in which tags are created
|
|
61
|
+
#
|
|
62
|
+
# Seperate methods used above for backwards compatibility
|
|
63
|
+
# so that the original acts_as_taggable_on method is unaffected
|
|
64
|
+
# as it's not possible to add another arguement to the method
|
|
65
|
+
# without the tag_types being enclosed in square brackets
|
|
66
|
+
#
|
|
67
|
+
# NB: method overridden in core module in order to create tag type
|
|
68
|
+
# associations and methods after this logic has executed
|
|
69
|
+
#
|
|
70
|
+
def taggable_on(preserve_tag_order, *tag_types)
|
|
71
|
+
tag_types = tag_types.to_a.flatten.compact.map(&:to_sym)
|
|
29
72
|
|
|
30
|
-
|
|
73
|
+
if taggable?
|
|
31
74
|
self.tag_types = (self.tag_types + tag_types).uniq
|
|
32
|
-
|
|
75
|
+
self.preserve_tag_order = preserve_tag_order
|
|
76
|
+
else
|
|
33
77
|
class_attribute :tag_types
|
|
34
78
|
self.tag_types = tag_types
|
|
79
|
+
class_attribute :preserve_tag_order
|
|
80
|
+
self.preserve_tag_order = preserve_tag_order
|
|
81
|
+
|
|
82
|
+
class_eval do
|
|
83
|
+
has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "ActsAsTaggableOn::Tagging"
|
|
84
|
+
has_many :base_tags, :through => :taggings, :source => :tag, :class_name => "ActsAsTaggableOn::Tag"
|
|
35
85
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
86
|
+
def self.taggable?
|
|
87
|
+
true
|
|
88
|
+
end
|
|
39
89
|
|
|
40
|
-
|
|
41
|
-
|
|
90
|
+
include ActsAsTaggableOn::Utils
|
|
91
|
+
include ActsAsTaggableOn::Taggable::Core
|
|
92
|
+
include ActsAsTaggableOn::Taggable::Collection
|
|
93
|
+
include ActsAsTaggableOn::Taggable::Cache
|
|
94
|
+
include ActsAsTaggableOn::Taggable::Ownership
|
|
95
|
+
include ActsAsTaggableOn::Taggable::Related
|
|
96
|
+
include ActsAsTaggableOn::Taggable::Dirty
|
|
42
97
|
end
|
|
43
|
-
|
|
44
|
-
include ActsAsTaggableOn::Utils
|
|
45
|
-
include ActsAsTaggableOn::Taggable::Core
|
|
46
|
-
include ActsAsTaggableOn::Taggable::Collection
|
|
47
|
-
include ActsAsTaggableOn::Taggable::Cache
|
|
48
|
-
include ActsAsTaggableOn::Taggable::Ownership
|
|
49
|
-
include ActsAsTaggableOn::Taggable::Related
|
|
50
98
|
end
|
|
51
99
|
end
|
|
52
|
-
|
|
100
|
+
|
|
53
101
|
end
|
|
54
102
|
end
|
|
@@ -31,7 +31,7 @@ module ActsAsTaggableOn
|
|
|
31
31
|
|
|
32
32
|
module InstanceMethods
|
|
33
33
|
##
|
|
34
|
-
# Tag a taggable model with tags that are owned by the tagger.
|
|
34
|
+
# Tag a taggable model with tags that are owned by the tagger.
|
|
35
35
|
#
|
|
36
36
|
# @param taggable The object that will be tagged
|
|
37
37
|
# @param [Hash] options An hash with options. Available options are:
|
|
@@ -42,7 +42,7 @@ module ActsAsTaggableOn
|
|
|
42
42
|
# @user.tag(@photo, :with => "paris, normandy", :on => :locations)
|
|
43
43
|
def tag(taggable, opts={})
|
|
44
44
|
opts.reverse_merge!(:force => true)
|
|
45
|
-
|
|
45
|
+
skip_save = opts.delete(:skip_save)
|
|
46
46
|
return false unless taggable.respond_to?(:is_taggable?) && taggable.is_taggable?
|
|
47
47
|
|
|
48
48
|
raise "You need to specify a tag context using :on" unless opts.has_key?(:on)
|
|
@@ -50,7 +50,7 @@ module ActsAsTaggableOn
|
|
|
50
50
|
raise "No context :#{opts[:on]} defined in #{taggable.class.to_s}" unless (opts[:force] || taggable.tag_types.include?(opts[:on]))
|
|
51
51
|
|
|
52
52
|
taggable.set_owner_tag_list_on(self, opts[:on].to_s, opts[:with])
|
|
53
|
-
taggable.save
|
|
53
|
+
taggable.save unless skip_save
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def is_tagger?
|
|
@@ -12,8 +12,8 @@ class ActsAsTaggableOnMigration < ActiveRecord::Migration
|
|
|
12
12
|
t.references :taggable, :polymorphic => true
|
|
13
13
|
t.references :tagger, :polymorphic => true
|
|
14
14
|
|
|
15
|
-
#
|
|
16
|
-
# http://bit.ly/vgW2Ql
|
|
15
|
+
# Limit is created to prevent MySQL error on index
|
|
16
|
+
# length for MyISAM table type: http://bit.ly/vgW2Ql
|
|
17
17
|
t.string :context, :limit => 128
|
|
18
18
|
|
|
19
19
|
t.datetime :created_at
|
|
@@ -8,6 +8,20 @@ describe "Acts As Taggable On" do
|
|
|
8
8
|
it "should provide a class method 'taggable?' that is false for untaggable models" do
|
|
9
9
|
UntaggableModel.should_not be_taggable
|
|
10
10
|
end
|
|
11
|
+
|
|
12
|
+
describe "Taggable Method Generation To Preserve Order" do
|
|
13
|
+
before(:each) do
|
|
14
|
+
clean_database!
|
|
15
|
+
TaggableModel.tag_types = []
|
|
16
|
+
TaggableModel.preserve_tag_order = false
|
|
17
|
+
TaggableModel.acts_as_ordered_taggable_on(:ordered_tags)
|
|
18
|
+
@taggable = TaggableModel.new(:name => "Bob Jones")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should respond 'true' to preserve_tag_order?" do
|
|
22
|
+
@taggable.class.preserve_tag_order?.should be_true
|
|
23
|
+
end
|
|
24
|
+
end
|
|
11
25
|
|
|
12
26
|
describe "Taggable Method Generation" do
|
|
13
27
|
before(:each) do
|
|
@@ -32,6 +46,18 @@ describe "Acts As Taggable On" do
|
|
|
32
46
|
it "should have all tag types" do
|
|
33
47
|
@taggable.tag_types.should == [:tags, :languages, :skills, :needs, :offerings]
|
|
34
48
|
end
|
|
49
|
+
|
|
50
|
+
it "should create a class attribute for preserve tag order" do
|
|
51
|
+
@taggable.class.should respond_to(:preserve_tag_order?)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "should create an instance attribute for preserve tag order" do
|
|
55
|
+
@taggable.should respond_to(:preserve_tag_order?)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should respond 'false' to preserve_tag_order?" do
|
|
59
|
+
@taggable.class.preserve_tag_order?.should be_false
|
|
60
|
+
end
|
|
35
61
|
|
|
36
62
|
it "should generate an association for each tag type" do
|
|
37
63
|
@taggable.should respond_to(:tags, :skills, :languages)
|
|
@@ -453,4 +479,36 @@ describe "Acts As Taggable On" do
|
|
|
453
479
|
@taggable.taggings.should == []
|
|
454
480
|
end
|
|
455
481
|
end
|
|
482
|
+
|
|
483
|
+
describe "@@remove_unused_tags" do
|
|
484
|
+
before do
|
|
485
|
+
@taggable = TaggableModel.create(:name => "Bob Jones")
|
|
486
|
+
@tag = ActsAsTaggableOn::Tag.create(:name => "awesome")
|
|
487
|
+
|
|
488
|
+
@tagging = ActsAsTaggableOn::Tagging.create(:taggable => @taggable, :tag => @tag, :context => 'tags')
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
context "if set to true" do
|
|
492
|
+
before do
|
|
493
|
+
ActsAsTaggableOn.remove_unused_tags = true
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
it "should remove unused tags after removing taggings" do
|
|
497
|
+
@tagging.destroy
|
|
498
|
+
ActsAsTaggableOn::Tag.find_by_name("awesome").should be_nil
|
|
499
|
+
end
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
context "if set to false" do
|
|
503
|
+
before do
|
|
504
|
+
ActsAsTaggableOn.remove_unused_tags = false
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
it "should not remove unused tags after removing taggings" do
|
|
508
|
+
@tagging.destroy
|
|
509
|
+
ActsAsTaggableOn::Tag.find_by_name("awesome").should == @tag
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
|
|
456
514
|
end
|
|
@@ -1,74 +1,126 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
1
2
|
require File.expand_path('../../spec_helper', __FILE__)
|
|
2
3
|
|
|
3
4
|
describe ActsAsTaggableOn::TagList do
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
it "should
|
|
9
|
-
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
it "should be able to be add a new tag word" do
|
|
13
|
-
@tag_list.add("cool")
|
|
14
|
-
@tag_list.include?("cool").should be_true
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
it "should be able to add delimited lists of words" do
|
|
18
|
-
@tag_list.add("cool, wicked", :parse => true)
|
|
19
|
-
@tag_list.include?("cool").should be_true
|
|
20
|
-
@tag_list.include?("wicked").should be_true
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it "should be able to add delimited list of words with quoted delimiters" do
|
|
24
|
-
@tag_list.add("'cool, wicked', \"really cool, really wicked\"", :parse => true)
|
|
25
|
-
@tag_list.include?("cool, wicked").should be_true
|
|
26
|
-
@tag_list.include?("really cool, really wicked").should be_true
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
it "should be able to handle other uses of quotation marks correctly" do
|
|
30
|
-
@tag_list.add("john's cool car, mary's wicked toy", :parse => true)
|
|
31
|
-
@tag_list.include?("john's cool car").should be_true
|
|
32
|
-
@tag_list.include?("mary's wicked toy").should be_true
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it "should be able to add an array of words" do
|
|
36
|
-
@tag_list.add(["cool", "wicked"], :parse => true)
|
|
37
|
-
@tag_list.include?("cool").should be_true
|
|
38
|
-
@tag_list.include?("wicked").should be_true
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
it "should be able to remove words" do
|
|
42
|
-
@tag_list.remove("awesome")
|
|
43
|
-
@tag_list.include?("awesome").should be_false
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it "should be able to remove delimited lists of words" do
|
|
47
|
-
@tag_list.remove("awesome, radical", :parse => true)
|
|
48
|
-
@tag_list.should be_empty
|
|
5
|
+
let(:tag_list) { ActsAsTaggableOn::TagList.new("awesome","radical") }
|
|
6
|
+
|
|
7
|
+
it { should be_kind_of Array }
|
|
8
|
+
|
|
9
|
+
it "#from should return empty array if empty array is passed" do
|
|
10
|
+
ActsAsTaggableOn::TagList.from([]).should be_empty
|
|
49
11
|
end
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
12
|
+
|
|
13
|
+
describe "#add" do
|
|
14
|
+
it "should be able to be add a new tag word" do
|
|
15
|
+
tag_list.add("cool")
|
|
16
|
+
tag_list.include?("cool").should be_true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should be able to add delimited lists of words" do
|
|
20
|
+
tag_list.add("cool, wicked", :parse => true)
|
|
21
|
+
tag_list.should include("cool", "wicked")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should be able to add delimited list of words with quoted delimiters" do
|
|
25
|
+
tag_list.add("'cool, wicked', \"really cool, really wicked\"", :parse => true)
|
|
26
|
+
tag_list.should include("cool, wicked", "really cool, really wicked")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should be able to handle other uses of quotation marks correctly" do
|
|
30
|
+
tag_list.add("john's cool car, mary's wicked toy", :parse => true)
|
|
31
|
+
tag_list.should include("john's cool car", "mary's wicked toy")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should be able to add an array of words" do
|
|
35
|
+
tag_list.add(["cool", "wicked"], :parse => true)
|
|
36
|
+
tag_list.should include("cool", "wicked")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should quote escape tags with commas in them" do
|
|
40
|
+
tag_list.add("cool","rad,bodacious")
|
|
41
|
+
tag_list.to_s.should == "awesome, radical, cool, \"rad,bodacious\""
|
|
42
|
+
end
|
|
43
|
+
|
|
54
44
|
end
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
45
|
+
|
|
46
|
+
describe "#remove" do
|
|
47
|
+
it "should be able to remove words" do
|
|
48
|
+
tag_list.remove("awesome")
|
|
49
|
+
tag_list.should_not include("awesome")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should be able to remove delimited lists of words" do
|
|
53
|
+
tag_list.remove("awesome, radical", :parse => true)
|
|
54
|
+
tag_list.should be_empty
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "should be able to remove an array of words" do
|
|
58
|
+
tag_list.remove(["awesome", "radical"], :parse => true)
|
|
59
|
+
tag_list.should be_empty
|
|
60
|
+
end
|
|
58
61
|
end
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
|
|
63
|
+
describe "#to_s" do
|
|
64
|
+
it "should give a delimited list of words when converted to string" do
|
|
65
|
+
tag_list.to_s.should == "awesome, radical"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "should be able to call to_s on a frozen tag list" do
|
|
69
|
+
tag_list.freeze
|
|
70
|
+
lambda { tag_list.add("cool","rad,bodacious") }.should raise_error
|
|
71
|
+
lambda { tag_list.to_s }.should_not raise_error
|
|
72
|
+
end
|
|
63
73
|
end
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
|
|
75
|
+
describe "cleaning" do
|
|
76
|
+
it "should parameterize if force_parameterize is set to true" do
|
|
77
|
+
ActsAsTaggableOn.force_parameterize = true
|
|
78
|
+
tag_list = ActsAsTaggableOn::TagList.new("awesome()","radical)(cc")
|
|
79
|
+
|
|
80
|
+
tag_list.to_s.should == "awesome, radical-cc"
|
|
81
|
+
ActsAsTaggableOn.force_parameterize = false
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "should lowercase if force_lowercase is set to true" do
|
|
85
|
+
ActsAsTaggableOn.force_lowercase = true
|
|
86
|
+
|
|
87
|
+
tag_list = ActsAsTaggableOn::TagList.new("aweSomE","RaDicaL")
|
|
88
|
+
tag_list.to_s.should == "awesome, radical"
|
|
89
|
+
|
|
90
|
+
ActsAsTaggableOn.force_lowercase = false
|
|
91
|
+
end
|
|
92
|
+
|
|
67
93
|
end
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
|
|
95
|
+
describe "Multiple Delimiter" do
|
|
96
|
+
before do
|
|
97
|
+
@old_delimiter = ActsAsTaggableOn.delimiter
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
after do
|
|
101
|
+
ActsAsTaggableOn.delimiter = @old_delimiter
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should separate tags by delimiters" do
|
|
105
|
+
ActsAsTaggableOn.delimiter = [',', ' ', '\|']
|
|
106
|
+
tag_list = ActsAsTaggableOn::TagList.from "cool, data|I have"
|
|
107
|
+
tag_list.to_s.should == 'cool, data, I, have'
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "should escape quote" do
|
|
111
|
+
ActsAsTaggableOn.delimiter = [',', ' ', '\|']
|
|
112
|
+
tag_list = ActsAsTaggableOn::TagList.from "'I have'|cool, data"
|
|
113
|
+
tag_list.to_s.should == '"I have", cool, data'
|
|
114
|
+
|
|
115
|
+
tag_list = ActsAsTaggableOn::TagList.from '"I, have"|cool, data'
|
|
116
|
+
tag_list.to_s.should == '"I, have", cool, data'
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "should work for utf8 delimiter and long delimiter" do
|
|
120
|
+
ActsAsTaggableOn.delimiter = [',', '的', '可能是']
|
|
121
|
+
tag_list = ActsAsTaggableOn::TagList.from "我的东西可能是不见了,还好有备份"
|
|
122
|
+
tag_list.to_s.should == "我, 东西, 不见了, 还好有备份"
|
|
123
|
+
end
|
|
73
124
|
end
|
|
74
|
-
|
|
125
|
+
|
|
126
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#encoding: utf-8
|
|
2
|
+
|
|
1
3
|
require File.expand_path('../../spec_helper', __FILE__)
|
|
2
4
|
|
|
3
5
|
describe ActsAsTaggableOn::Tag do
|
|
@@ -40,6 +42,23 @@ describe ActsAsTaggableOn::Tag do
|
|
|
40
42
|
end
|
|
41
43
|
end
|
|
42
44
|
|
|
45
|
+
unless ActsAsTaggableOn::Tag.using_sqlite?
|
|
46
|
+
describe "find or create by unicode name" do
|
|
47
|
+
before(:each) do
|
|
48
|
+
@tag.name = "привет"
|
|
49
|
+
@tag.save
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should find by name" do
|
|
53
|
+
ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("привет").should == @tag
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should find by name case insensitive" do
|
|
57
|
+
ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("ПРИВЕТ").should == @tag
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
43
62
|
describe "find or create all by any name" do
|
|
44
63
|
before(:each) do
|
|
45
64
|
@tag.name = "awesome"
|
|
@@ -82,6 +101,16 @@ describe ActsAsTaggableOn::Tag do
|
|
|
82
101
|
@tag.errors[:name].should == []
|
|
83
102
|
end
|
|
84
103
|
|
|
104
|
+
it "should limit the name length to 255 or less characters" do
|
|
105
|
+
@tag.name = "fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxranr"
|
|
106
|
+
@tag.valid?
|
|
107
|
+
@tag.errors[:name].should == ["is too long (maximum is 255 characters)"]
|
|
108
|
+
|
|
109
|
+
@tag.name = "fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxran"
|
|
110
|
+
@tag.valid?
|
|
111
|
+
@tag.errors[:name].should == []
|
|
112
|
+
end
|
|
113
|
+
|
|
85
114
|
it "should equal a tag with the same name" do
|
|
86
115
|
@tag.name = "awesome"
|
|
87
116
|
new_tag = ActsAsTaggableOn::Tag.new(:name => "awesome")
|
|
@@ -120,35 +149,5 @@ describe ActsAsTaggableOn::Tag do
|
|
|
120
149
|
end
|
|
121
150
|
|
|
122
151
|
end
|
|
123
|
-
|
|
124
|
-
describe ".remove_unused" do
|
|
125
|
-
before do
|
|
126
|
-
@taggable = TaggableModel.create(:name => "Bob Jones")
|
|
127
|
-
@tag = ActsAsTaggableOn::Tag.create(:name => "awesome")
|
|
128
152
|
|
|
129
|
-
@tagging = ActsAsTaggableOn::Tagging.create(:taggable => @taggable, :tag => @tag, :context => 'tags')
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
context "if set to true" do
|
|
133
|
-
before do
|
|
134
|
-
ActsAsTaggableOn::Tag.remove_unused = true
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
it "should remove unused tags after removing taggings" do
|
|
138
|
-
@tagging.destroy
|
|
139
|
-
ActsAsTaggableOn::Tag.find_by_name("awesome").should be_nil
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
context "if set to false" do
|
|
144
|
-
before do
|
|
145
|
-
ActsAsTaggableOn::Tag.remove_unused = false
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
it "should not remove unused tags after removing taggings" do
|
|
149
|
-
@tagging.destroy
|
|
150
|
-
ActsAsTaggableOn::Tag.find_by_name("awesome").should == @tag
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
153
|
end
|
|
@@ -1,5 +1,114 @@
|
|
|
1
1
|
require File.expand_path('../../spec_helper', __FILE__)
|
|
2
2
|
|
|
3
|
+
describe "Taggable To Preserve Order" do
|
|
4
|
+
before(:each) do
|
|
5
|
+
clean_database!
|
|
6
|
+
@taggable = OrderedTaggableModel.new(:name => "Bob Jones")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "should have tag types" do
|
|
10
|
+
[:tags, :colours].each do |type|
|
|
11
|
+
OrderedTaggableModel.tag_types.should include type
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@taggable.tag_types.should == OrderedTaggableModel.tag_types
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should have tag associations" do
|
|
18
|
+
[:tags, :colours].each do |type|
|
|
19
|
+
@taggable.respond_to?(type).should be_true
|
|
20
|
+
@taggable.respond_to?("#{type.to_s.singularize}_taggings").should be_true
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should have tag associations ordered by id" do
|
|
25
|
+
[:tags, :colours].each do |type|
|
|
26
|
+
OrderedTaggableModel.reflect_on_association(type).options[:order].should include('id')
|
|
27
|
+
OrderedTaggableModel.reflect_on_association("#{type.to_s.singularize}_taggings".to_sym).options[:order].should include('id')
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should have tag methods" do
|
|
32
|
+
[:tags, :colours].each do |type|
|
|
33
|
+
@taggable.respond_to?("#{type.to_s.singularize}_list").should be_true
|
|
34
|
+
@taggable.respond_to?("#{type.to_s.singularize}_list=").should be_true
|
|
35
|
+
@taggable.respond_to?("all_#{type.to_s}_list").should be_true
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should return tag list in the order the tags were created" do
|
|
40
|
+
# create
|
|
41
|
+
@taggable.tag_list = "rails, ruby, css"
|
|
42
|
+
@taggable.instance_variable_get("@tag_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
|
|
43
|
+
|
|
44
|
+
lambda {
|
|
45
|
+
@taggable.save
|
|
46
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(3)
|
|
47
|
+
|
|
48
|
+
@taggable.reload
|
|
49
|
+
@taggable.tag_list.should == %w(rails ruby css)
|
|
50
|
+
|
|
51
|
+
# update
|
|
52
|
+
@taggable.tag_list = "pow, ruby, rails"
|
|
53
|
+
@taggable.save
|
|
54
|
+
|
|
55
|
+
@taggable.reload
|
|
56
|
+
@taggable.tag_list.should == %w(pow ruby rails)
|
|
57
|
+
|
|
58
|
+
# update with no change
|
|
59
|
+
@taggable.tag_list = "pow, ruby, rails"
|
|
60
|
+
@taggable.save
|
|
61
|
+
|
|
62
|
+
@taggable.reload
|
|
63
|
+
@taggable.tag_list.should == %w(pow ruby rails)
|
|
64
|
+
|
|
65
|
+
# update to clear tags
|
|
66
|
+
@taggable.tag_list = ""
|
|
67
|
+
@taggable.save
|
|
68
|
+
|
|
69
|
+
@taggable.reload
|
|
70
|
+
@taggable.tag_list.should == []
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should return tag objects in the order the tags were created" do
|
|
74
|
+
# create
|
|
75
|
+
@taggable.tag_list = "pow, ruby, rails"
|
|
76
|
+
@taggable.instance_variable_get("@tag_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
|
|
77
|
+
|
|
78
|
+
lambda {
|
|
79
|
+
@taggable.save
|
|
80
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(3)
|
|
81
|
+
|
|
82
|
+
@taggable.reload
|
|
83
|
+
@taggable.tags.map{|t| t.name}.should == %w(pow ruby rails)
|
|
84
|
+
|
|
85
|
+
# update
|
|
86
|
+
@taggable.tag_list = "rails, ruby, css, pow"
|
|
87
|
+
@taggable.save
|
|
88
|
+
|
|
89
|
+
@taggable.reload
|
|
90
|
+
@taggable.tags.map{|t| t.name}.should == %w(rails ruby css pow)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "should return tag objects in tagging id order" do
|
|
94
|
+
# create
|
|
95
|
+
@taggable.tag_list = "pow, ruby, rails"
|
|
96
|
+
@taggable.save
|
|
97
|
+
|
|
98
|
+
@taggable.reload
|
|
99
|
+
ids = @taggable.tags.map{|t| t.taggings.first.id}
|
|
100
|
+
ids.should == ids.sort
|
|
101
|
+
|
|
102
|
+
# update
|
|
103
|
+
@taggable.tag_list = "rails, ruby, css, pow"
|
|
104
|
+
@taggable.save
|
|
105
|
+
|
|
106
|
+
@taggable.reload
|
|
107
|
+
ids = @taggable.tags.map{|t| t.taggings.first.id}
|
|
108
|
+
ids.should == ids.sort
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
3
112
|
describe "Taggable" do
|
|
4
113
|
before(:each) do
|
|
5
114
|
clean_database!
|
|
@@ -224,19 +333,19 @@ describe "Taggable" do
|
|
|
224
333
|
frank = TaggableModel.create(:name => "Frank", :tag_list => "bobby, jim")
|
|
225
334
|
steve = TaggableModel.create(:name => "Steve", :tag_list => "john, patricia")
|
|
226
335
|
jim = TaggableModel.create(:name => "Jim", :tag_list => "jim, steve")
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :any => true).to_a.should == [bob, frank, steve]
|
|
230
|
-
TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :exclude => true).to_a.should == [jim]
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :any => true).to_a.sort_by{|o| o.id}.should == [bob, frank, steve]
|
|
339
|
+
TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :exclude => true).to_a.should == [jim]
|
|
231
340
|
end
|
|
232
341
|
end
|
|
233
|
-
|
|
342
|
+
|
|
234
343
|
it "should be able to find tagged on a custom tag context" do
|
|
235
344
|
bob = TaggableModel.create(:name => "Bob")
|
|
236
345
|
bob.set_tag_list_on(:rotors, "spinning, jumping")
|
|
237
346
|
bob.tag_list_on(:rotors).should == ["spinning","jumping"]
|
|
238
347
|
bob.save
|
|
239
|
-
|
|
348
|
+
|
|
240
349
|
TaggableModel.tagged_with("spinning", :on => :rotors).to_a.should == [bob]
|
|
241
350
|
end
|
|
242
351
|
|
|
@@ -407,6 +516,28 @@ describe "Taggable" do
|
|
|
407
516
|
@taggable.tag_list_on(:test).should == ["hello"]
|
|
408
517
|
end
|
|
409
518
|
end
|
|
519
|
+
|
|
520
|
+
describe "Dirty Objects" do
|
|
521
|
+
before(:each) do
|
|
522
|
+
@taggable = TaggableModel.create(:tag_list => "awesome, epic")
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
it 'should show changes of dirty object' do
|
|
526
|
+
@taggable.changes.should == {}
|
|
527
|
+
@taggable.tag_list = 'one'
|
|
528
|
+
@taggable.changes.should == {"tag_list"=>["awesome, epic", ["one"]]}
|
|
529
|
+
|
|
530
|
+
@taggable.tag_list_changed?.should be_true
|
|
531
|
+
@taggable.tag_list_was.should == "awesome, epic"
|
|
532
|
+
@taggable.tag_list_change.should == ["awesome, epic", ["one"]]
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
it 'should show no changes if the same tag_list' do
|
|
536
|
+
@taggable.tag_list = "awesome, epic"
|
|
537
|
+
@taggable.tag_list_changed?.should be_false
|
|
538
|
+
@taggable.changes.should == {}
|
|
539
|
+
end
|
|
540
|
+
end
|
|
410
541
|
end
|
|
411
542
|
|
|
412
543
|
|