public
Description: Starter Kit for developers using Ruby on Rails to quickly get a blog up & running and then add features.
Homepage: http://www.faithfulgeek.org
Clone URL: git://github.com/faithfulgeek/blog-starter-kit.git
Search Repo:
Implemented tagging; no tag cloud as of yet
Joe Fiorini (author)
Thu May 01 00:14:57 -0700 2008
commit  89f0c5e3301605d73ead8c546bf5db8f7f38893d
tree    2654531cf83c8804a45dfbaa2231b84675ee5b64
parent  c391bf4b9975a547ac27306479b367706b62e346
...
35
36
37
 
 
 
38
 
39
40
41
...
35
36
37
38
39
40
41
42
43
44
45
0
@@ -35,7 +35,11 @@
0
   protected
0
   
0
   def load_collection
0
+ if params[:tag]
0
+ @posts = Post.find_tagged_with params[:tag]
0
+ else
0
       @posts = Post.find(:all, :limit => 10, :order => "created_at desc")
0
+ end
0
   end
0
 
0
 end
...
3
4
5
 
 
 
 
 
 
 
6
...
3
4
5
6
7
8
9
10
11
12
13
0
@@ -3,5 +3,12 @@
0
     value = value.body if value.respond_to?(:body)
0
     RedCloth.new(value, [:filter_html, :filter_styles]).to_html
0
   end
0
+
0
+ def tags_for(post)
0
+ tags = post.tag_list.map do |t|
0
+ link_to t, tag_url(t)
0
+ end
0
+ tags.join " – "
0
+ end
0
 end
...
1
2
3
 
4
5
...
1
2
3
4
5
6
0
@@ -1,6 +1,7 @@
0
 class Post < ActiveRecord::Base
0
   belongs_to :author, :class_name => 'User'
0
   acts_as_commentable :order => 'created_at desc'
0
+ acts_as_taggable
0
 
0
 end
...
6
7
8
9
10
11
 
12
13
14
...
6
7
8
 
 
 
9
10
11
12
0
@@ -6,9 +6,7 @@
0
     = format_date_long(post.created_at)
0
     &ndash;
0
     tagged with:
0
- %a{:href => "/tags/personal"} personal
0
- &ndash;
0
- %a{:href => "/tags/dogs"} dogs
0
+ = tags_for post
0
   .post-content
0
     = textilize post
0
     #tools
...
12
13
14
 
 
 
15
16
17
...
12
13
14
15
16
17
18
19
20
0
@@ -12,6 +12,9 @@
0
     = body_label
0
     ~ f.text_area :body
0
   %p
0
+ = tags_label
0
+ = f.text_field :tag_list
0
+ %p
0
     = is_published_label
0
     = f.check_box :is_published
0
   %p
...
12
13
14
15
 
 
 
 
 
 
16
17
18
...
12
13
14
 
15
16
17
18
19
20
21
22
23
0
@@ -12,7 +12,12 @@
0
     <b><%= body_label %></b><br />
0
     <%= f.text_area :body %>
0
   </p>
0
-
0
+
0
+ <p>
0
+ <%= tags_label %><br />
0
+ <%= f.text_field :tags %>
0
+ </p>
0
+
0
   <p>
0
     <b><%= excerpt_label %></b><br />
0
     <%= f.text_area :excerpt %>
...
6
7
8
9
 
10
11
12
...
6
7
8
 
9
10
11
12
0
@@ -6,7 +6,7 @@
0
   map.logout 'logout', :controller => 'sessions', :action => 'destroy'
0
   map.feed  'feed', :controller => 'posts', :action => 'index', :format => 'atom'
0
   map.comments_for_post 'posts/show/:id', :controller => 'posts', :action => 'show', :anchor => 'comments'
0
-
0
+ map.tag 'posts/tags/:tag', :controller => 'posts', :action => 'index'
0
   map.resources :posts, :has_many => :comments
0
     
0
   map.connect '/:month',
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
0
@@ -1 +1,27 @@
0
+class ActsAsTaggableMigration < ActiveRecord::Migration
0
+ def self.up
0
+ create_table :tags do |t|
0
+ t.column :name, :string
0
+ end
0
+
0
+ create_table :taggings do |t|
0
+ t.column :tag_id, :integer
0
+ t.column :taggable_id, :integer
0
+
0
+ # You should make sure that the column created is
0
+ # long enough to store the required class names.
0
+ t.column :taggable_type, :string
0
+
0
+ t.column :created_at, :datetime
0
+ end
0
+
0
+ add_index :taggings, :tag_id
0
+ add_index :taggings, [:taggable_id, :taggable_type]
0
+ end
0
+
0
+ def self.down
0
+ drop_table :taggings
0
+ drop_table :tags
0
+ end
0
+end
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
0
@@ -1 +1,183 @@
0
+[30 Mar 08]
0
+
0
+* Make TagList.from accept array arguments.
0
+
0
+[29 Mar 08]
0
+
0
+* Improve parsing of quotes inside tags [Arturas Slajus].
0
+
0
+* Add Tag.counts method.
0
+
0
+[28 Mar 08]
0
+
0
+* Make Tag#taggings :dependent => :destroy.
0
+
0
+[27 Mar 08]
0
+
0
+* Fix documentation for tag_counts.
0
+
0
+[18 Mar 08]
0
+
0
+* Add TagList#toggle [Pete Yandell].
0
+
0
+# Add find_related_tags method [Austin Foncaier].
0
+
0
+[30 Jan 08]
0
+
0
+* Fix Tag.destroy_unused on Rails 2.0.
0
+
0
+[23 October 2007]
0
+
0
+* Make find_options_for_tag_counts and find_options_for_tagged_with dup their options.
0
+
0
+* Apply conditions properly in find_options_for_tag_counts.
0
+
0
+* Fix tag_cloud when no tags are present.
0
+
0
+[22 October 2007]
0
+
0
+* Fix find_tagged_with using :match_all and :include.
0
+
0
+* Use inner joins instead of left outer joins.
0
+
0
+[15 October 2007]
0
+
0
+* Make find_tagged_with correctly apply :conditions
0
+
0
+* Add Tag.destroy_unused option.
0
+
0
+[11 October 2007]
0
+
0
+* Make tag_counts work correctly with STI.
0
+
0
+[3 October 2007]
0
+
0
+* Improve documentation.
0
+
0
+* Fix TagsHelper and test.
0
+
0
+[2 October 2007]
0
+
0
+* Remove TagList.parse, use TagList.from instead.
0
+
0
+* Add :parse option to TagList#new, TagList#add, and TagList#remove.
0
+
0
+ tag_list = TagList.new("One, Two", :parse => true) # ["One", "Two"]
0
+
0
+ tag_list # ["One", "Two"]
0
+ tag_list.add("Three, Four", :parse => true) # ["One", "Two", "Three", "Four"]
0
+
0
+* Remove TagList#names.
0
+
0
+[29 September 2007]
0
+
0
+* Add TagsHelper to assist with generating tag clouds and provide a simple example.
0
+
0
+[27 September 2007]
0
+
0
+* Add #tag_counts method to get tag counts for a specific object's tags.
0
+
0
+* BACKWARDS INCOMPATIBILITY: Rename #find_options_for_tagged_with to #find_options_for_find_tagged_with
0
+
0
+[17 September 2007]
0
+
0
+* Fix clearing of cached tag list when all tags removed.
0
+
0
+[12 September 2007]
0
+
0
+* Make the TagList class inherit from Array.
0
+
0
+* Deprecate obsolete TagList#names.
0
+
0
+[6 September 2007]
0
+
0
+* Add TagList#include? and TagList#empty?
0
+
0
+[26 August 2007]
0
+
0
+* Remove deprecated Tag.delimiter. Use TagList.delimiter instead.
0
+
0
+[25 August 2007]
0
+
0
+* Make tag_counts work with has_many :through
0
+
0
+[23 August 2007]
0
+
0
+* Make search comparisons case-insensitive across different databases. [Moisés Machado]
0
+
0
+* Improve compatiblity with STI. [Moisés Machado]
0
+
0
+[25 July 2007]
0
+
0
+* Respect custom table names for the Tag and Tagging classes.
0
+
0
+* Fix the :exclude option for find_tagged_with
0
+
0
+[17 July 2007]
0
+
0
+* Make the migration work on edge rails
0
+
0
+[8 July 2007]
0
+
0
+* find_options_for_tagged_with should not alter its arguments
0
+
0
+[1 July 2007]
0
+
0
+* Fix incorrect tagging when the case of the tag list is changed.
0
+
0
+* Fix deprecated Tag.delimiter accessor.
0
+
0
+[23 June 2007]
0
+
0
+* Add validation to Tag model.
0
+
0
+* find_options_for_tagged_with should always return a hash.
0
+
0
+* find_tagged_with passing in no tags should return an empty array.
0
+
0
+* Improve compatibility with PostgreSQL.
0
+
0
+[21 June 2007]
0
+
0
+* Remove extra .rb from generated migration file name.
0
+
0
+[15 June 2007]
0
+
0
+* Introduce TagList class.
0
+
0
+* Various cleanups and improvements.
0
+
0
+* Use TagList.delimiter now, not Tag.delimiter. Tag.delimiter will be removed at some stage.
0
+
0
+[11 June 2007]
0
+
0
+* Restructure the creation of the options for find_tagged_with [Thijs Cadier]
0
+
0
+* Add an example migration with a generator.
0
+
0
+* Add caching.
0
+
0
+* Fix compatibility with Ruby < 1.8.6
0
+
0
+[23 April 2007]
0
+
0
+* Make tag_list to respect Tag.delimiter
0
+
0
+[31 March 2007]
0
+
0
+* Add Tag.delimiter accessor to change how tags are parsed.
0
+
0
+* Fix :include => :tags when used with find_tagged_with
0
+
0
+[7 March 2007]
0
+
0
+* Fix tag_counts for SQLServer [Brad Young]
0
+
0
+[21 Feb 2007]
0
+
0
+* Use scoping instead of TagCountsExtension [Michael Schuerig]
0
+
0
+[7 Jan 2007]
0
+
0
+* Add :match_all to find_tagged_with [Michael Sheakoski]
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0
@@ -1 +1,21 @@
0
+Copyright (c) 2006 Jonathan Viney
0
+
0
+Permission is hereby granted, free of charge, to any person obtaining
0
+a copy of this software and associated documentation files (the
0
+"Software"), to deal in the Software without restriction, including
0
+without limitation the rights to use, copy, modify, merge, publish,
0
+distribute, sublicense, and/or sell copies of the Software, and to
0
+permit persons to whom the Software is furnished to do so, subject to
0
+the following conditions:
0
+
0
+The above copyright notice and this permission notice shall be
0
+included in all copies or substantial portions of the Software.
0
+
0
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
0
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
0
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
0
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
0
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
0
@@ -1 +1,147 @@
0
+= acts_as_taggable_on_steroids
0
+
0
+If you find this plugin useful, please consider a donation to show your support!
0
+
0
+ http://www.paypal.com/cgi-bin/webscr?cmd=_send-money
0
+
0
+ Email address: jonathan.viney@gmail.com
0
+
0
+== Instructions
0
+
0
+This plugin is based on acts_as_taggable by DHH but includes extras
0
+such as tests, smarter tag assignment, and tag cloud calculations.
0
+
0
+== Installation
0
+
0
+ ruby script/plugin install http://svn.viney.net.nz/things/rails/plugins/acts_as_taggable_on_steroids
0
+
0
+== Usage
0
+
0
+=== Prepare database
0
+
0
+Generate and apply the migration:
0
+
0
+ ruby script/generate acts_as_taggable_migration
0
+ rake db:migrate
0
+
0
+=== Basic tagging
0
+
0
+Let's suppose users have many posts and we want those posts to have tags.
0
+The first step is to add +acts_as_taggable+ to the Post class:
0
+
0
+ class Post < ActiveRecord::Base
0
+ acts_as_taggable
0
+
0
+ belongs_to :user
0
+ end
0
+
0
+We can now use the tagging methods provided by acts_as_taggable, <tt>#tag_list</tt> and <tt>#tag_list=</tt>. Both these
0
+methods work like regular attribute accessors.
0
+
0
+ p = Post.find(:first)
0
+ p.tag_list # []
0
+ p.tag_list = "Funny, Silly"
0
+ p.save
0
+ p.tag_list # ["Funny", "Silly"]
0
+
0
+You can also add or remove arrays of tags.
0
+
0
+ p.tag_list.add("Great", "Awful")
0
+ p.tag_list.remove("Funny")
0
+
0
+=== Finding tagged objects
0
+
0
+To retrieve objects tagged with a certain tag, use find_tagged_with.
0
+
0
+ Post.find_tagged_with('Funny, Silly')
0
+
0
+By default, find_tagged_with will find objects that have any of the given tags. To
0
+find only objects that are tagged with all the given tags, use match_all.
0
+
0
+ Post.find_tagged_with('Funny, Silly', :match_all => true)
0
+
0
+See <tt>ActiveRecord::Acts::Taggable::InstanceMethods</tt> for more methods and options.
0
+
0
+=== Tag cloud calculations
0
+
0
+To construct tag clouds, the frequency of each tag needs to be calculated.
0
+Because we specified +acts_as_taggable+ on the <tt>Post</tt> class, we can
0
+get a calculation of all the tag counts by using <tt>Post.tag_counts</tt>. But what if we wanted a tag count for
0
+an single user's posts? To achieve this we call tag_counts on the association:
0
+
0
+ User.find(:first).posts.tag_counts
0
+
0
+A helper is included to assist with generating tag clouds. Include it in your helper file:
0
+
0
+ module ApplicationHelper
0
+ include TagsHelper
0
+ end
0
+
0
+You can also use the <tt>counts</tt> method on <tt>Tag</tt> to get the counts for all tags in the database.
0
+
0
+ Tag.counts
0
+
0
+Here is an example that generates a tag cloud.
0
+
0
+Controller:
0
+
0
+ class PostController < ApplicationController
0
+ def tag_cloud
0
+ @tags = Post.tag_counts
0
+ end
0
+ end
0
+
0
+View:
0
+ <% tag_cloud @tags, %w(css1 css2 css3 css4) do |tag, css_class| %>
0
+ <%= link_to tag.name, { :action => :tag, :id => tag.name }, :class => css_class %>
0
+ <% end %>
0
+
0
+CSS:
0
+
0
+ .css1 { font-size: 1.0em; }
0
+ .css2 { font-size: 1.2em; }
0
+ .css3 { font-size: 1.4em; }
0
+ .css4 { font-size: 1.6em; }
0
+
0
+=== Caching
0
+
0
+It is useful to cache the list of tags to reduce the number of queries executed. To do this,
0
+add a column named <tt>cached_tag_list</tt> to the model which is being tagged. The column should be long enough to hold
0
+the full tag list and must have a default value of null, not an empty string.
0
+
0
+ class CachePostTagList < ActiveRecord::Migration
0
+ def self.up
0
+ add_column :posts, :cached_tag_list, :string
0
+ end
0
+ end
0
+
0
+ class Post < ActiveRecord::Base
0
+ acts_as_taggable
0
+
0
+ # The caching column defaults to cached_tag_list, but can be changed:
0
+ #
0
+ # set_cached_tag_list_column_name "my_caching_column_name"
0
+ end
0
+
0
+The details of the caching are handled for you. Just continue to use the tag_list accessor as you normally would.
0
+Note that the cached tag list will not be updated if you directly create Tagging objects or manually append to the
0
+<tt>tags</tt> or <tt>taggings</tt> associations. To update the cached tag list you should call <tt>save_cached_tag_list</tt> manually.
0
+
0
+=== Delimiter
0
+
0
+If you want to change the delimiter used to parse and present tags, set TagList.delimiter.
0
+For example, to use spaces instead of commas, add the following to config/environment.rb:
0
+
0
+ TagList.delimiter = " "
0
+
0
+=== Unused tags
0
+
0
+Set Tag.destroy_unused to remove tags when they are no longer being
0
+used to tag any objects. Defaults to false.
0
+
0
+ Tag.destroy_unused = true
0
+
0
+=== Other
0
+
0
+Problems, comments, and suggestions all welcome. jonathan.viney@gmail.com
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
0
@@ -1 +1,23 @@
0
+require 'rake'
0
+require 'rake/testtask'
0
+require 'rake/rdoctask'
0
+
0
+desc 'Default: run unit tests.'
0
+task :default => :test
0
+
0
+desc 'Test the acts_as_taggable_on_steroids plugin.'
0
+Rake::TestTask.new(:test) do |t|
0
+ t.libs << 'lib'
0
+ t.pattern = 'test/**/*_test.rb'
0
+ t.verbose = true
0
+end
0
+
0
+desc 'Generate documentation for the acts_as_taggable_on_steroids plugin.'
0
+Rake::RDocTask.new(:rdoc) do |rdoc|
0
+ rdoc.rdoc_dir = 'rdoc'
0
+ rdoc.title = 'Acts As Taggable On Steroids'
0
+ rdoc.options << '--line-numbers' << '--inline-source'
0
+ rdoc.rdoc_files.include('README')
0
+ rdoc.rdoc_files.include('lib/**/*.rb')
0
+end
...
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
0
@@ -1 +1,12 @@
0
+class ActsAsTaggableMigrationGenerator < Rails::Generator::Base
0
+ def manifest
0
+ record do |m|
0
+ m.migration_template 'migration.rb', 'db/migrate'
0
+ end
0
+ end
0
+
0
+ def file_name
0
+ "acts_as_taggable_migration"
0
+ end
0
+end
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
0
@@ -1 +1,27 @@
0
+class ActsAsTaggableMigration < ActiveRecord::Migration
0
+ def self.up
0
+ create_table :tags do |t|
0
+ t.column :name, :string
0
+ end
0
+
0
+ create_table :taggings do |t|
0
+ t.column :tag_id, :integer
0
+ t.column :taggable_id, :integer
0
+
0
+ # You should make sure that the column created is
0
+ # long enough to store the required class names.
0
+ t.column :taggable_type, :string
0
+
0
+ t.column :created_at, :datetime
0
+ end
0
+
0
+ add_index :taggings, :tag_id
0
+ add_index :taggings, [:taggable_id, :taggable_type]
0
+ end
0
+
0
+ def self.down
0
+ drop_table :taggings
0
+ drop_table :tags
0
+ end
0
+end
...
 
...
1
0
@@ -1 +1,2 @@
0
+require File.dirname(__FILE__) + '/lib/acts_as_taggable'
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
0
@@ -1 +1,206 @@
0
+module ActiveRecord #:nodoc:
0
+ module Acts #:nodoc:
0
+ module Taggable #:nodoc:
0
+ def self.included(base)
0
+ base.extend(ClassMethods)
0
+ end
0
+
0
+ module ClassMethods
0
+ def acts_as_taggable
0
+ has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
0
+ has_many :tags, :through => :taggings
0
+
0
+ before_save :save_cached_tag_list
0
+ after_save :save_tags
0
+
0
+ include ActiveRecord::Acts::Taggable::InstanceMethods
0
+ extend ActiveRecord::Acts::Taggable::SingletonMethods
0
+
0
+ alias_method_chain :reload, :tag_list
0
+ end
0
+
0
+ def cached_tag_list_column_name
0
+ "cached_tag_list"
0
+ end
0
+
0
+ def set_cached_tag_list_column_name(value = nil, &block)
0
+ define_attr_method :cached_tag_list_column_name, value, &block
0
+ end
0
+ end
0
+
0
+ module SingletonMethods
0
+ # Returns an array of related tags.
0
+ # Related tags are all the other tags that are found on the models tagged with the provided tags.
0
+ #
0
+ # Pass either a tag, string, or an array of strings or tags.
0
+ #
0
+ # Options:
0
+ # :order - SQL Order how to order the tags. Defaults to "count DESC, tags.name".
0
+ def find_related_tags(tags, options = {})
0
+ tags = tags.is_a?(Array) ? TagList.new(tags.map(&:to_s)) : TagList.from(tags)
0
+
0
+ related_models = find_tagged_with(tags)
0
+
0
+ return [] if related_models.blank?
0
+
0
+ related_ids = related_models.to_s(:db)
0
+
0
+ Tag.find(:all, options.merge({
0
+ :select => "#{Tag.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
0
+ :joins => "JOIN #{Tagging.table_name} ON #{Tagging.table_name}.taggable_type = '#{base_class.name}'
0
+ AND #{Tagging.table_name}.taggable_id IN (#{related_ids})
0
+ AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id",
0
+ :order => options[:order] || "count DESC, #{Tag.table_name}.name",
0
+ :group => "#{Tag.table_name}.id, #{Tag.table_name}.name HAVING #{Tag.table_name}.name NOT IN (#{tags.map { |n| quote_value(n) }.join(",")})"
0
+ }))
0
+ end
0
+
0
+ # Pass either a tag, string, or an array of strings or tags.
0
+ #
0
+ # Options:
0
+ # :exclude - Find models that are not tagged with the given tags
0
+ # :match_all - Find models that match all of the given tags, not just one
0
+ # :conditions - A piece of SQL conditions to add to the query
0
+ def find_tagged_with(*args)
0
+ options = find_options_for_find_tagged_with(*args)
0
+ options.blank? ? [] : find(:all, options)
0
+ end
0
+
0
+ def find_options_for_find_tagged_with(tags, options = {})
0
+ tags = tags.is_a?(Array) ? TagList.new(tags.map(&:to_s)) : TagList.from(tags)
0
+ options = options.dup
0
+
0
+ return {} if tags.empty?