acts-as-taggable-on 1.0.13 → 2.0.0.pre1
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/CHANGELOG +5 -2
- data/Gemfile +8 -0
- data/README.rdoc +12 -1
- data/Rakefile +7 -6
- data/VERSION +1 -1
- data/lib/acts-as-taggable-on.rb +27 -7
- data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +127 -84
- data/lib/acts_as_taggable_on/acts_as_tagger.rb +21 -13
- data/lib/acts_as_taggable_on/group_helper.rb +2 -0
- data/lib/acts_as_taggable_on/tag.rb +37 -10
- data/lib/acts_as_taggable_on/tag_list.rb +29 -29
- data/lib/acts_as_taggable_on/tagging.rb +16 -2
- data/lib/acts_as_taggable_on/tags_helper.rb +8 -2
- data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +31 -0
- data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +28 -0
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +7 -17
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +45 -4
- data/spec/acts_as_taggable_on/group_helper_spec.rb +3 -0
- data/spec/acts_as_taggable_on/tag_list_spec.rb +18 -0
- data/spec/acts_as_taggable_on/tag_spec.rb +55 -13
- data/spec/acts_as_taggable_on/taggable_spec.rb +101 -67
- data/spec/acts_as_taggable_on/tagger_spec.rb +20 -3
- data/spec/acts_as_taggable_on/tagging_spec.rb +13 -3
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -1
- data/spec/spec.opts +1 -2
- data/spec/spec_helper.rb +26 -9
- metadata +20 -8
- data/rails/init.rb +0 -7
|
@@ -1,26 +1,53 @@
|
|
|
1
1
|
class Tag < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
attr_accessible :name
|
|
4
|
+
|
|
5
|
+
### ASSOCIATIONS:
|
|
6
|
+
|
|
2
7
|
has_many :taggings, :dependent => :destroy
|
|
3
|
-
|
|
8
|
+
|
|
9
|
+
### VALIDATIONS:
|
|
10
|
+
|
|
4
11
|
validates_presence_of :name
|
|
5
12
|
validates_uniqueness_of :name
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
|
|
14
|
+
### NAMED SCOPES:
|
|
15
|
+
|
|
16
|
+
scope :named, lambda { |name| { :conditions => ["name LIKE ?", name] } }
|
|
17
|
+
scope :named_any, lambda { |list| { :conditions => list.map { |tag| sanitize_sql(["name LIKE ?", tag.to_s]) }.join(" OR ") } }
|
|
18
|
+
scope :named_like, lambda { |name| { :conditions => ["name LIKE ?", "%#{name}%"] } }
|
|
19
|
+
scope :named_like_any, lambda { |list| { :conditions => list.map { |tag| sanitize_sql(["name LIKE ?", "%#{tag.to_s}%"]) }.join(" OR ") } }
|
|
20
|
+
|
|
21
|
+
### CLASS METHODS:
|
|
22
|
+
|
|
11
23
|
def self.find_or_create_with_like_by_name(name)
|
|
12
|
-
|
|
24
|
+
named_like(name).first || create(:name => name)
|
|
13
25
|
end
|
|
14
|
-
|
|
26
|
+
|
|
27
|
+
def self.find_or_create_all_with_like_by_name(*list)
|
|
28
|
+
list = [list].flatten
|
|
29
|
+
|
|
30
|
+
return [] if list.empty?
|
|
31
|
+
|
|
32
|
+
existing_tags = Tag.named_any(list).all
|
|
33
|
+
new_tag_names = list.reject { |name| existing_tags.any? { |tag| tag.name.downcase == name.downcase } }
|
|
34
|
+
created_tags = new_tag_names.map { |name| Tag.create(:name => name) }
|
|
35
|
+
|
|
36
|
+
existing_tags + created_tags
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
### INSTANCE METHODS:
|
|
40
|
+
|
|
15
41
|
def ==(object)
|
|
16
42
|
super || (object.is_a?(Tag) && name == object.name)
|
|
17
43
|
end
|
|
18
|
-
|
|
44
|
+
|
|
19
45
|
def to_s
|
|
20
46
|
name
|
|
21
47
|
end
|
|
22
|
-
|
|
48
|
+
|
|
23
49
|
def count
|
|
24
50
|
read_attribute(:count).to_i
|
|
25
51
|
end
|
|
52
|
+
|
|
26
53
|
end
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
class TagList < Array
|
|
2
|
+
|
|
2
3
|
cattr_accessor :delimiter
|
|
4
|
+
|
|
3
5
|
self.delimiter = ','
|
|
4
|
-
|
|
6
|
+
|
|
5
7
|
def initialize(*args)
|
|
6
8
|
add(*args)
|
|
7
9
|
end
|
|
8
|
-
|
|
10
|
+
|
|
9
11
|
attr_accessor :owner
|
|
10
|
-
|
|
12
|
+
|
|
11
13
|
# Add tags to the tag_list. Duplicate or blank tags will be ignored.
|
|
12
14
|
#
|
|
13
15
|
# tag_list.add("Fun", "Happy")
|
|
14
|
-
#
|
|
16
|
+
#
|
|
15
17
|
# Use the <tt>:parse</tt> option to add an unparsed tag string.
|
|
16
18
|
#
|
|
17
19
|
# tag_list.add("Fun, Happy", :parse => true)
|
|
@@ -21,33 +23,34 @@ class TagList < Array
|
|
|
21
23
|
clean!
|
|
22
24
|
self
|
|
23
25
|
end
|
|
24
|
-
|
|
26
|
+
|
|
25
27
|
# Remove specific tags from the tag_list.
|
|
26
|
-
#
|
|
28
|
+
#
|
|
27
29
|
# tag_list.remove("Sad", "Lonely")
|
|
28
30
|
#
|
|
29
31
|
# Like #add, the <tt>:parse</tt> option can be used to remove multiple tags in a string.
|
|
30
|
-
#
|
|
32
|
+
#
|
|
31
33
|
# tag_list.remove("Sad, Lonely", :parse => true)
|
|
32
34
|
def remove(*names)
|
|
33
35
|
extract_and_apply_options!(names)
|
|
34
36
|
delete_if { |name| names.include?(name) }
|
|
35
37
|
self
|
|
36
38
|
end
|
|
37
|
-
|
|
39
|
+
|
|
38
40
|
# Transform the tag_list into a tag string suitable for edting in a form.
|
|
39
41
|
# The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
|
|
40
42
|
#
|
|
41
43
|
# tag_list = TagList.new("Round", "Square,Cube")
|
|
42
44
|
# tag_list.to_s # 'Round, "Square,Cube"'
|
|
43
45
|
def to_s
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
tags = frozen? ? self.dup : self
|
|
47
|
+
tags.send(:clean!)
|
|
48
|
+
|
|
49
|
+
tags.map do |name|
|
|
47
50
|
name.include?(delimiter) ? "\"#{name}\"" : name
|
|
48
51
|
end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
|
|
49
52
|
end
|
|
50
|
-
|
|
53
|
+
|
|
51
54
|
private
|
|
52
55
|
# Remove whitespace, duplicates, and blanks.
|
|
53
56
|
def clean!
|
|
@@ -55,41 +58,38 @@ class TagList < Array
|
|
|
55
58
|
map!(&:strip)
|
|
56
59
|
uniq!
|
|
57
60
|
end
|
|
58
|
-
|
|
61
|
+
|
|
59
62
|
def extract_and_apply_options!(args)
|
|
60
63
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
61
64
|
options.assert_valid_keys :parse
|
|
62
|
-
|
|
65
|
+
|
|
63
66
|
if options[:parse]
|
|
64
67
|
args.map! { |a| self.class.from(a) }
|
|
65
68
|
end
|
|
66
|
-
|
|
69
|
+
|
|
67
70
|
args.flatten!
|
|
68
71
|
end
|
|
69
|
-
|
|
72
|
+
|
|
70
73
|
class << self
|
|
74
|
+
|
|
71
75
|
# Returns a new TagList using the given tag string.
|
|
72
|
-
#
|
|
76
|
+
#
|
|
73
77
|
# tag_list = TagList.from("One , Two, Three")
|
|
74
78
|
# tag_list # ["One", "Two", "Three"]
|
|
75
79
|
def from(string)
|
|
76
80
|
string = string.join(", ") if string.respond_to?(:join)
|
|
77
81
|
|
|
78
|
-
|
|
82
|
+
new.tap do |tag_list|
|
|
79
83
|
string = string.to_s.dup
|
|
80
|
-
|
|
84
|
+
|
|
81
85
|
# Parse the quoted tags
|
|
82
|
-
string.gsub!(/"(.*?)"\s
|
|
83
|
-
string.gsub!(/'(.*?)'\s
|
|
84
|
-
|
|
86
|
+
string.gsub!(/(\A|#{delimiter})\s*"(.*?)"\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
|
|
87
|
+
string.gsub!(/(\A|#{delimiter})\s*'(.*?)'\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
|
|
88
|
+
|
|
85
89
|
tag_list.add(string.split(delimiter))
|
|
86
90
|
end
|
|
87
91
|
end
|
|
88
|
-
|
|
89
|
-
def from_owner(owner, *tags)
|
|
90
|
-
returning from(*tags) do |taglist|
|
|
91
|
-
taglist.owner = owner
|
|
92
|
-
end
|
|
93
|
-
end
|
|
92
|
+
|
|
94
93
|
end
|
|
95
|
-
|
|
94
|
+
|
|
95
|
+
end
|
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
class Tagging < ActiveRecord::Base #:nodoc:
|
|
2
|
+
|
|
3
|
+
attr_accessible :tag,
|
|
4
|
+
:tag_id,
|
|
5
|
+
:context,
|
|
6
|
+
:taggable,
|
|
7
|
+
:taggable_type,
|
|
8
|
+
:taggable_id,
|
|
9
|
+
:tagger,
|
|
10
|
+
:tagger_type,
|
|
11
|
+
:tagger_id
|
|
12
|
+
|
|
2
13
|
belongs_to :tag
|
|
3
14
|
belongs_to :taggable, :polymorphic => true
|
|
4
|
-
belongs_to :tagger,
|
|
5
|
-
|
|
15
|
+
belongs_to :tagger, :polymorphic => true
|
|
16
|
+
|
|
6
17
|
validates_presence_of :context
|
|
7
18
|
validates_presence_of :tag_id
|
|
19
|
+
|
|
20
|
+
validates_uniqueness_of :tag_id, :scope => [ :taggable_type, :taggable_id, :context, :tagger_id, :tagger_type ]
|
|
21
|
+
|
|
8
22
|
end
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
module TagsHelper
|
|
2
|
+
|
|
2
3
|
# See the README for an example using tag_cloud.
|
|
3
4
|
def tag_cloud(tags, classes)
|
|
5
|
+
tags = tags.all if tags.is_a? ActiveRecord::Relation
|
|
6
|
+
|
|
7
|
+
return [] if tags.empty?
|
|
8
|
+
|
|
4
9
|
max_count = tags.sort_by(&:count).last.count.to_f
|
|
5
|
-
|
|
10
|
+
|
|
6
11
|
tags.each do |tag|
|
|
7
12
|
index = ((tag.count / max_count) * (classes.size - 1)).round
|
|
8
13
|
yield tag, classes[index]
|
|
9
14
|
end
|
|
10
15
|
end
|
|
11
|
-
|
|
16
|
+
|
|
17
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'rails/generators/migration'
|
|
2
|
+
|
|
3
|
+
module ActsAsTaggableOn
|
|
4
|
+
class MigrationGenerator < Rails::Generators::Base
|
|
5
|
+
include Rails::Generators::Migration
|
|
6
|
+
|
|
7
|
+
desc "Generates migration for Tag and Tagging models"
|
|
8
|
+
|
|
9
|
+
def self.orm
|
|
10
|
+
Rails::Generators.options[:rails][:orm]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.source_root
|
|
14
|
+
File.join(File.dirname(__FILE__), 'templates', orm)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.orm_has_migration?
|
|
18
|
+
[:active_record].include? orm
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.next_migration_number(path)
|
|
22
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def create_migration_file
|
|
26
|
+
if self.class.orm_has_migration?
|
|
27
|
+
migration_template 'migration.rb', 'db/migrate/acts_as_taggable_on_migration'
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class ActsAsTaggableOnMigration < ActiveRecord::Migration
|
|
2
|
+
def self.up
|
|
3
|
+
create_table :tags do |t|
|
|
4
|
+
t.string :name
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
create_table :taggings do |t|
|
|
8
|
+
t.references :tag
|
|
9
|
+
|
|
10
|
+
# You should make sure that the column created is
|
|
11
|
+
# long enough to store the required class names.
|
|
12
|
+
t.references :taggable, :polymorphic => true
|
|
13
|
+
t.references :tagger, :polymorphic => true
|
|
14
|
+
|
|
15
|
+
t.string :context
|
|
16
|
+
|
|
17
|
+
t.datetime :created_at
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
add_index :taggings, :tag_id
|
|
21
|
+
add_index :taggings, [:taggable_id, :taggable_type, :context]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.down
|
|
25
|
+
drop_table :taggings
|
|
26
|
+
drop_table :tags
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
2
|
|
|
3
3
|
describe "Acts As Taggable On" do
|
|
4
|
+
before(:each) do
|
|
5
|
+
clean_database!
|
|
6
|
+
end
|
|
7
|
+
|
|
4
8
|
it "should provide a class method 'taggable?' that is false for untaggable models" do
|
|
5
9
|
UntaggableModel.should_not be_taggable
|
|
6
10
|
end
|
|
@@ -18,7 +22,7 @@ describe "Acts As Taggable On" do
|
|
|
18
22
|
it "should create a class attribute for tag types" do
|
|
19
23
|
@taggable.class.should respond_to(:tag_types)
|
|
20
24
|
end
|
|
21
|
-
|
|
25
|
+
|
|
22
26
|
it "should create an instance attribute for tag types" do
|
|
23
27
|
@taggable.should respond_to(:tag_types)
|
|
24
28
|
end
|
|
@@ -32,7 +36,7 @@ describe "Acts As Taggable On" do
|
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
it "should add tagged_with and tag_counts to singleton" do
|
|
35
|
-
TaggableModel.should respond_to(:
|
|
39
|
+
TaggableModel.should respond_to(:tagged_with, :tag_counts)
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
it "should add saving of tag lists and cached tag lists to the instance" do
|
|
@@ -176,16 +180,6 @@ describe "Acts As Taggable On" do
|
|
|
176
180
|
end
|
|
177
181
|
|
|
178
182
|
describe 'Tagging Contexts' do
|
|
179
|
-
before(:all) do
|
|
180
|
-
class Array
|
|
181
|
-
def freq
|
|
182
|
-
k=Hash.new(0)
|
|
183
|
-
self.each {|e| k[e]+=1}
|
|
184
|
-
k
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
|
|
189
183
|
it 'should eliminate duplicate tagging contexts ' do
|
|
190
184
|
TaggableModel.acts_as_taggable_on(:skills, :skills)
|
|
191
185
|
TaggableModel.tag_types.freq[:skills].should_not == 3
|
|
@@ -212,10 +206,6 @@ describe "Acts As Taggable On" do
|
|
|
212
206
|
TaggableModel.acts_as_taggable_on([nil])
|
|
213
207
|
}.should_not raise_error
|
|
214
208
|
end
|
|
215
|
-
|
|
216
|
-
after(:all) do
|
|
217
|
-
class Array; remove_method :freq; end
|
|
218
|
-
end
|
|
219
209
|
end
|
|
220
210
|
|
|
221
|
-
end
|
|
211
|
+
end
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
2
|
|
|
3
3
|
describe "acts_as_tagger" do
|
|
4
|
+
before(:each) do
|
|
5
|
+
clean_database!
|
|
6
|
+
end
|
|
7
|
+
|
|
4
8
|
context "Tagger Method Generation" do
|
|
5
|
-
|
|
6
9
|
before(:each) do
|
|
7
10
|
@tagger = TaggableUser.new()
|
|
8
11
|
end
|
|
@@ -48,8 +51,22 @@ describe "acts_as_tagger" do
|
|
|
48
51
|
|
|
49
52
|
it 'should by default create the tag context on-the-fly' do
|
|
50
53
|
@taggable.tag_list_on(:here_ond_now).should be_empty
|
|
51
|
-
@tagger.tag(@taggable, :with=>'that', :on
|
|
52
|
-
@taggable.tag_list_on(:here_ond_now).
|
|
54
|
+
@tagger.tag(@taggable, :with=>'that', :on => :here_ond_now)
|
|
55
|
+
@taggable.tag_list_on(:here_ond_now).should_not include('that')
|
|
56
|
+
@taggable.all_tags_list_on(:here_ond_now).should include('that')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "should show all the tag list when both public and owned tags exist" do
|
|
60
|
+
@taggable.tag_list = 'ruby, python'
|
|
61
|
+
@tagger.tag(@taggable, :with => 'java, lisp', :on => :tags)
|
|
62
|
+
@taggable.all_tags_on(:tags).map(&:name).sort.should == %w(ruby python java lisp).sort
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should not add owned tags to the common list" do
|
|
66
|
+
@taggable.tag_list = 'ruby, python'
|
|
67
|
+
@tagger.tag(@taggable, :with => 'java, lisp', :on => :foo)
|
|
68
|
+
@tagger.tag(@taggable, :with => '', :on => :foo)
|
|
69
|
+
@taggable.tag_list.should == %w(ruby python)
|
|
53
70
|
end
|
|
54
71
|
|
|
55
72
|
it "should throw an exception when the default is over-ridden" do
|
|
@@ -64,9 +81,33 @@ describe "acts_as_tagger" do
|
|
|
64
81
|
@tagger.tag(@taggable, :with=>'this, and, that', :on=>:foo_boo, :force=>false) rescue
|
|
65
82
|
@taggable.tag_list_on(:foo_boo).should be_empty
|
|
66
83
|
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context "when called by multiple tagger's" do
|
|
87
|
+
before(:each) do
|
|
88
|
+
@user_x = TaggableUser.create(:name => "User X")
|
|
89
|
+
@user_y = TaggableUser.create(:name => "User Y")
|
|
90
|
+
@taggable = TaggableModel.create(:name => 'acts_as_taggable_on', :tag_list => 'plugin')
|
|
91
|
+
|
|
92
|
+
@user_x.tag(@taggable, :with => 'ruby, rails', :on => :tags)
|
|
93
|
+
@user_y.tag(@taggable, :with => 'ruby, plugin', :on => :tags)
|
|
67
94
|
|
|
95
|
+
@user_y.tag(@taggable, :with => '', :on => :tags)
|
|
96
|
+
@user_y.tag(@taggable, :with => '', :on => :tags)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "should delete owned tags" do
|
|
100
|
+
@user_y.owned_tags.should == []
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "should not delete other taggers tags" do
|
|
104
|
+
@user_x.owned_tags.should have(2).items
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "should not delete original tags" do
|
|
108
|
+
@taggable.all_tags_list_on(:tags).should include('plugin')
|
|
109
|
+
end
|
|
68
110
|
end
|
|
69
|
-
|
|
70
111
|
end
|
|
71
112
|
|
|
72
113
|
end
|
|
@@ -20,6 +20,18 @@ describe TagList do
|
|
|
20
20
|
@tag_list.include?("wicked").should be_true
|
|
21
21
|
end
|
|
22
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
|
+
|
|
23
35
|
it "should be able to add an array of words" do
|
|
24
36
|
@tag_list.add(["cool", "wicked"], :parse => true)
|
|
25
37
|
@tag_list.include?("cool").should be_true
|
|
@@ -49,4 +61,10 @@ describe TagList do
|
|
|
49
61
|
@tag_list.add("cool","rad,bodacious")
|
|
50
62
|
@tag_list.to_s.should == "awesome, radical, cool, \"rad,bodacious\""
|
|
51
63
|
end
|
|
64
|
+
|
|
65
|
+
it "should be able to call to_s on a frozen tag list" do
|
|
66
|
+
@tag_list.freeze
|
|
67
|
+
lambda { @tag_list.add("cool","rad,bodacious") }.should raise_error
|
|
68
|
+
lambda { @tag_list.to_s }.should_not raise_error
|
|
69
|
+
end
|
|
52
70
|
end
|
|
@@ -2,25 +2,36 @@ require File.dirname(__FILE__) + '/../spec_helper'
|
|
|
2
2
|
|
|
3
3
|
describe Tag do
|
|
4
4
|
before(:each) do
|
|
5
|
+
clean_database!
|
|
5
6
|
@tag = Tag.new
|
|
6
|
-
@user = TaggableModel.create(:name => "Pablo")
|
|
7
|
-
Tag.delete_all
|
|
7
|
+
@user = TaggableModel.create(:name => "Pablo")
|
|
8
8
|
end
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
describe "named like any" do
|
|
11
|
+
before(:each) do
|
|
12
|
+
Tag.create(:name => "awesome")
|
|
13
|
+
Tag.create(:name => "epic")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should find both tags" do
|
|
17
|
+
Tag.named_like_any(["awesome", "epic"]).should have(2).items
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
10
21
|
describe "find or create by name" do
|
|
11
22
|
before(:each) do
|
|
12
23
|
@tag.name = "awesome"
|
|
13
24
|
@tag.save
|
|
14
25
|
end
|
|
15
|
-
|
|
26
|
+
|
|
16
27
|
it "should find by name" do
|
|
17
28
|
Tag.find_or_create_with_like_by_name("awesome").should == @tag
|
|
18
29
|
end
|
|
19
|
-
|
|
30
|
+
|
|
20
31
|
it "should find by name case insensitive" do
|
|
21
32
|
Tag.find_or_create_with_like_by_name("AWESOME").should == @tag
|
|
22
33
|
end
|
|
23
|
-
|
|
34
|
+
|
|
24
35
|
it "should create by name" do
|
|
25
36
|
lambda {
|
|
26
37
|
Tag.find_or_create_with_like_by_name("epic")
|
|
@@ -28,35 +39,66 @@ describe Tag do
|
|
|
28
39
|
end
|
|
29
40
|
end
|
|
30
41
|
|
|
42
|
+
describe "find or create all by any name" do
|
|
43
|
+
before(:each) do
|
|
44
|
+
@tag.name = "awesome"
|
|
45
|
+
@tag.save
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should find by name" do
|
|
49
|
+
Tag.find_or_create_all_with_like_by_name("awesome").should == [@tag]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should find by name case insensitive" do
|
|
53
|
+
Tag.find_or_create_all_with_like_by_name("AWESOME").should == [@tag]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should create by name" do
|
|
57
|
+
lambda {
|
|
58
|
+
Tag.find_or_create_all_with_like_by_name("epic")
|
|
59
|
+
}.should change(Tag, :count).by(1)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should find or create by name" do
|
|
63
|
+
lambda {
|
|
64
|
+
Tag.find_or_create_all_with_like_by_name("awesome", "epic").map(&:name).should == ["awesome", "epic"]
|
|
65
|
+
}.should change(Tag, :count).by(1)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "should return an empty array if no tags are specified" do
|
|
69
|
+
Tag.find_or_create_all_with_like_by_name([]).should == []
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
31
73
|
it "should require a name" do
|
|
32
74
|
@tag.valid?
|
|
33
|
-
@tag.errors
|
|
75
|
+
@tag.errors[:name].should == ["can't be blank"]
|
|
34
76
|
@tag.name = "something"
|
|
35
77
|
@tag.valid?
|
|
36
|
-
@tag.errors
|
|
78
|
+
@tag.errors[:name].should == []
|
|
37
79
|
end
|
|
38
|
-
|
|
80
|
+
|
|
39
81
|
it "should equal a tag with the same name" do
|
|
40
82
|
@tag.name = "awesome"
|
|
41
83
|
new_tag = Tag.new(:name => "awesome")
|
|
42
84
|
new_tag.should == @tag
|
|
43
85
|
end
|
|
44
|
-
|
|
86
|
+
|
|
45
87
|
it "should return its name when to_s is called" do
|
|
46
88
|
@tag.name = "cool"
|
|
47
89
|
@tag.to_s.should == "cool"
|
|
48
90
|
end
|
|
49
|
-
|
|
91
|
+
|
|
50
92
|
it "have named_scope named(something)" do
|
|
51
93
|
@tag.name = "cool"
|
|
52
94
|
@tag.save!
|
|
53
95
|
Tag.named('cool').should include(@tag)
|
|
54
96
|
end
|
|
55
|
-
|
|
97
|
+
|
|
56
98
|
it "have named_scope named_like(something)" do
|
|
57
99
|
@tag.name = "cool"
|
|
58
100
|
@tag.save!
|
|
59
101
|
@another_tag = Tag.create!(:name => "coolip")
|
|
60
102
|
Tag.named_like('cool').should include(@tag, @another_tag)
|
|
61
103
|
end
|
|
62
|
-
end
|
|
104
|
+
end
|