gnugeek-is_taggable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/README.rdoc +85 -0
- data/generators/is_taggable/USAGE +14 -0
- data/generators/is_taggable/is_taggable_generator.rb +35 -0
- data/generators/is_taggable/templates/autocomplete.js.erb +5 -0
- data/generators/is_taggable/templates/migration.rb +31 -0
- data/generators/is_taggable/templates/taggable_controller.rb +15 -0
- data/generators/is_taggable/templates/taggable_helper.rb +32 -0
- data/init.rb +1 -0
- data/lib/is_taggable.rb +112 -0
- data/lib/tag.rb +20 -0
- data/lib/tagging.rb +3 -0
- data/test/is_taggable_test.rb +59 -0
- data/test/tag_test.rb +36 -0
- data/test/tagging_test.rb +8 -0
- data/test/test_helper.rb +34 -0
- metadata +69 -0
data/CHANGELOG
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
= Is Taggable
|
2
|
+
|
3
|
+
Rails plugin for working easily with tagging stuff in your application.
|
4
|
+
|
5
|
+
Stores _tags_ in separate table and implements a polymorphic interface _taggable_ for attaching tags to ActiveRecord objects.
|
6
|
+
|
7
|
+
Comes with a generator for database migrations and autocompleting tags in forms (with the <tt>--with-autocompleter</tt> option)
|
8
|
+
|
9
|
+
See examples below.
|
10
|
+
|
11
|
+
|
12
|
+
== Usage
|
13
|
+
|
14
|
+
Install the plugin by cloning the repository and running
|
15
|
+
|
16
|
+
script/generate is_taggable .
|
17
|
+
|
18
|
+
To generate migration tables. The dot _is_ necessary.
|
19
|
+
|
20
|
+
|
21
|
+
== Example
|
22
|
+
|
23
|
+
In your _Article_ model:
|
24
|
+
|
25
|
+
class Article < ActiveRecord::Base
|
26
|
+
is_taggable :tags
|
27
|
+
end
|
28
|
+
|
29
|
+
Then:
|
30
|
+
|
31
|
+
>> Tag.all
|
32
|
+
Tag Load (0.3ms) SELECT * FROM `tags`
|
33
|
+
=> [#<Tag id: 1, name: "one", kind: "tag", ...>, #<Tag id: 2, name: "two", kind: "tag", ...>]
|
34
|
+
|
35
|
+
>> a = Article.first
|
36
|
+
Article Load (0.4ms) SELECT * FROM `articles` LIMIT 1
|
37
|
+
=> #<Article id: 1, title: ....>
|
38
|
+
|
39
|
+
>> a.tag_list
|
40
|
+
Article Load (0.4ms) SELECT * FROM `articles` LIMIT 1
|
41
|
+
Tag Load (0.5ms) SELECT `tags`.* FROM `tags` INNER JOIN taggings ON tags.id = taggings.tag_id WHERE ((`taggings`.taggable_type = 'Article') AND (`taggings`.taggable_id = 1)) AND (`tags`.`kind` = 'tag')
|
42
|
+
=> ["one"]
|
43
|
+
|
44
|
+
>> a.tag_list = ['one', 'two']
|
45
|
+
=> ["one", "two"]
|
46
|
+
|
47
|
+
>> a.save
|
48
|
+
...
|
49
|
+
Tagging Create (0.3ms) INSERT INTO `taggings` (`updated_at`, `tag_id`, `taggable_type`, `taggable_id`, `created_at`) VALUES('2008-12-18 16:51:50', 2, 'Article', 1, '2008-12-18 16:51:50')
|
50
|
+
...
|
51
|
+
=> true
|
52
|
+
|
53
|
+
>> Article.find_all_tagged_with 'one'
|
54
|
+
Article Load (1.9ms) SELECT * FROM `articles`
|
55
|
+
Tagging Load (66.6ms) SELECT `taggings`.* FROM `taggings` WHERE (`taggings`.`taggable_id` IN (1,2,3) and `taggings`.`taggable_type` = 'Article')
|
56
|
+
Tag Load (0.4ms) SELECT * FROM `tags` WHERE (`tags`.`id` IN (1,2))
|
57
|
+
=> [#<Article id: 1, title: "Lorem...", ...>]
|
58
|
+
|
59
|
+
>> Article.find_all_tagged_with ['one', 'two']
|
60
|
+
Article Load (2.1ms) SELECT * FROM `articles`
|
61
|
+
Tagging Load (2.9ms) SELECT `taggings`.* FROM `taggings` WHERE (`taggings`.`taggable_id` IN (1,2,3) and `taggings`.`taggable_type` = 'Article')
|
62
|
+
Tag Load (0.3ms) SELECT * FROM `tags` WHERE (`tags`.`id` IN (1,2))
|
63
|
+
=> [#<Article id: 1, title: "Lorem...", ...>]
|
64
|
+
|
65
|
+
>> Article.first.find_tagged_alike
|
66
|
+
SQL (0.5ms) SELECT count(*) AS count_all FROM `tags` INNER JOIN taggings ON tags.id = taggings.tag_id WHERE ((`taggings`.taggable_type = 'Article') AND (`taggings`.taggable_id = 1))
|
67
|
+
Article Load (1.5ms) SELECT * FROM `articles` WHERE (id != '1')
|
68
|
+
Tagging Load (2.9ms) SELECT `taggings`.* FROM `taggings` WHERE (`taggings`.`taggable_id` IN (1,2,3) and `taggings`.`taggable_type` = 'Article')
|
69
|
+
Tag Load (0.3ms) SELECT * FROM `tags` WHERE (`tags`.`id` IN (1,2,3))
|
70
|
+
Tag Load (0.4ms) SELECT `tags`.* FROM `tags` INNER JOIN taggings ON tags.id = taggings.tag_id WHERE ((`taggings`.taggable_type = 'Article') AND (`taggings`.taggable_id = 1)) AND (`tags`.`kind` = 'tag')
|
71
|
+
=> [#<Article id: 201, title: "Another...", ...>]
|
72
|
+
|
73
|
+
|
74
|
+
When you run generator with the <tt>--with-autocompleter</tt> option, also a controller and view are generated.
|
75
|
+
You can use it like this then:
|
76
|
+
|
77
|
+
<% form_for(@my_object) do |f| %>
|
78
|
+
<%= f.text_area :tag_list %>
|
79
|
+
<%= taggable_autocompleter_for('my_object_tag_list') %>
|
80
|
+
<% end %>
|
81
|
+
|
82
|
+
Tags matching input will be then added to your input/textarea. Assumes you separate tags with commas (,).
|
83
|
+
|
84
|
+
|
85
|
+
Copyright (c) 2008 James Golick & Karel Minarik, released under the MIT license
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Description:
|
2
|
+
Generate migrations for +is_taggable+ plugin
|
3
|
+
|
4
|
+
Example:
|
5
|
+
./script/generate is_taggable .
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
db/migrate/XXXXXXXXXX_create_taggables.rb
|
9
|
+
|
10
|
+
If the --with-autocomplete options is passed, it will generate this as well:
|
11
|
+
create app/controllers/taggable_controller.rb
|
12
|
+
create app/views/taggable
|
13
|
+
create app/views/taggable/autocomplete.js.erb
|
14
|
+
create app/helpers/taggable_helper.rb
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class IsTaggableGenerator < Rails::Generator::NamedBase
|
2
|
+
|
3
|
+
def manifest
|
4
|
+
record do |m|
|
5
|
+
|
6
|
+
# * Generate migration
|
7
|
+
m.migration_template 'migration.rb', "db/migrate", {
|
8
|
+
:assigns => { :migration_name => 'CreateTaggables' },
|
9
|
+
:migration_file_name => 'create_taggables'
|
10
|
+
}
|
11
|
+
|
12
|
+
# * Generate controller and view for Scriptaculous autocompleter
|
13
|
+
if options[:with_autocompleter]
|
14
|
+
m.file 'taggable_controller.rb', 'app/controllers/taggable_controller.rb', :collision => :skip
|
15
|
+
m.directory 'app/views/taggable'
|
16
|
+
m.file 'autocomplete.js.erb', 'app/views/taggable/autocomplete.js.erb', :collision => :skip
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def banner
|
25
|
+
"\nUsage: script/generate is_taggable . [options]\n\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_options!(opt)
|
29
|
+
opt.separator ''
|
30
|
+
opt.separator 'Options:'
|
31
|
+
opt.on("--with-autocompleter",
|
32
|
+
"Generate controller and view for autocompleting tags") { |v| options[:with_autocompleter] = v }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# = Migration for the is_taggable plugin
|
2
|
+
#
|
3
|
+
class CreateTaggables < ActiveRecord::Migration
|
4
|
+
def self.up
|
5
|
+
|
6
|
+
create_table :tags do |t|
|
7
|
+
t.string :name, :default => ''
|
8
|
+
t.string :kind, :default => ''
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :taggings do |t|
|
13
|
+
t.integer :tag_id
|
14
|
+
# Interface +taggable+
|
15
|
+
t.string :taggable_type
|
16
|
+
t.integer :taggable_id
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
|
20
|
+
# Add indices
|
21
|
+
add_index :tags, :name
|
22
|
+
add_index :tags, [:name, :kind], :name => "name_and_kind"
|
23
|
+
add_index :taggings, [:taggable_type, :taggable_id], :name => "taggable_interface_index"
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.down
|
28
|
+
drop_table :tags
|
29
|
+
drop_table :taggings
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# = Return autocomplete results for the Tag class from is_taggable plugin
|
2
|
+
# Add before_filters, copy it somewhere else, etc, as you like.
|
3
|
+
# Just don't forget the <tt>app/views/taggable/autocomplete.js.erb</tt> file as well
|
4
|
+
#
|
5
|
+
class TaggableController < ApplicationController
|
6
|
+
|
7
|
+
# Return autocomplete results
|
8
|
+
def autocomplete
|
9
|
+
@autocomplete_tags = Tag.all( :conditions => ['name LIKE ?', "#{params[:autocomplete_tags]}%"], :order => 'name', :limit => 50)
|
10
|
+
respond_to do |format|
|
11
|
+
format.js { render :template => "autocomplete", :layout => false }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# = Encapsulating Script.aculo.us <tt>Ajax.Autocompleter</tt> method
|
2
|
+
# Over-ride the CSS in your stylesheets with a more specific selector, like <tt>.my-page div.autocomplete { color: red }</tt>
|
3
|
+
|
4
|
+
module TaggableHelper
|
5
|
+
|
6
|
+
# Returns <tt><div></tt>, CSS and JavaScript code needed for autocompleting tags
|
7
|
+
# See http://github.com/madrobby/scriptaculous/wikis/ajax-autocompleter
|
8
|
+
def taggable_autocompleter_for(field_name, options={})
|
9
|
+
authenticity_token_param = protect_against_forgery? ? "authenticity_token=#{form_authenticity_token}" : '' # Beware of tests :)
|
10
|
+
autocompleter=<<HTML
|
11
|
+
<style type="text/css" media="screen">
|
12
|
+
div.autocomplete {
|
13
|
+
position:absolute; width:250px;
|
14
|
+
background-color: white;
|
15
|
+
border:1px solid #888; margin:0; padding:0; }
|
16
|
+
div.autocomplete ul { list-style-type:none; margin:0; padding:0; }
|
17
|
+
div.autocomplete ul li.selected { background-color: #ecf4fe;}
|
18
|
+
div.autocomplete ul li { display: block; margin: 0; padding: 0.2em; height: 1em; cursor: pointer; }
|
19
|
+
</style>
|
20
|
+
<div id="#{field_name}_autocomplete_choices" class="autocomplete"></div>
|
21
|
+
<script type="text/javascript">
|
22
|
+
new Ajax.Autocompleter("#{field_name}",
|
23
|
+
"#{field_name}_autocomplete_choices",
|
24
|
+
"/taggable/autocomplete?#{authenticity_token_param}",
|
25
|
+
{ paramName: 'autocomplete_tag', tokens: ',' }
|
26
|
+
);
|
27
|
+
</script>
|
28
|
+
HTML
|
29
|
+
return autocompleter
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'is_taggable'
|
data/lib/is_taggable.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
path = File.expand_path(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH << path unless $LOAD_PATH.include?(path)
|
3
|
+
require 'tag'
|
4
|
+
require 'tagging'
|
5
|
+
|
6
|
+
module IsTaggable
|
7
|
+
class TagList < Array
|
8
|
+
def to_s
|
9
|
+
join(', ')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ActiveRecordExtension
|
14
|
+
def is_taggable(*kinds)
|
15
|
+
class_inheritable_accessor :tag_kinds
|
16
|
+
self.tag_kinds = kinds.map(&:to_s).map(&:singularize)
|
17
|
+
self.tag_kinds << :tag if kinds.empty?
|
18
|
+
|
19
|
+
include IsTaggable::TaggableMethods
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module TaggableMethods
|
24
|
+
def self.included(klass)
|
25
|
+
klass.class_eval do
|
26
|
+
include IsTaggable::TaggableMethods::InstanceMethods
|
27
|
+
|
28
|
+
has_many :taggings, :as => :taggable
|
29
|
+
has_many :tags, :through => :taggings
|
30
|
+
after_save :save_tags
|
31
|
+
|
32
|
+
tag_kinds.each do |k|
|
33
|
+
define_method("#{k}_list") { get_tag_list(k) }
|
34
|
+
define_method("#{k}_list=") { |new_list| set_tag_list(k, new_list) }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Find all records tagged with a +'tag'+ or ['tag one', 'tag two']
|
38
|
+
# Pass either String for single tag or Array for multiple tags
|
39
|
+
# TODO : Add option all x any
|
40
|
+
def self.find_all_tagged_with(tag_or_tags, conditions=[])
|
41
|
+
return [] if tag_or_tags.nil? || tag_or_tags.empty?
|
42
|
+
case tag_or_tags
|
43
|
+
when Array, IsTaggable::TagList
|
44
|
+
all(:include => ['tags', 'taggings'], :conditions => conditions ).select { |record| tag_or_tags.all? { |tag| record.tags.map(&:name).include?(tag) } } || []
|
45
|
+
else
|
46
|
+
all(:include => ['tags', 'taggings'], :conditions => conditions).select { |record| record.tags.map(&:name).include?(tag_or_tags) } || []
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.find_all_ids_tagged_with(tag_or_tags, conditions=[])
|
51
|
+
find_all_tagged_with(tag_or_tags, conditions).map(&:id)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Find all records tagged with the same tags as current object,
|
55
|
+
# *excluding* the current object (for things like "Related articles")
|
56
|
+
# TODO : Add option all x any
|
57
|
+
# TODO : Remove hardcoded +tag_list+ kind of tags, could be any kind
|
58
|
+
def find_tagged_alike
|
59
|
+
return [] if self.tags.empty?
|
60
|
+
self.class.all(:include => ['tags', 'taggings'],
|
61
|
+
:conditions => ["id != '?'", self.id]).
|
62
|
+
select { |record| self.tag_list.all? { |tag| record.tags.map(&:name).include?(tag) } } || []
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module InstanceMethods
|
69
|
+
def set_tag_list(kind, list)
|
70
|
+
list.gsub!(/ *, */,',') unless list.is_a?(Array)
|
71
|
+
tag_list = TagList.new(list.is_a?(Array) ? list : list.split(','))
|
72
|
+
instance_variable_set(tag_list_name_for_kind(kind), tag_list)
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_tag_list(kind)
|
76
|
+
set_tag_list(kind, tags.of_kind(kind).map(&:name)) if tag_list_instance_variable(kind).nil?
|
77
|
+
tag_list_instance_variable(kind)
|
78
|
+
end
|
79
|
+
|
80
|
+
protected
|
81
|
+
def tag_list_name_for_kind(kind)
|
82
|
+
"@#{kind}_list"
|
83
|
+
end
|
84
|
+
|
85
|
+
def tag_list_instance_variable(kind)
|
86
|
+
instance_variable_get(tag_list_name_for_kind(kind))
|
87
|
+
end
|
88
|
+
|
89
|
+
def save_tags
|
90
|
+
tag_kinds.each do |tag_kind|
|
91
|
+
delete_unused_tags(tag_kind)
|
92
|
+
add_new_tags(tag_kind)
|
93
|
+
end
|
94
|
+
|
95
|
+
taggings.each(&:save)
|
96
|
+
end
|
97
|
+
|
98
|
+
def delete_unused_tags(tag_kind)
|
99
|
+
tags.of_kind(tag_kind).each { |t| tags.delete(t) unless get_tag_list(tag_kind).include?(t.name) }
|
100
|
+
end
|
101
|
+
|
102
|
+
def add_new_tags(tag_kind)
|
103
|
+
tag_names = tags.of_kind(tag_kind).map(&:name)
|
104
|
+
get_tag_list(tag_kind).each do |tag_name|
|
105
|
+
tags << Tag.find_or_initialize_with_name_like_and_kind(tag_name, tag_kind) unless tag_names.include?(tag_name)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
ActiveRecord::Base.send(:extend, IsTaggable::ActiveRecordExtension)
|
data/lib/tag.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class Tag < ActiveRecord::Base
|
2
|
+
class << self
|
3
|
+
def find_or_initialize_with_name_like_and_kind(name, kind)
|
4
|
+
with_name_like_and_kind(name, kind).first || new(:name => name, :kind => kind)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
has_many :taggings
|
9
|
+
|
10
|
+
validates_presence_of :name
|
11
|
+
validates_uniqueness_of :name, :scope => :kind
|
12
|
+
|
13
|
+
named_scope :with_name_like_and_kind, lambda { |name, kind| { :conditions => ["name like ? AND kind = ?", name, kind] } }
|
14
|
+
named_scope :of_kind, lambda { |kind| { :conditions => {:kind => kind} } }
|
15
|
+
named_scope :unique_by_name_for_kind, lambda { |kind| { :conditions => {:kind => kind}, :group => 'id,name,kind,created_at,updated_at' } }
|
16
|
+
|
17
|
+
def self.unique_tag_list_by_kind(kind)
|
18
|
+
unique_by_name_for_kind('available_service').map(&:name)
|
19
|
+
end
|
20
|
+
end
|
data/lib/tagging.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
Expectations do
|
4
|
+
expect Tag do
|
5
|
+
Post.new.tags.build
|
6
|
+
end
|
7
|
+
|
8
|
+
expect Tagging do
|
9
|
+
Post.new.taggings.build
|
10
|
+
end
|
11
|
+
|
12
|
+
expect ["something cool", "something else cool"] do
|
13
|
+
p = Post.new :tag_list => "something cool, something else cool"
|
14
|
+
p.tag_list
|
15
|
+
end
|
16
|
+
|
17
|
+
expect ["something cool", "something new"] do
|
18
|
+
p = Post.new :tag_list => "something cool, something else cool"
|
19
|
+
p.save!
|
20
|
+
p.tag_list = "something cool, something new"
|
21
|
+
p.save!
|
22
|
+
p.tags.reload
|
23
|
+
p.instance_variable_set("@tag_list", nil)
|
24
|
+
p.tag_list
|
25
|
+
end
|
26
|
+
|
27
|
+
expect ["english", "french"] do
|
28
|
+
p = Post.new :language_list => "english, french"
|
29
|
+
p.save!
|
30
|
+
p.tags.reload
|
31
|
+
p.instance_variable_set("@language_list", nil)
|
32
|
+
p.language_list
|
33
|
+
end
|
34
|
+
|
35
|
+
expect ["english", "french"] do
|
36
|
+
p = Post.new :language_list => "english, french"
|
37
|
+
p.language_list
|
38
|
+
end
|
39
|
+
|
40
|
+
expect "english, french" do
|
41
|
+
p = Post.new :language_list => "english, french"
|
42
|
+
p.language_list.to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
# added - should clean up strings with arbitrary spaces around commas
|
46
|
+
expect ["spaces","should","not","matter"] do
|
47
|
+
p = Post.new
|
48
|
+
p.tag_list = "spaces,should, not,matter"
|
49
|
+
p.save!
|
50
|
+
p.tags.reload
|
51
|
+
p.tag_list
|
52
|
+
end
|
53
|
+
|
54
|
+
expect 2 do
|
55
|
+
p = Post.new :language_list => "english, french"
|
56
|
+
p.save!
|
57
|
+
p.tags.length
|
58
|
+
end
|
59
|
+
end
|
data/test/tag_test.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
Expectations do
|
4
|
+
expect Tagging do
|
5
|
+
Tag.new.taggings.proxy_reflection.klass
|
6
|
+
end
|
7
|
+
|
8
|
+
expect Tag.new(:name => "duplicate").not.to.be.valid? do
|
9
|
+
Tag.create!(:name => "duplicate")
|
10
|
+
end
|
11
|
+
|
12
|
+
expect Tag.new(:name => "not dup").to.be.valid? do
|
13
|
+
Tag.create!(:name => "not dup", :kind => "something")
|
14
|
+
end
|
15
|
+
|
16
|
+
expect Tag.new.not.to.be.valid?
|
17
|
+
expect String do
|
18
|
+
t = Tag.new
|
19
|
+
t.valid?
|
20
|
+
t.errors[:name]
|
21
|
+
end
|
22
|
+
|
23
|
+
expect Tag.create!(:name => "iamawesome", :kind => "awesomestuff") do
|
24
|
+
Tag.find_or_initialize_with_name_like_and_kind("iamawesome", "awesomestuff")
|
25
|
+
end
|
26
|
+
|
27
|
+
expect true do
|
28
|
+
Tag.create!(:name => "iamawesome", :kind => "stuff")
|
29
|
+
Tag.find_or_initialize_with_name_like_and_kind("iamawesome", "otherstuff").new_record?
|
30
|
+
end
|
31
|
+
|
32
|
+
expect Tag.create!(:kind => "language", :name => "French") do
|
33
|
+
Tag.of_kind("language").first
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'activerecord'
|
3
|
+
require File.dirname(__FILE__)+'/../lib/is_taggable'
|
4
|
+
require 'expectations'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
ActiveRecord::Base.configurations = {'sqlite3' => {:adapter => 'sqlite3', :database => ':memory:'}}
|
8
|
+
ActiveRecord::Base.establish_connection('sqlite3')
|
9
|
+
|
10
|
+
ActiveRecord::Base.logger = Logger.new(STDERR)
|
11
|
+
ActiveRecord::Base.logger.level = Logger::WARN
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define(:version => 0) do
|
14
|
+
create_table :posts do |t|
|
15
|
+
t.string :title, :default => ''
|
16
|
+
end
|
17
|
+
|
18
|
+
create_table :tags do |t|
|
19
|
+
t.string :name, :default => ''
|
20
|
+
t.string :kind, :default => ''
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :taggings do |t|
|
24
|
+
t.integer :tag_id
|
25
|
+
|
26
|
+
t.string :taggable_type, :default => ''
|
27
|
+
t.integer :taggable_id
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Post < ActiveRecord::Base
|
32
|
+
is_taggable :tags, :languages, :unique
|
33
|
+
end
|
34
|
+
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gnugeek-is_taggable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- giraffesoft,Karmi,Brian Knox
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-04 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Extends activerecord to make models taggable
|
17
|
+
email: gnutse@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
- CHANGELOG
|
25
|
+
files:
|
26
|
+
- init.rb
|
27
|
+
- README.rdoc
|
28
|
+
- CHANGELOG
|
29
|
+
- lib/is_taggable.rb
|
30
|
+
- lib/tagging.rb
|
31
|
+
- lib/tag.rb
|
32
|
+
- generators/is_taggable/is_taggable_generator.rb
|
33
|
+
- generators/is_taggable/USAGE
|
34
|
+
- generators/is_taggable/templates/autocomplete.js.erb
|
35
|
+
- generators/is_taggable/templates/migration.rb
|
36
|
+
- generators/is_taggable/templates/taggable_controller.rb
|
37
|
+
- generators/is_taggable/templates/taggable_helper.rb
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: http://github.com/gnugeek/is_taggable/
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options:
|
42
|
+
- --main
|
43
|
+
- README.rdoc
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
requirements: []
|
59
|
+
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.2.0
|
62
|
+
signing_key:
|
63
|
+
specification_version: 2
|
64
|
+
summary: tagging that isn't ugly
|
65
|
+
test_files:
|
66
|
+
- test/is_taggable_test.rb
|
67
|
+
- test/tagging_test.rb
|
68
|
+
- test/tag_test.rb
|
69
|
+
- test/test_helper.rb
|