dm-is-taggable 0.9.7
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/History.txt +1 -0
- data/LICENSE +20 -0
- data/Manifest.txt +18 -0
- data/README.txt +5 -0
- data/Rakefile +51 -0
- data/TODO +5 -0
- data/lib/dm-is-taggable/is/tag.rb +17 -0
- data/lib/dm-is-taggable/is/taggable.rb +119 -0
- data/lib/dm-is-taggable/is/tagger.rb +68 -0
- data/lib/dm-is-taggable/is/tagging.rb +8 -0
- data/lib/dm-is-taggable/is/version.rb +7 -0
- data/lib/dm-is-taggable.rb +25 -0
- data/spec/classes.rb +27 -0
- data/spec/integration/tag_spec.rb +25 -0
- data/spec/integration/taggable_spec.rb +176 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +32 -0
- data/tasks/hoe.rb +39 -0
- metadata +103 -0
data/History.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Maxime Guilbot, Aaron Qian and Abdul-Rahman Advany
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
TODO
|
7
|
+
lib/dm-is-taggable.rb
|
8
|
+
lib/dm-is-taggable/is/tag.rb
|
9
|
+
lib/dm-is-taggable/is/taggable.rb
|
10
|
+
lib/dm-is-taggable/is/tagger.rb
|
11
|
+
lib/dm-is-taggable/is/tagging.rb
|
12
|
+
lib/dm-is-taggable/is/version.rb
|
13
|
+
spec/classes.rb
|
14
|
+
spec/integration/tag_spec.rb
|
15
|
+
spec/integration/taggable_spec.rb
|
16
|
+
spec/spec.opts
|
17
|
+
spec/spec_helper.rb
|
18
|
+
tasks/hoe.rb
|
data/README.txt
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
ROOT = Pathname(__FILE__).dirname.expand_path
|
7
|
+
require ROOT + 'lib/dm-is-taggable/is/version'
|
8
|
+
|
9
|
+
AUTHOR = "Maxime Guilbot"
|
10
|
+
EMAIL = "maxime [a] ekohe [d] com"
|
11
|
+
GEM_NAME = "dm-is-taggable"
|
12
|
+
GEM_VERSION = DataMapper::Is::Taggable::VERSION
|
13
|
+
GEM_DEPENDENCIES = [["dm-core", GEM_VERSION], ["dm-is-remixable", GEM_VERSION]]
|
14
|
+
GEM_CLEAN = ["log", "pkg"]
|
15
|
+
GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.txt LICENSE TODO ] }
|
16
|
+
|
17
|
+
PROJECT_NAME = "dm-is-taggable"
|
18
|
+
PROJECT_URL = "http://github.com/maxime/dm-is-taggable/wikis"
|
19
|
+
PROJECT_DESCRIPTION = PROJECT_SUMMARY = "Taggable of a DataMapper plugin"
|
20
|
+
|
21
|
+
require 'tasks/hoe'
|
22
|
+
|
23
|
+
task :default => [ :spec ]
|
24
|
+
|
25
|
+
WIN32 = (RUBY_PLATFORM =~ /win32|mingw|cygwin/) rescue nil
|
26
|
+
SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
|
27
|
+
|
28
|
+
desc "Install #{GEM_NAME} #{GEM_VERSION}"
|
29
|
+
task :install => [ :package ] do
|
30
|
+
sh "#{SUDO} gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources", :verbose => false
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Uninstall #{GEM_NAME} #{GEM_VERSION} (default ruby)"
|
34
|
+
task :uninstall => [ :clobber ] do
|
35
|
+
sh "#{SUDO} gem uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x", :verbose => false
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'Run specifications'
|
39
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
40
|
+
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
41
|
+
t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s)
|
42
|
+
|
43
|
+
begin
|
44
|
+
t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
|
45
|
+
t.rcov_opts << "--exclude 'config,spec,#{Gem::path.join(',')}'"
|
46
|
+
t.rcov_opts << '--text-summary'
|
47
|
+
t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
|
48
|
+
rescue Exception
|
49
|
+
# rcov not installed
|
50
|
+
end
|
51
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class Tag
|
2
|
+
include DataMapper::Resource
|
3
|
+
|
4
|
+
property :id, Serial
|
5
|
+
property :name, String, :unique => true
|
6
|
+
|
7
|
+
before :save, :strip_name
|
8
|
+
|
9
|
+
def self.build(name)
|
10
|
+
name = name.strip
|
11
|
+
Tag.first(:name => name) || Tag.create(:name => name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def strip_name
|
15
|
+
self.name = self.name.strip if self.name
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Is
|
3
|
+
module Taggable
|
4
|
+
|
5
|
+
##
|
6
|
+
# fired when your plugin gets included into Resource
|
7
|
+
#
|
8
|
+
def self.included(base)
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Methods that should be included in DataMapper::Model.
|
14
|
+
# Normally this should just be your generator, so that the namespace
|
15
|
+
# does not get cluttered. ClassMethods and InstanceMethods gets added
|
16
|
+
# in the specific resources when you fire is :example
|
17
|
+
##
|
18
|
+
|
19
|
+
def is_taggable(options={})
|
20
|
+
|
21
|
+
# Add class-methods
|
22
|
+
extend DataMapper::Is::Taggable::ClassMethods
|
23
|
+
# Add instance-methods
|
24
|
+
include DataMapper::Is::Taggable::InstanceMethods
|
25
|
+
|
26
|
+
# Make the magic happen
|
27
|
+
options[:by] ||= []
|
28
|
+
|
29
|
+
taggers_associations = ""
|
30
|
+
options[:by].each do |tagger_class|
|
31
|
+
taggers_associations << "belongs_to :#{Extlib::Inflection.underscore(tagger_class.to_s)}\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
class_eval <<-RUBY
|
35
|
+
remix n, :taggings
|
36
|
+
|
37
|
+
enhance :taggings do
|
38
|
+
belongs_to :tag
|
39
|
+
belongs_to :#{Extlib::Inflection.underscore(self.to_s)}
|
40
|
+
#{taggers_associations}
|
41
|
+
end
|
42
|
+
|
43
|
+
has n, :tags, :through => :#{Extlib::Inflection.underscore(self.to_s)}_tags
|
44
|
+
RUBY
|
45
|
+
|
46
|
+
Tag.class_eval <<-RUBY
|
47
|
+
has n, :#{Extlib::Inflection.underscore(self.to_s)}_tags
|
48
|
+
has n, :#{Extlib::Inflection.underscore(self.to_s).pluralize}, :through => :#{Extlib::Inflection.underscore(self.to_s)}_tags
|
49
|
+
RUBY
|
50
|
+
|
51
|
+
options[:by].each do |tagger_class|
|
52
|
+
tagger_class.class_eval <<-RUBY
|
53
|
+
is :tagger, :for => [#{self}]
|
54
|
+
RUBY
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module ClassMethods
|
59
|
+
def taggable?
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def tagged_with(tags)
|
64
|
+
# tags can be an object or an array
|
65
|
+
tags = [tags] unless tags.class == Array
|
66
|
+
|
67
|
+
# Transform Strings to Tags if necessary
|
68
|
+
tags.collect!{|t| t.class == Tag ? t : Tag.build(t)}
|
69
|
+
|
70
|
+
# Query the objects tagged with those tags
|
71
|
+
taggings = Extlib::Inflection::constantize("#{self.to_s}Tag").all(:tag_id.in => tags.collect{|t| t.id})
|
72
|
+
taggings.collect{|tagging| tagging.send(Extlib::Inflection::underscore(self.to_s)) }
|
73
|
+
end
|
74
|
+
end # ClassMethods
|
75
|
+
|
76
|
+
module InstanceMethods
|
77
|
+
def tag(tags)
|
78
|
+
tags = [tags] unless tags.class == Array
|
79
|
+
|
80
|
+
tags.each do |tag_name|
|
81
|
+
tag_name = Tag.build(tag_name) if tag_name.class == String
|
82
|
+
next if self.send("#{Extlib::Inflection::underscore(self.class.to_s)}_tags").first(:tag_id => tag_name.id)
|
83
|
+
|
84
|
+
p = Extlib::Inflection::constantize("#{self.class.to_s}Tag").new(:tag => tag_name)
|
85
|
+
self.send("#{Extlib::Inflection::underscore(self.class.to_s)}_tags") << p
|
86
|
+
p.save unless self.new_record?
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def untag(tags)
|
91
|
+
tags = [tags] unless tags.class == Array
|
92
|
+
|
93
|
+
tags.each do |tag_name|
|
94
|
+
tag_name = Tag.build(tag_name) if tag_name.class == String
|
95
|
+
|
96
|
+
p = self.send("#{Extlib::Inflection::underscore(self.class.to_s)}_tags").first(:tag_id => tag_name.id)
|
97
|
+
p.destroy if p
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def tags_list
|
102
|
+
@tags_list || self.tags.collect {|t| t.name}.join(", ")
|
103
|
+
end
|
104
|
+
|
105
|
+
def tags_list=(list)
|
106
|
+
@tags_list = list
|
107
|
+
self.tags.each {|t| self.untag(t) }
|
108
|
+
|
109
|
+
# Tag list generation
|
110
|
+
list = list.split(",").collect {|s| s.strip}
|
111
|
+
|
112
|
+
# Do the tagging here
|
113
|
+
list.each { |t| self.tag(Tag.build(t)) }
|
114
|
+
end
|
115
|
+
end # InstanceMethods
|
116
|
+
|
117
|
+
end # Taggable
|
118
|
+
end # Is
|
119
|
+
end # DataMapper
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Is
|
3
|
+
module Tagger
|
4
|
+
##
|
5
|
+
# fired when your plugin gets included into Resource
|
6
|
+
#
|
7
|
+
def self.included(base)
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# Methods that should be included in DataMapper::Model.
|
13
|
+
# Normally this should just be your generator, so that the namespace
|
14
|
+
# does not get cluttered. ClassMethods and InstanceMethods gets added
|
15
|
+
# in the specific resources when you fire is :example
|
16
|
+
##
|
17
|
+
|
18
|
+
def is_tagger(options={})
|
19
|
+
unless self.respond_to?(:tagger?)
|
20
|
+
# Add class-methods
|
21
|
+
extend DataMapper::Is::Tagger::ClassMethods
|
22
|
+
|
23
|
+
# Add instance-methods
|
24
|
+
include DataMapper::Is::Tagger::InstanceMethods
|
25
|
+
|
26
|
+
cattr_accessor(:taggable_object_classes)
|
27
|
+
self.taggable_object_classes = []
|
28
|
+
end
|
29
|
+
|
30
|
+
raise "options[:for] is missing" unless options[:for]
|
31
|
+
|
32
|
+
add_taggable_object_classes(options[:for])
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
def tagger?
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_taggable_object_classes(taggable_object_classes)
|
41
|
+
taggable_object_classes.each do |taggable_object_class|
|
42
|
+
self.taggable_object_classes << taggable_object_class
|
43
|
+
self.has n, "#{taggable_object_class.storage_name.singular}_tags".intern
|
44
|
+
self.has n, taggable_object_class.storage_name.intern, :through => "#{taggable_object_class.storage_name.singular}_tags".intern
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end # ClassMethods
|
48
|
+
|
49
|
+
module InstanceMethods
|
50
|
+
def tag(object, options={})
|
51
|
+
raise "Object of type #{object.class} isn't taggable!" unless self.taggable_object_classes.include?(object.class)
|
52
|
+
|
53
|
+
tags = options[:with]
|
54
|
+
tags = [tags] if tags.class != Array
|
55
|
+
|
56
|
+
tags.each do |tag|
|
57
|
+
join_row_class = Extlib::Inflection::constantize("#{object.class.to_s}Tag")
|
58
|
+
join_row = join_row_class.new(:tag => tag,
|
59
|
+
Extlib::Inflection::underscore(self.class.to_s).intern => self)
|
60
|
+
object.send("#{Extlib::Inflection::underscore(object.class.to_s)}_tags") << join_row
|
61
|
+
join_row.save
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end # InstanceMethods
|
65
|
+
|
66
|
+
end # Tagger
|
67
|
+
end # Is
|
68
|
+
end # DataMapper
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Needed to import datamapper and other gems
|
2
|
+
require 'rubygems'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
# Add all external dependencies for the plugin here
|
6
|
+
gem 'dm-core', '=0.9.7'
|
7
|
+
require 'dm-core'
|
8
|
+
|
9
|
+
require 'dm-is-remixable'
|
10
|
+
|
11
|
+
# Require plugin-files
|
12
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'is' / 'taggable.rb'
|
13
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'is' / 'tag.rb'
|
14
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'is' / 'tagging.rb'
|
15
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'is' / 'tagger.rb'
|
16
|
+
|
17
|
+
# Include the plugin in Resource
|
18
|
+
module DataMapper
|
19
|
+
module Resource
|
20
|
+
module ClassMethods
|
21
|
+
include DataMapper::Is::Taggable
|
22
|
+
include DataMapper::Is::Tagger
|
23
|
+
end # module ClassMethods
|
24
|
+
end # module Resource
|
25
|
+
end # module DataMapper
|
data/spec/classes.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
class Post
|
2
|
+
include DataMapper::Resource
|
3
|
+
|
4
|
+
property :id, Serial
|
5
|
+
property :name, String
|
6
|
+
property :description, Text
|
7
|
+
|
8
|
+
is :taggable
|
9
|
+
end
|
10
|
+
|
11
|
+
class User
|
12
|
+
include DataMapper::Resource
|
13
|
+
|
14
|
+
property :id, Serial
|
15
|
+
property :login, String
|
16
|
+
end
|
17
|
+
|
18
|
+
class Book
|
19
|
+
include DataMapper::Resource
|
20
|
+
|
21
|
+
property :id, Serial
|
22
|
+
property :isbn, String, :length => 13, :nullable => false
|
23
|
+
property :title, String, :nullable => false
|
24
|
+
property :author, String
|
25
|
+
|
26
|
+
is :taggable, :by => [User]
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
|
3
|
+
|
4
|
+
describe 'Tag' do
|
5
|
+
before :all do
|
6
|
+
Tag.all.destroy!
|
7
|
+
end
|
8
|
+
|
9
|
+
before do
|
10
|
+
@tag = Tag.new
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have id and name columns" do
|
14
|
+
@tag.attributes.should have_key(:id)
|
15
|
+
@tag.attributes.should have_key(:name)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should strip the tag name" do
|
19
|
+
@tag.name = "blue "
|
20
|
+
@tag.save
|
21
|
+
@tag.name.should == "blue"
|
22
|
+
second_tag = Tag.build("blue ")
|
23
|
+
second_tag.should == @tag
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
|
3
|
+
|
4
|
+
describe 'DataMapper::Is::Taggable' do
|
5
|
+
before :all do
|
6
|
+
Post.all.destroy!
|
7
|
+
@post = Post.create(:name => "My First Post")
|
8
|
+
@blue = Tag.build('blue')
|
9
|
+
@yellow = Tag.build('yellow')
|
10
|
+
|
11
|
+
Book.all.destroy!
|
12
|
+
@book = Book.create(:title => "Wonderful world", :isbn => "1234567890123", :author => "Awesome author")
|
13
|
+
@fiction = Tag.build('fiction')
|
14
|
+
@english = Tag.build('english')
|
15
|
+
|
16
|
+
User.all.destroy!
|
17
|
+
@bob = User.create(:login => 'bob')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should add a taggable? method that return true" do
|
21
|
+
Post.should respond_to(:taggable?)
|
22
|
+
Post.taggable?.should == true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should add a tag method for tagging purposes" do
|
26
|
+
@post.should respond_to(:tag)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should add a tags method for getting all the tags in an array" do
|
30
|
+
@post.should respond_to(:tags)
|
31
|
+
@post.tags.should be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should add a tag_list method for getting the tag list" do
|
35
|
+
@post.tags_list.should == ""
|
36
|
+
|
37
|
+
@post.tag(@blue)
|
38
|
+
@post.reload
|
39
|
+
@post.tags_list.should == "blue"
|
40
|
+
|
41
|
+
@post.tag(@yellow)
|
42
|
+
@post.reload
|
43
|
+
@post.tags_list.should == "blue, yellow"
|
44
|
+
|
45
|
+
@post.untag(@blue)
|
46
|
+
@post.untag(@yellow)
|
47
|
+
|
48
|
+
@post.reload
|
49
|
+
@post.tags_list.should == ""
|
50
|
+
end
|
51
|
+
|
52
|
+
# Post tagging
|
53
|
+
it "should be able to tag a post" do
|
54
|
+
@post.tag('blue')
|
55
|
+
@post.tags.reload
|
56
|
+
@post.tags.should have(1).thing
|
57
|
+
@post.tags.should include(@blue)
|
58
|
+
|
59
|
+
@post.tag(['yellow', @blue])
|
60
|
+
@post.tags.reload
|
61
|
+
@post.tags.should have(2).things
|
62
|
+
@post.tags.should include(@blue)
|
63
|
+
@post.tags.should include(@yellow)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get post from tags
|
67
|
+
it "should be able to get posts tagged with a tag" do
|
68
|
+
@yellow.posts.should have(1).thing
|
69
|
+
@yellow.posts.first.should == @post
|
70
|
+
|
71
|
+
@blue.posts.should have(1).thing
|
72
|
+
@blue.posts.first.should == @post
|
73
|
+
end
|
74
|
+
|
75
|
+
# Post Untagging
|
76
|
+
it "should be able to untag a post" do
|
77
|
+
@post.untag(@blue)
|
78
|
+
@post.tags.reload
|
79
|
+
@post.tags.should_not include(@blue)
|
80
|
+
|
81
|
+
@blue.posts.reload
|
82
|
+
@blue.posts.should be_empty
|
83
|
+
end
|
84
|
+
|
85
|
+
# Book tagging
|
86
|
+
it "should be able to tag a book without tagger" do
|
87
|
+
@book.tag(@fiction)
|
88
|
+
@book.tags.reload
|
89
|
+
@book.tags.should have(1).thing
|
90
|
+
@book.tags.should include(@fiction)
|
91
|
+
|
92
|
+
@book.tag(@english)
|
93
|
+
@book.tags.reload
|
94
|
+
@book.tags.should have(2).things
|
95
|
+
@book.tags.should include(@fiction)
|
96
|
+
@book.tags.should include(@english)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Get books from tags
|
100
|
+
it "should be able to get books tagged with a tag" do
|
101
|
+
@english.books.should have(1).thing
|
102
|
+
@english.books.first.should == @book
|
103
|
+
|
104
|
+
@fiction.books.should have(1).thing
|
105
|
+
@fiction.books.first.should == @book
|
106
|
+
end
|
107
|
+
|
108
|
+
# Book Untagging
|
109
|
+
it "should be able to untag a book" do
|
110
|
+
@book.untag(@english)
|
111
|
+
@book.tags.reload
|
112
|
+
@book.tags.should_not include(@english)
|
113
|
+
@book.tags.should include(@fiction)
|
114
|
+
|
115
|
+
@english.posts.reload
|
116
|
+
@english.posts.should be_empty
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should provide a method for listing the books tagged by bob" do
|
120
|
+
@bob.books.should be_empty
|
121
|
+
end
|
122
|
+
|
123
|
+
it "bob user should be able to tag a book as a tagger" do
|
124
|
+
@scifi = Tag.build('scifi')
|
125
|
+
@bob.tag(@book, :with => @scifi)
|
126
|
+
|
127
|
+
@bob.books.reload
|
128
|
+
@bob.books.should have(1).thing
|
129
|
+
@bob.books.should include(@book)
|
130
|
+
|
131
|
+
@scifi.books.should have(1).thing
|
132
|
+
@scifi.books.should include(@book)
|
133
|
+
|
134
|
+
@book.tags.reload
|
135
|
+
@book.tags.should have(2).thing
|
136
|
+
@book.tags.should include(@scifi) # tagged by bob
|
137
|
+
@book.tags.should include(@fiction) # without taggers
|
138
|
+
end
|
139
|
+
|
140
|
+
it "User should be tagger" do
|
141
|
+
User.should be_tagger
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should be able to tag a book with the tags_list= helper" do
|
145
|
+
@book.tags_list = ""
|
146
|
+
@book.tags.reload
|
147
|
+
@book.tags.should be_empty
|
148
|
+
|
149
|
+
@book.tags_list = "orange, red"
|
150
|
+
@book.tags.reload
|
151
|
+
@book.tags.should have(2).things
|
152
|
+
@book.tags.should include(Tag.build('orange'))
|
153
|
+
@book.tags.should include(Tag.build('red'))
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should be able to tag a newly created object with tags_list=" do
|
157
|
+
new_book = Book.new(:title => "Awesome world", :isbn => "1234567890124", :author => "Wonderful author", :tags_list => "new, awesome, book")
|
158
|
+
new_book.save
|
159
|
+
|
160
|
+
new_book.reload
|
161
|
+
new_book.tags.should have(3).things
|
162
|
+
['new', 'awesome', 'book'].each do |tag|
|
163
|
+
new_book.tags.should include(Tag.build(tag))
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should be able to get the books tagged with a specific tag" do
|
168
|
+
Book.tagged_with('orange').should have(1).thing
|
169
|
+
Book.tagged_with('orange').should include(@book)
|
170
|
+
|
171
|
+
Post.tagged_with('blue').should have(0).thing
|
172
|
+
Post.tagged_with('yellow').should have(1).thing
|
173
|
+
Post.tagged_with([@yellow, @blue]).should have(1).thing
|
174
|
+
Post.tagged_with(['yellow', 'blue']).should include(@post)
|
175
|
+
end
|
176
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'rspec', '>=1.1.3'
|
3
|
+
require 'spec'
|
4
|
+
require 'pathname'
|
5
|
+
require Pathname(__FILE__).dirname.expand_path.parent + 'lib/dm-is-taggable'
|
6
|
+
|
7
|
+
def load_driver(name, default_uri)
|
8
|
+
return false if ENV['ADAPTER'] != name.to_s
|
9
|
+
|
10
|
+
lib = "do_#{name}"
|
11
|
+
|
12
|
+
begin
|
13
|
+
gem lib, '>=0.9.5'
|
14
|
+
require lib
|
15
|
+
DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
|
16
|
+
DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
|
17
|
+
true
|
18
|
+
rescue Gem::LoadError => e
|
19
|
+
warn "Could not load #{lib}: #{e}"
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
ENV['ADAPTER'] ||= 'sqlite3'
|
25
|
+
|
26
|
+
HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
|
27
|
+
HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_is_taggable_test')
|
28
|
+
HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_is_taggable_test')
|
29
|
+
|
30
|
+
require File.dirname(__FILE__) + '/classes'
|
31
|
+
|
32
|
+
DataMapper.auto_migrate!
|
data/tasks/hoe.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'hoe'
|
2
|
+
|
3
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
4
|
+
@config = nil
|
5
|
+
RUBYFORGE_USERNAME = "unknown"
|
6
|
+
def rubyforge_username
|
7
|
+
unless @config
|
8
|
+
begin
|
9
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
10
|
+
rescue
|
11
|
+
puts <<-EOS
|
12
|
+
ERROR: No rubyforge config file found: #{@config_file}
|
13
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
14
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
15
|
+
EOS
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
end
|
19
|
+
RUBYFORGE_USERNAME.replace @config["username"]
|
20
|
+
end
|
21
|
+
|
22
|
+
hoe = Hoe.new(GEM_NAME, GEM_VERSION) do |p|
|
23
|
+
|
24
|
+
p.developer(AUTHOR, EMAIL)
|
25
|
+
|
26
|
+
p.description = PROJECT_DESCRIPTION
|
27
|
+
p.summary = PROJECT_SUMMARY
|
28
|
+
p.url = PROJECT_URL
|
29
|
+
|
30
|
+
p.rubyforge_name = PROJECT_NAME if PROJECT_NAME
|
31
|
+
|
32
|
+
p.clean_globs |= GEM_CLEAN
|
33
|
+
p.spec_extras = GEM_EXTRAS if GEM_EXTRAS
|
34
|
+
|
35
|
+
GEM_DEPENDENCIES.each do |dep|
|
36
|
+
p.extra_deps << dep
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dm-is-taggable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Maxime Guilbot
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-02 00:00:00 +08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: dm-core
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - "="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.9.7
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: dm-is-remixable
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.9.7
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.8.2
|
44
|
+
version:
|
45
|
+
description: Taggable of a DataMapper plugin
|
46
|
+
email:
|
47
|
+
- maxime [a] ekohe [d] com
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- README.txt
|
54
|
+
- LICENSE
|
55
|
+
- TODO
|
56
|
+
files:
|
57
|
+
- History.txt
|
58
|
+
- LICENSE
|
59
|
+
- Manifest.txt
|
60
|
+
- README.txt
|
61
|
+
- Rakefile
|
62
|
+
- TODO
|
63
|
+
- lib/dm-is-taggable.rb
|
64
|
+
- lib/dm-is-taggable/is/tag.rb
|
65
|
+
- lib/dm-is-taggable/is/taggable.rb
|
66
|
+
- lib/dm-is-taggable/is/tagger.rb
|
67
|
+
- lib/dm-is-taggable/is/tagging.rb
|
68
|
+
- lib/dm-is-taggable/is/version.rb
|
69
|
+
- spec/classes.rb
|
70
|
+
- spec/integration/tag_spec.rb
|
71
|
+
- spec/integration/taggable_spec.rb
|
72
|
+
- spec/spec.opts
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
- tasks/hoe.rb
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: http://github.com/maxime/dm-is-taggable/wikis
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options:
|
79
|
+
- --main
|
80
|
+
- README.txt
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: "0"
|
88
|
+
version:
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: "0"
|
94
|
+
version:
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project: dm-is-taggable
|
98
|
+
rubygems_version: 1.3.1
|
99
|
+
signing_key:
|
100
|
+
specification_version: 2
|
101
|
+
summary: Taggable of a DataMapper plugin
|
102
|
+
test_files: []
|
103
|
+
|