« 軽量なバリデーションライブラリ | メイン | Ajaxアプリケーションに変身 »

Rails プラグイン acts_as_taggable_redux でタグクラウドを作ろうはてなブックマークに追加 livedoorクリップに追加 Yahoo!ブックマークに追加 del.icio.usに追加 イザ!ブックマーク ニフティクリップに追加

こんにちは。Ruby(とRails)を担当している石原です。

ソーシャル「OSを入れた後にインストールする10のアプリケーション」(仮)の開発の続きです。

今回は登録したアプリケーションに対してタグ付けできるようにして、タグクラウドを作ることを目標にします。

初見の方、よろしくお願いいたします。これまでのエントリーの一覧はこちらです ↓

  1. つくるぶガイドブログ: Ruby on Rails を使ってひとりでサービスを作ってみよう
  2. つくるぶガイドブログ: ひとりサービスの雛型をつくる(リキッドレイアウト、GetText、Acts as Authenticated)
  3. つくるぶガイドブログ: Rails で楽々ソーシャルブックマークの仕組みを作る

それでは、さっそく作っていきます。

acts_as_taggable_redux プラグインのインストール

タグの各機能を簡単に実装するためにプラグインを使います。

タグ関連のプラグインには acts_as_taggable を始めとして、acts_as_taggable_on_steroidsacts_as_taggable_reduxscalable_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' %>

以上、細かい部分はまだ実装が甘いですが、目標のタグクラウドは表示できるようになりました。

画面イメージは以下のとおりです。

tagcloud.jpg

まとめ

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... [詳しくはこちら]