gnugeek-is_taggable 0.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/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
|