Rails プラグイン acts_as_taggable_redux でタグクラウドを作ろう

こんにちは。Ruby(とRails)を担当している石原です。
ソーシャル「OSを入れた後にインストールする10のアプリケーション」(仮)の開発の続きです。
今回は登録したアプリケーションに対してタグ付けできるようにして、タグクラウドを作ることを目標にします。
初見の方、よろしくお願いいたします。これまでのエントリーの一覧はこちらです ↓
- つくるぶガイドブログ: Ruby on Rails を使ってひとりでサービスを作ってみよう
- つくるぶガイドブログ: ひとりサービスの雛型をつくる(リキッドレイアウト、GetText、Acts as Authenticated)
- つくるぶガイドブログ: Rails で楽々ソーシャルブックマークの仕組みを作る
それでは、さっそく作っていきます。
acts_as_taggable_redux プラグインのインストール
タグの各機能を簡単に実装するためにプラグインを使います。
タグ関連のプラグインには acts_as_taggable を始めとして、acts_as_taggable_on_steroids や acts_as_taggable_redux、scalable_acts_as_taggable などたくさんあるのですが、今回はタグクラウドを簡単に作ることができるという理由で、acts_as_taggable_redux を使うことにします。
選ぶにあたっては、以下の記事を参考にさせていただきました。
» railsで簡単にタグクラウドを作る - acts_as_taggable_redux
以下をコマンドラインより実行してプラグインをインストールします。
script/plugin install http://svn.devjavu.com/geemus/rails/plugins/acts_as_taggable_redux
acts_as_taggable_redux を使うための下準備
必要なテーブルを DB に追加してくれる Migration スクリプトを rake で生成します。
rake acts_as_taggable:db:create
生成された Migration スクリプトを見て、データ構造を理解しておきましょう。
== db/migrate/006_add_acts_as_taggable_tables.rb ==
class AddActsAsTaggableTables < ActiveRecord::Migration
def self.up
create_table :tags do |t|
t.column :name, :string
t.column :taggings_count, :integer, :default => 0, :null => false
end
add_index :tags, :name
add_index :tags, :taggings_count
create_table :taggings do |t|
t.column :tag_id, :integer
t.column :taggable_id, :integer
t.column :taggable_type, :string
t.column :user_id, :integer
end
# Find objects for a tag
add_index :taggings, [:tag_id, :taggable_type]
add_index :taggings, [:user_id, :tag_id, :taggable_type]
# Find tags for an object
add_index :taggings, [:taggable_id, :taggable_type]
add_index :taggings, [:user_id, :taggable_id, :taggable_type]
end
def self.down
drop_table :tags
drop_table :taggings
end
end
追加されるテーブルは2つ、tags と taggings です。
tags にはタグそのものの名前(name)とどれだけタグ付けされているかのカウント数(taggings_count)が保存されるようです。taggings_count がテーブル名 + count であることからカウンターキャッシュ(必要ならRoR Wiki 翻訳 Wiki - MagicFieldNamesのカウンターキャッシュの項参照)が使える、あるいは自動的に使われるのではないかと予想できます。
taggings テーブルのカラムは tag_id、taggable_id、taggable_type、user_id の4つです。
tag_id があることから、tags と taggings は1対多の関係でひもづけられ、taggable_id と taggable_type の -able_id、-able_type という特徴的な名前から taggings はタグ付けする対象のモデルとポリモーフィックアソシエーションでつながるのだろうとあたりをつけておきます。
user_id によって users テーブルと関連付けられます。誰がつけたタグかを特定するためです。ここでは、ユーザーの情報は users テーブルに入っているという暗黙の了解を元にしています。レール(Rail)に乗って滞りなく素早く開発していくには、こうした暗黙の了解を崩すような特別なこと(テーブル名を独特のものにするなど)はなるべくしないようにすべきだということがわかります。
rake db:migrate
を実行してテーブルを追加します。
User モデルには、acts_as_tagger という一行を追加します。tagger つまり、「タグ付けした人」のふるまいを追加することで Tagging モデルとひもづけられます。
== app/models/user.rb == class User < ActiveRecord::Base # For acts_as_taggable_redux acts_as_tagger
また、タグ付けする対象モデルに対して、acts_as_taggable の一行を追加します。対象は登録するアプリケーションなので、Software モデルです。
== app/models/software.rb == class Software < ActiveRecord::Base acts_as_taggable
タグクラウド用のスタイルシートも用意されています。以下の rake タスクで生成し、
rake acts_as_taggable:stylesheet:create
次の一行を app/views/layouts/application.rhtml に追加して、スタイルシートを読み込めるようにします。
<%= stylesheet_link_tag 'acts_as_taggable_stylesheet' %>
タグ機能の実装
準備が整ったので、いよいよアプリケーションにタグ機能を実装していきます。
acts_as_taggable_redux は RESTful なアプリケーションを前提としているのですが、ソーシャル「OSを入れた後にインストールする10のアプリケーション」(仮) 10best は RESTful ではありません。このため、多少カスタマイズする必要があります。
登録画面にタグを入力するテキストフィールドを追加します。誰がつけたタグかがわかるように、ログインしているユーザーの情報(current_user)とともにタグを登録できるようにしておきます。
== app/views/softwares/_form.rhtml == <%= error_messages_for 'software' %> <p><label for="software_title">Title</label><br/> <%= text_field 'software', 'title' %></p> <p>
自分が登録したアプリケーションに付けられたタグを一覧できるようにします。追加したのは赤字の部分です。
== app/views/bookmarks/show.rhtml ==
<p>
<b>Title:</b> <%= link_to h(@bookmark.software.title), :controller => 'software', :action => 'show', :id => @bookmark.software.id %>
</p>
<% for column in Bookmark.content_columns %>
<p>
<b><%= column.human_name %>:</b> <%=h @bookmark.send(column.name) %>
</p>
<% end %>
<p>
<b>Tags:</b>
<% @bookmark.software.tags.each do |tag| -%>
<%= link_to_tag(tag) %>
<% end -%>
</p>
<%= link_to 'Edit', :action => 'edit', :id => @bookmark %> |
<%= link_to 'Back', :action => 'list' %>
最後にタグクラウドです。左メニューに表示するようにしましょう。わずか一行、追加するのは最後の tag_cloud だけです。
== app/views/shared/_left.rhtml ==
<p><%= sprintf(_('Welcome, %s'), h(current_user.login)) if logged_in? %></p>
<ul>
<%- if logged_in? -%>
<li><%= link_to _('Logout'), :controller => 'account', :action => 'logout' %></li>
<%- else -%>
<li><%= link_to _('Log in'), :controller => 'account', :action => 'login' %></li>
<li><%= link_to _('Sign up'), :controller => 'account', :action => 'signup' %></li>
<%- end -%>
</ul>
<%= tag_cloud %>
ただしこのまま実行すると、
undefined method `tag_url' for ...
というエラーが表示されてしまいます。これは前述したように acts_as_taggable_redux が RESTful を前提としているため、具体的にはタグ関連のヘルパーが REST 対応で書かれているためです。
そこで、vendor/plugins/acts_as_taggable_redux/lib/acts_as_taggable_helper.rb に書かれている link_to_tag、tag_cloud の2つのヘルパーを app/helpers/application_helper.rb にコピーして、以下の部分を修正することによりヘルパーを置き換えてしまいます。
== app/helpers/application_helper.rb ==
module ApplicationHelper
def link_to_tag(tag)
#link_to(tag.name, tag_url(tag), :rel => 'tag')
link_to(tag.name, {:controller => 'softwares', :action => 'list', :tag => tag.name}, :rel => 'tag')
end
def tag_cloud(options = {})
<中略>
tags.each do |tag|
html << %( <li>)
#html << link_to(tag.name, tag_url(tag), :class => classes[(tag.taggings_count - min) / divisor])
html << link_to(tag.name, {:controller => 'softwares', :action => 'list', :tag => tag.name}, :class => classes[(tag.taggings_count - min) / divisor])
html << %(</li> \n)
end
html << %( </ul>\n)
html << %(</div>\n)
end
end
タグのリンク先には softwares/list を指定し、tag というパラメーターでタグの内容そのままを渡すようにしました。パラメーターで渡されたタグが付いているアプリケーションだけをリストする処理を list アクションに追加します。
== app/controllers/softwares_controller.rb ==
def list
if params[:tag]
@softwares = Software.find_tagged_with(params[:tag])
else
@software_pages, @softwares = paginate :softwares, :per_page => 10
end
end
タグがついているアプリケーションだけをリストするときはページネーションさせないようにします。ビューを以下のように修正しておきます。
== app/views/softwares/list.rhtml ==
<前半部分 略>
<% if @software_pages %>
<%= link_to 'Previous page', { :page => @software_pages.current.previous } if @software_pages.current.previous %>
<%= link_to 'Next page', { :page => @software_pages.current.next } if @software_pages.current.next %>
<% end %>
<br />
<%= link_to 'New software', :action => 'new' %>
以上、細かい部分はまだ実装が甘いですが、目標のタグクラウドは表示できるようになりました。
画面イメージは以下のとおりです。
まとめ
acts_as_taggable_redux を使ってタグクラウドを実装しました。次回まで編集画面からのタグの編集など細かい部分は実装しておく予定です。余力があれば、自分でやってみるといいと思います。
ここまでのソースコードをアップロードしておきます。
» 10best - Google Code
svn checkout http://10best.googlecode.com/svn/trunk/ 10best
でチェックアウトするか、ブラウザでもリポジトリを閲覧することができます。
トラックバック
必ず利用規約をお読み頂き、同意の上、送信してください。
また、トラックバック元・リンク先の内容にはリクルートは一切責任を負いません。
この一覧は、次のエントリーを参照しています: Rails プラグイン acts_as_taggable_redux でタグクラウドを作ろう:
» [Rails]acts_as_taggable_redux 送信元 cys b
acts_as_taggable系列のタグ付けプラグインをいろいろチェックしていて、まあ、あるわあるわ。結局どれがいいのかよく分からないが、acts_a... [詳しくはこちら]




最近のコメント