genki-dm-is-taggable 0.1
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 +25 -0
- data/README.textile +185 -0
- data/Rakefile +52 -0
- data/TODO +2 -0
- data/lib/dm-is-taggable.rb +31 -0
- data/lib/dm-is-taggable/aggregate_patch.rb +47 -0
- data/lib/dm-is-taggable/is/shared.rb +111 -0
- data/lib/dm-is-taggable/is/tag.rb +189 -0
- data/lib/dm-is-taggable/is/taggable.rb +114 -0
- data/lib/dm-is-taggable/is/tagger.rb +80 -0
- data/lib/dm-is-taggable/is/tagging.rb +30 -0
- data/lib/dm-is-taggable/is/version.rb +8 -0
- data/lib/dm-is-taggable/tag.rb +4 -0
- data/lib/dm-is-taggable/tag_list.rb +113 -0
- data/lib/dm-is-taggable/tagging.rb +4 -0
- data/spec/data/article.rb +5 -0
- data/spec/data/bot.rb +6 -0
- data/spec/data/picture.rb +5 -0
- data/spec/data/user.rb +6 -0
- data/spec/integration/taggable_spec.rb +289 -0
- data/spec/integration/tagger_similarity_spec.rb +40 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +36 -0
- metadata +110 -0
data/History.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Aaron Qian, Maxime Guilbot at Ekohe
|
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,25 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README.textile
|
5
|
+
Rakefile
|
6
|
+
TODO
|
7
|
+
lib/dm-is-taggable.rb
|
8
|
+
lib/dm-is-taggable/aggregate_patch.rb
|
9
|
+
lib/dm-is-taggable/tag.rb
|
10
|
+
lib/dm-is-taggable/tagging.rb
|
11
|
+
lib/dm-is-taggable/tag_list.rb
|
12
|
+
lib/dm-is-taggable/is/taggable.rb
|
13
|
+
lib/dm-is-taggable/is/version.rb
|
14
|
+
lib/dm-is-taggable/is/shared.rb
|
15
|
+
lib/dm-is-taggable/is/tag.rb
|
16
|
+
lib/dm-is-taggable/is/tagging.rb
|
17
|
+
lib/dm-is-taggable/is/tagger.rb
|
18
|
+
spec/integration/taggable_spec.rb
|
19
|
+
spec/integration/tagger_similarity_spec.rb
|
20
|
+
spec/data/article.rb
|
21
|
+
spec/data/picture.rb
|
22
|
+
spec/data/bot.rb
|
23
|
+
spec/data/user.rb
|
24
|
+
spec/spec.opts
|
25
|
+
spec/spec_helper.rb
|
data/README.textile
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
h1. dm-is-taggable
|
2
|
+
|
3
|
+
dm-is-taggable is a tagging system built for datamapper. It has supports for multiple tagger types and taggable types.
|
4
|
+
Each tagger can tag different taggable objects.
|
5
|
+
|
6
|
+
|
7
|
+
h2. Installation
|
8
|
+
|
9
|
+
h3. Download the plugin
|
10
|
+
|
11
|
+
In your console:
|
12
|
+
<pre><code>
|
13
|
+
git clone git://github.com/aq1018/dm-is-taggable.git
|
14
|
+
</code></pre>
|
15
|
+
|
16
|
+
h3. Install the gem
|
17
|
+
|
18
|
+
In your console:
|
19
|
+
<pre><code>
|
20
|
+
cd dm-is-taggable
|
21
|
+
sudo rake install
|
22
|
+
</code></pre>
|
23
|
+
|
24
|
+
h3. Include it Merb
|
25
|
+
|
26
|
+
In merb init.rb:
|
27
|
+
|
28
|
+
<pre><code>
|
29
|
+
dependency "dm-is-taggable"
|
30
|
+
</code></pre>
|
31
|
+
|
32
|
+
|
33
|
+
h2. Using dm-is-taggable in your code
|
34
|
+
|
35
|
+
h3. Define taggers
|
36
|
+
|
37
|
+
<pre><code>
|
38
|
+
class User
|
39
|
+
include DataMapper::Resource
|
40
|
+
property :id, Serial
|
41
|
+
property :name, String
|
42
|
+
is :tagger, :on => ["Article", "Picture"]
|
43
|
+
end
|
44
|
+
|
45
|
+
class Bot
|
46
|
+
include DataMapper::Resource
|
47
|
+
property :id, Serial
|
48
|
+
property :name, String
|
49
|
+
is :tagger, :on => ["Article", "Picture"]
|
50
|
+
end
|
51
|
+
</code></pre>
|
52
|
+
|
53
|
+
h3. Define taggables
|
54
|
+
|
55
|
+
<pre><code>
|
56
|
+
class Article
|
57
|
+
include DataMapper::Resource
|
58
|
+
property :id, Serial
|
59
|
+
is :taggable, :by => ["User", "Bot"]
|
60
|
+
end
|
61
|
+
|
62
|
+
class Picture
|
63
|
+
include DataMapper::Resource
|
64
|
+
property :id, Serial
|
65
|
+
is :taggable, :by => ["User", "Bot"]
|
66
|
+
end
|
67
|
+
</code></pre>
|
68
|
+
|
69
|
+
h3. Create tags
|
70
|
+
|
71
|
+
<pre><code>
|
72
|
+
|
73
|
+
@picture = Picture.first
|
74
|
+
@scott = User.first
|
75
|
+
|
76
|
+
# You can tag like this
|
77
|
+
@picture.tag(:with => "shanghai, bar, beer", :by => @scott)
|
78
|
+
|
79
|
+
# or like this
|
80
|
+
# Note: this doesn't remove the previous tags
|
81
|
+
Tag.as(@scott) do
|
82
|
+
@picture.tag(:with => "cool, tag1, tag2")
|
83
|
+
end
|
84
|
+
|
85
|
+
# or like this
|
86
|
+
# Note, this removes all previous tags
|
87
|
+
Tag.as(@scott) do
|
88
|
+
@picture.taglist("cool, tag1, tag2")
|
89
|
+
end
|
90
|
+
</code></pre>
|
91
|
+
|
92
|
+
h3. Retrieve objects with tags
|
93
|
+
|
94
|
+
<pre><code>
|
95
|
+
|
96
|
+
# find pictures tagged with tag1 and tag2
|
97
|
+
Picture.find(:with => "tag1, tag2")
|
98
|
+
|
99
|
+
# find pictures tagged with tag1 or tag2
|
100
|
+
Picture.find(:with => "tag1, tag2", :match => :any)
|
101
|
+
|
102
|
+
# find pictures tagged with tag1 or tag2, tagged by @user1
|
103
|
+
Picture.find(:with => "tag1, tag2", :match => :any, :by => @user1)
|
104
|
+
|
105
|
+
# find pictures tagged with tag1 or tag2, tagged by all users
|
106
|
+
Picture.find(:with => "tag1, tag2", :match => :any, :by => User)
|
107
|
+
|
108
|
+
# or you can do scoped way
|
109
|
+
|
110
|
+
# find pictures tagged with tag1 or tag2, tagged by all users
|
111
|
+
Tag.as(User) do
|
112
|
+
Picture.find(:with => "tag1, tag2", :match => :any)
|
113
|
+
end
|
114
|
+
|
115
|
+
# find pictures tagged with tag1 or tag2, tagged by @user1
|
116
|
+
Tag.as(@user1) do
|
117
|
+
Picture.find(:with => "tag1, tag2", :match => :any)
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
# You can tag like this
|
122
|
+
@picture.tag(:with => "shanghai, bar, beer", :by => @scott)
|
123
|
+
|
124
|
+
# or like this
|
125
|
+
# Note: this doesn't remove the previous tags
|
126
|
+
Tag.as(@scott) do
|
127
|
+
@picture.tag(:with => "cool, tag1, tag2")
|
128
|
+
end
|
129
|
+
|
130
|
+
# or like this
|
131
|
+
# Note, this removes all previous tags
|
132
|
+
Tag.as(@scott) do
|
133
|
+
@picture.taglist("cool, tag1, tag2")
|
134
|
+
end
|
135
|
+
</code></pre>
|
136
|
+
|
137
|
+
h3. Retrieve tags with objects
|
138
|
+
|
139
|
+
<pre><code>
|
140
|
+
|
141
|
+
@picture1 = Picture.first
|
142
|
+
|
143
|
+
# get all tags associated with @picture2 as a string
|
144
|
+
@picture1.taglist
|
145
|
+
|
146
|
+
# or as tag objects
|
147
|
+
@picture1.tags
|
148
|
+
|
149
|
+
# tags tagged by users
|
150
|
+
@picture1.tags_by_users
|
151
|
+
|
152
|
+
# find tags by all users
|
153
|
+
Tag.by(User)
|
154
|
+
|
155
|
+
# find tags by a user
|
156
|
+
Tag.by(@user)
|
157
|
+
|
158
|
+
# find tags on all pictures
|
159
|
+
Tag.on(Picture)
|
160
|
+
|
161
|
+
# find tags on a picture
|
162
|
+
Tag.on(@picture1)
|
163
|
+
|
164
|
+
# find tags by a user, on all pictures
|
165
|
+
Tag.by(@user).on(Pictures)
|
166
|
+
|
167
|
+
# find tags by all users on a picture
|
168
|
+
Tag.by(User).on(@picture)
|
169
|
+
|
170
|
+
# find tags by a user on a picture
|
171
|
+
Tag.by(@user).on(@picture)
|
172
|
+
|
173
|
+
</code></pre>
|
174
|
+
|
175
|
+
h3. Counting tags
|
176
|
+
|
177
|
+
<pre><code>
|
178
|
+
|
179
|
+
# Count how many articles are tagged by @user1
|
180
|
+
Tag.tagged_count(:by => @user1, :on => Article)
|
181
|
+
|
182
|
+
# Count how many articles are tagged by @user1 with "tag1"
|
183
|
+
Tag.tagged_count(:by => @user1, :on => Article, :with => "tag1")
|
184
|
+
|
185
|
+
</code></pre>
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
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 = "Aaron Qian, Maxime Guilbot"
|
10
|
+
EMAIL = "team [a] ekohe [d] com"
|
11
|
+
GEM_NAME = "dm-is-taggable"
|
12
|
+
GEM_VERSION = DataMapper::Is::Taggable::VERSION
|
13
|
+
GEM_DEPENDENCY_VERSION = DataMapper::Is::Taggable::DEPENDENCY_VERSION
|
14
|
+
GEM_DEPENDENCIES = [["dm-core", GEM_DEPENDENCY_VERSION], ["dm-aggregates", GEM_DEPENDENCY_VERSION]]
|
15
|
+
GEM_CLEAN = ["log", "pkg"]
|
16
|
+
GEM_EXTRAS = { :has_rdoc => false, :extra_rdoc_files => %w[ README.textile LICENSE TODO ] }
|
17
|
+
|
18
|
+
PROJECT_NAME = "dm-is-taggable"
|
19
|
+
PROJECT_URL = "http://github.com/aq1018/dm-is-taggable"
|
20
|
+
PROJECT_DESCRIPTION = PROJECT_SUMMARY = "Tagging implementation for DataMapper"
|
21
|
+
|
22
|
+
require 'tasks/hoe'
|
23
|
+
|
24
|
+
task :default => [ :spec ]
|
25
|
+
|
26
|
+
WIN32 = (RUBY_PLATFORM =~ /win32|mingw|cygwin/) rescue nil
|
27
|
+
SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
|
28
|
+
|
29
|
+
desc "Install #{GEM_NAME} #{GEM_VERSION}"
|
30
|
+
task :install => [ :package ] do
|
31
|
+
sh "#{SUDO} gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources", :verbose => false
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Uninstall #{GEM_NAME} #{GEM_VERSION} (default ruby)"
|
35
|
+
task :uninstall => [ :clobber ] do
|
36
|
+
sh "#{SUDO} gem uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x", :verbose => false
|
37
|
+
end
|
38
|
+
|
39
|
+
desc 'Run specifications'
|
40
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
41
|
+
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
42
|
+
t.spec_files = Pathname.glob(Pathname.new(__FILE__).dirname + 'spec/**/*_spec.rb')
|
43
|
+
|
44
|
+
begin
|
45
|
+
t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
|
46
|
+
t.rcov_opts << '--exclude' << 'spec'
|
47
|
+
t.rcov_opts << '--text-summary'
|
48
|
+
t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
|
49
|
+
rescue Exception
|
50
|
+
# rcov not installed
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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.6'
|
7
|
+
gem 'dm-aggregates', '>=0.9.6'
|
8
|
+
require 'dm-core'
|
9
|
+
require 'dm-aggregates'
|
10
|
+
|
11
|
+
# Require plugin-files
|
12
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'tag_list.rb'
|
13
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'aggregate_patch.rb'
|
14
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'is' / 'shared.rb'
|
15
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'is' / 'taggable.rb'
|
16
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'is' / 'tag.rb'
|
17
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'is' / 'tagging.rb'
|
18
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'is' / 'tagger.rb'
|
19
|
+
|
20
|
+
# Include the plugin in Resource
|
21
|
+
module DataMapper
|
22
|
+
module Resource
|
23
|
+
module ClassMethods
|
24
|
+
include DataMapper::Is::Taggable
|
25
|
+
end # module ClassMethods
|
26
|
+
end # module Resource
|
27
|
+
end # module DataMapper
|
28
|
+
|
29
|
+
# Require the Tag and Tagging Resources
|
30
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'tag.rb'
|
31
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-taggable' / 'tagging.rb'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# we need this unless someone adds the "count(distinct id)" support in dm-aggegrates
|
2
|
+
module DataMapper
|
3
|
+
module Adapters
|
4
|
+
class DataObjectsAdapter
|
5
|
+
module SQL
|
6
|
+
private
|
7
|
+
def fields_statement(query)
|
8
|
+
qualify = query.links.any?
|
9
|
+
query.fields.map { |p| property_to_column_name(query.repository, p, qualify, query.unique?) } * ', '
|
10
|
+
end
|
11
|
+
|
12
|
+
def property_to_column_name(repository, property, qualify, unique=false)
|
13
|
+
case property
|
14
|
+
when Query::Operator
|
15
|
+
aggregate_field_statement(repository, property.operator, property.target, qualify, unique)
|
16
|
+
when Property
|
17
|
+
original_property_to_column_name(repository, property, qualify)
|
18
|
+
when Query::Path
|
19
|
+
original_property_to_column_name(repository, property, qualify)
|
20
|
+
else
|
21
|
+
raise ArgumentError, "+property+ must be a DataMapper::Query::Operator or a DataMapper::Property, but was a #{property.class} (#{property.inspect})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def aggregate_field_statement(repository, aggregate_function, property, qualify, unique=false)
|
26
|
+
column_name = if aggregate_function == :count && property == :all
|
27
|
+
'*'
|
28
|
+
else
|
29
|
+
unique ? "distinct #{property_to_column_name(repository, property, qualify)}" :
|
30
|
+
property_to_column_name(repository, property, qualify)
|
31
|
+
end
|
32
|
+
|
33
|
+
function_name = case aggregate_function
|
34
|
+
when :count then 'COUNT'
|
35
|
+
when :min then 'MIN'
|
36
|
+
when :max then 'MAX'
|
37
|
+
when :avg then 'AVG'
|
38
|
+
when :sum then 'SUM'
|
39
|
+
else raise "Invalid aggregate function: #{aggregate_function.inspect}"
|
40
|
+
end
|
41
|
+
|
42
|
+
"#{function_name}(#{column_name})"
|
43
|
+
end
|
44
|
+
end # module SQL
|
45
|
+
end # class DataObjectsAdapter
|
46
|
+
end # module Adapters
|
47
|
+
end # module DataMapper
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Is
|
3
|
+
module Taggable
|
4
|
+
module SharedClassMethods
|
5
|
+
|
6
|
+
def extract_class_object(obj)
|
7
|
+
return [] if obj.nil?
|
8
|
+
if obj.is_a?(Class)
|
9
|
+
[obj, nil]
|
10
|
+
else
|
11
|
+
[obj.class, obj]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def by(tagger_class_or_obj)
|
16
|
+
tagger_class, tagger_obj = extract_tagger_class_object(tagger_class_or_obj)
|
17
|
+
query = {:unique => true}
|
18
|
+
query.merge!(Tag.taggings.tagger_type => tagger_class.to_s) if tagger_class
|
19
|
+
query.merge!(Tag.taggings.tagger_id => tagger_obj.id) if tagger_obj
|
20
|
+
all(query)
|
21
|
+
end
|
22
|
+
|
23
|
+
def on(taggable_class_or_obj)
|
24
|
+
taggable_class, taggable_obj = extract_taggable_class_object(taggable_class_or_obj)
|
25
|
+
query = {:unique => true}
|
26
|
+
query.merge!(Tag.taggings.taggable_type => taggable_class.to_s) if taggable_class
|
27
|
+
query.merge!(Tag.taggings.taggable_id => taggable_obj.id) if taggable_obj
|
28
|
+
all(query)
|
29
|
+
end
|
30
|
+
|
31
|
+
def extract_options(options)
|
32
|
+
if options.is_a?(Array) || options.is_a?(String)
|
33
|
+
options = {:with => TagList.from(options)}
|
34
|
+
end
|
35
|
+
options = options.dup
|
36
|
+
|
37
|
+
tagger = options.delete(:by)
|
38
|
+
tagger ||= Tag.tagger
|
39
|
+
taggable = options.delete(:on)
|
40
|
+
tags = TagList.from(options.delete(:with))
|
41
|
+
return [tagger, taggable, tags, options]
|
42
|
+
end
|
43
|
+
|
44
|
+
def extract_tagger_class_object(obj)
|
45
|
+
obj = Extlib::Inflection.constantize(obj.to_s.camel_case.singular) if obj && (obj.is_a?(String) || obj.is_a?(Symbol))
|
46
|
+
return [] if obj.nil?
|
47
|
+
|
48
|
+
if obj.tagger?
|
49
|
+
extract_class_object(obj)
|
50
|
+
else
|
51
|
+
raise Exception.new("#{obj} is not a Tagger class or object!")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def extract_taggable_class_object(obj)
|
56
|
+
obj = Extlib::Inflection.constantize(obj.to_s.camel_case.singular) if obj && (obj.is_a?(String) || obj.is_a?(Symbol))
|
57
|
+
return [] if obj.nil?
|
58
|
+
|
59
|
+
if obj.taggable?
|
60
|
+
extract_class_object(obj)
|
61
|
+
else
|
62
|
+
raise Exception.new("#{obj} is not a Taggable class or object!")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_taggings(tagger, taggable, tags=nil)
|
67
|
+
return if tags.nil? || tags.empty?
|
68
|
+
|
69
|
+
unless tagger.nil? || (tagger.respond_to?(:tagger?) && tagger.tagger? && !tagger.is_a?(Class))
|
70
|
+
raise Exception.new("#{tagger} is not a tagger datamapper resource object!")
|
71
|
+
end
|
72
|
+
|
73
|
+
unless taggable.respond_to?(:taggable?) && taggable.taggable? && !taggable.is_a?(Class)
|
74
|
+
raise Exception.new("#{taggable} is not a taggable datamapper resource object!")
|
75
|
+
end
|
76
|
+
|
77
|
+
raise Exception.new("#{tagger.class} cannot Tag #{taggable.class}") unless tagger.nil? || tagger.can_tag_on?(taggable)
|
78
|
+
|
79
|
+
TagList.from(tags).each do |tag|
|
80
|
+
tag_obj = Tag.fetch(tag)
|
81
|
+
|
82
|
+
# build tagging (tagger could be anonymous)
|
83
|
+
tagging_hash = {:tag_id => tag_obj.id, :taggable_id => taggable.id, :taggable_type => taggable.class.to_s}
|
84
|
+
tagging_hash.merge!(:tagger_id => tagger.id, :tagger_type => tagger.class.to_s) if tagger
|
85
|
+
|
86
|
+
# see if we already have this tagging
|
87
|
+
tagging_obj = Tagging.first(tagging_hash)
|
88
|
+
|
89
|
+
# if we have the tagging already, just skip this one...
|
90
|
+
next if tagging_obj
|
91
|
+
|
92
|
+
# tagging is not in db, let's create one
|
93
|
+
Tagging.create(tagging_hash)
|
94
|
+
end # end of each
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
module SharedInstanceMethods
|
99
|
+
def extract_tagger_class_object(tagger_class_or_object)
|
100
|
+
self.class.extract_tagger_class_object(tagger_class_or_object)
|
101
|
+
end
|
102
|
+
def extract_taggable_class_object(taggable_class_or_object)
|
103
|
+
self.class.extract_taggable_class_object(taggable_class_or_object)
|
104
|
+
end
|
105
|
+
def extract_options(options)
|
106
|
+
self.class.extract_options(options)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|