gotagota日記

「面白きことは良きことなり」

古いバージョンの mecab を homebrew でいれたのでメモ とちょっとしたエラー対処

今現在の mecab 最新バージョンは 0.996 なので、単純に $ homebrew install mecab とすると 0.996 が入ってしまいます。
今回は、mecab-ruby とのバージョンの兼ね合いで mecab 0.99 を使う必要が出たのでそのインストール手順をメモします。

環境

  • Homebrew 0.9.5

前提

少し前だと、

$ brew versions mecab

で過去のバージョンを調べ、Formula の過去バージョン にチェックアウトしてから インストールすればよかったらしいです。

しかーし!

homebrew のアップデートに伴いその機能がなくなってしまったみたいです。
現在はそれに変わる機能で、Homebrew-versions というものが使われだしたらしいのですが、使い方がわからず(というかそもそも mecab 自体組み込まれてない???) 断念。

なので、今回は参考URLあげたリンク先のページから拝借して、各バージョンとそれに伴う git ブランチを以下に書きます。

0.996    git checkout ee21df2 /usr/local/Library/Formula/mecab.rb
0.995    git checkout 1c6ad82 /usr/local/Library/Formula/mecab.rb
0.994    git checkout 4844910 /usr/local/Library/Formula/mecab.rb
0.993    git checkout 642c664 /usr/local/Library/Formula/mecab.rb
0.992    git checkout bb95f13 /usr/local/Library/Formula/mecab.rb
0.99     git checkout 01788c0 /usr/local/Library/Formula/mecab.rb
0.98     git checkout 0476235 /usr/local/Library/Formula/mecab.rb

これを参考にいれたいバージョンをいれてください。

実際の手順

今回は 0.99 を使いたいので、

  • $ git checkout 01788c0 /usr/local/Library/Formula/mecab.rb
  • $ brew install mecab mecab-ipadic

こうする。
mecab-ipadic は辞書です。

これで、たいていの方ははいると思います。

$ mecab
すもももももももものうち
すもも   名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも  名詞,一般,*,*,*,*,もも,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも  名詞,一般,*,*,*,*,もも,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち  名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS

すげええええええ。

補足

で、

実は mecab のインストール時に以下のようなメッセージが出てけっこうはまりました。

$ brew install mecab
==> Downloading http://mecab.googlecode.com/files/mecab-0.99.tar.gz
######################################################################## 100.0%
==> ./configure --prefix=/usr/local/Cellar/mecab/0.99
==> make install
      MeCab::Iconv::open(char const*, char const*) in iconv_utils.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[1]: *** [libmecab.la] Error 1
make: *** [install-recursive] Error 1

詳しいことはよくわかりませんw

$ brew doctor してみると、

Warning: Some keg-only formula are linked into the Cellar.
Linking a keg-only formula, such as gettext, into the cellar with
`brew link <formula>` will cause other formulae to detect them during
the `./configure` step. This may cause problems when compiling those
other formulae.

Binaries provided by keg-only formulae may override system binaries
with other strange results.

You may wish to `brew unlink` these brews:

    libiconv
    libxml2
    libxslt

と出て、思い出しました。
最近 nokogiri を入れた際に警告を出すようになって、その時に

$ brew link libiconv libxml2 libxslt

としたのがどうやら原因みたいです。(曖昧)

なので、

$ brew unlink libiconv libxml2 libxslt

としたあとに、$ brew install mecab すればうまくはいります。

参考URL

Solr をとりあえず Rails で動かしてみた

Ruby アプリケーションに全文検索機能をさくっと追加できるらしい sunspot を使ってみたのでそのメモ。

Solr を初めて知って、まずは Rails で動けばいいやって人はこの手順通りにやってみればさくっといけるかもしれない。

詳しい動作をもっと知りたいって方はドキュメントやら下記参考URLを覗いてみてください。

環境

手順

事前準備

  • $ be rais new sun
  • $ cd sun
  • Gemfile
gem 'sunspot_rails'
gem 'sunspot_solr' 
  • $ bundle install --path vendor/bundle

sunspot をいじる

  • sunspot を使うために設定ファイルを作る
$ be rails g sunspot_rails:install`
      create  config/sunspot.yml
  • 起動する
$ be rake sunspot:solr:start
  • ちなみ停止は $ be rake sunspot:solr:stop

  • 今回は以下の様な感じで Scaffold する。

$ be rails g scaffold Article title content:text
$ be rake db:migrate
  • 実際に検索する対象を searchable に渡す。 app/models/article.rb を編集。
    • text全文検索対象の属性を指定
    • :stored オプションで Hit 時にその情報を保持できる (ハイライト機能で使える)
class Article < ActiveRecord::Base
  searchable do
    text :title
    text :content, :stored => true
  end
end
  • $ be rails s でサーバを立ち上げ、http://localhost:3000/articles にアクセス後、適当にデータを挿入

    • 自分は yahoo news から適当に3つほど挿れました
  • ひとまず検索が機能してるか調べる

    • $ be rails console
irb(main):001:0> search = Article.search do
irb(main):002:1*   fulltext 'ドルトムント'
irb(main):003:1> end

下のような値が返ってくればとりあえず動いてる。

D, [2014-10-10T01:52:55.472090 #8950] DEBUG -- :   SOLR Request (12.3ms)  [ path=select parameters={fq: ["type:Article"], q: "ドルトムント", fl: "* score", qf: "title_text content_texts", defType: "edismax", start: 0, rows: 30} ]
=> <Sunspot::Search:{:fq=>["type:Article"], :q=>"ドルトムント", :fl=>"* score", :qf=>"title_text content_texts", :defType=>"edismax", :start=>0, :rows=>30}>
  • コンソールからではなくブラウザからも操作してみる

    • コントローラを作る
    • $ be rails g controller article_search index search
  • 検索窓を作る

    • app/views/article_search/index.html.erb を編集する。
<%= form_tag '/article_search/search', method: "get" do %>
  <%= text_field_tag :q %>
  <%= submit_tag '検索' %>
<% end %>
  • 検索結果を表示するページを作る
    • app/views/article_search/search.html.erb を編集する。
    • each_hit_with_result メソッドを使う
    • ちなみにここでハイライト機能を使っている
<p>
検索ワード: <%= params[:q] %>
</p>
<p>
<%= @search.total %> 件ヒット
</p>
<ul>
<% @search.each_hit_with_result do |hit, result| %>
    <li><div>Article #{<%= hit.primary_key %>}</div>
    <div><%= result.title %></div>
    <% hit.highlights(:content).each do |highlight| %>
        <%= highlight.format  { |word| "*#{word}*" } %>
    <% end %>
    </li>
<% end %>
</ul>
  • アクションをまだ作っていなかったのでコントローラに以下を追記する
    • app/controllers/article_search_controller.rb
  def search
    @search = Article.search do
      fulltext params[:q] do
        highlight :content
      end
    end
  end
  • http://localhost:3000/article_search/index にアクセスして検索ワードをつっこんでみる。

どや!

f:id:gotagotagoat:20141010031905p:plain

ハイライトもされてていい感じですね。 簡単ではありましたが、以上です。

参考URL

homebrew update したら Error: Failed to update tap: homebrew/dupes って出たので解消した

おもむろに homebrew をアップデートしようとしたら、唐突にエラーが出たのでその解消法を。

$ brew update

エラーメッセージは以下。

error: insufficient permission for adding an object to repository database .git/objects
fatal: failed to write object
fatal: unpack-objects failed
Error: Failed to update tap: homebrew/dupes

なにやらパーミッションの問題で、 homebrew/dupes (?) がアップデートできないらしい。

とりあえず、 homebrew/dupes のありかを探して
$ sudo find / -name homebrew-dupes

移動します。
$ cd /usr/local/Library/Taps/homebrew/homebrew-dupes

強制的に リモートリポジトリから引っ張ってきます。
$ git co .
$ sudo git pull --force

で、homebrew をアップデート。
$ brew update

どや!

Already up-to-date.

ふう。
簡単ではありましたが、以上です。

参考URL

エラーの監視に便利な gem , exception_notification を使ってみた

なんかしらのエラーが発生すると、メールを自動的に送付してくれる gem 、exception_notification を実際にかる〜く使ってみたので、そのメモ。

例によって、bundler 経由で gem をがしがしいれていきます。

環境

  • Rails 4.1.6
  • Ruby 2.1.1
  • exception_notification 4.0.1
  • mailcatcher 0.2.4

手順

事前準備

  • 適当に rails アプリを作る。
    be rails new AppName --skip-bundle

  • Gemfile に 以下の二つを追加。

gem 'mailcatcher'
gem 'exception_notification'
  • gem をインストール。
    bundle install --path vendor/bundle

これで必要なものが揃いました。

exception_notification を使ってみる

まずは設定ファイルをいじっていく。
rails デフォルト設定の修正

development 環境では、エラーが発生してもデフォルトでメールを送らないような設定になっているので、以下のように config/environments/development.rbtrue に修正。

config.action_mailer.raise_delivery_errors = ture
SMTP サーバの設定

config/application.rb のを次のように書き換える。
※ 余計なコメントは削除しました

require File.expand_path('../boot', __FILE__)
require "active_model/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "sprockets/railtie"

Bundler.require(*Rails.groups)

module Notifi
  class Application < Rails::Application
    config.action_mailer.delivery_method = :smtp
    config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025 }
  end
end

これらは、 本家の書き方に習っている。
sj26/mailcatcher

exception_notification の設定

エラー時に送信されるメール情報についての設定を config/environments/development.rb に追記する。

  config.middleware.use ExceptionNotification::Rack,
    :email => {
      email_prefix: "[Exception] ",
      sender_address: %{"Exception Notifier" <no-reply@example.com>},
      exception_recipients: ["notification@example.com"]
    }
コントローラを作成する

わざとエラーを発生させるためのページを作ります。

$ be rails g controller home index

そして、HomeController#index に例外を発生させるため以下を追記。

class HomeController < ApplicationController
  def index
    raise 'aaa'
  end
end
サーバを起動し、確認する

$ be rails srails サーバを立てつつ、 Command + t でタブを増やすなりして、$ be mailcatcher で SMPT サーバを起動する。

まず、rails サーバにアクセスして例外を発生させる。
デフォルトでは以下。
http://localhost:3000/home/index

その次に、SMPT サーバにアクセスしてメールがと届いているか確認する。
デフォルトでは以下。 http://127.0.0.1:1080

f:id:gotagotagoat:20141008165157p:plain

こんな感じで、さっきまで空だったのが、なんか来てます。
ここまで来たら成功。

参考URL

CentOS 6.5 に solr をいれていじってみた

solr をローカル開発環境で使ってみたのでそのメモ。
ローカル開発環境の構築はドットインストールさんを参考に書いた ローカル開発環境 ドットインストールまとめ - gotagota日記 を参照にしてみてください。

環境

手順

  • java をインストール
    $ sudo yum install -y java-1.7.0-openjdk

  • solr をダウンロード
    $ sudo wget http://ftp.jaist.ac.jp/pub/apache/lucene/solr/4.8.1/solr-4.8.java-1.tgz

  • 解凍
    $ tar -xzf solr-4.8.1.tgz

  • 起動
    $ cd solr-4.8.1/example $ java -jar start.jar

  • 管理画面を覗いてみる
    ブラウザで、http://<hostname>:8080/solr/ を叩く

  • サンプルデータをインデックス
    $ java -jar exampledocs/post.jar exampledocs/*.xml

  • 検索してみる
    $ curl "http://localhost:8983/solr/collection1/select?q=hello&wt=xml&indent=true"

    • パラメータの意味は、
      • q=検索文字列
      • wt=出力フォーマット
      • indent=出力結果にインデントをつけるか否か

参考URL

データ構成やらの設定など、より発展的な内容はこのサイトが詳しい。

rails new のデフォルトオプションを設定した

どうも潔癖症日本代表です。
嘘です、実際そこまで潔癖症ではありません。

ですが、 Bunlder で Gem を管理する際には Ruby 環境下に余計な gem を入れたくないという思いは強いです。

この度、 Rails を復習しようかと思い、久しぶりに Ruby on Rails チュートリアル をやり直そうとした際に何も考えず bundle updatebundle install してしまい、gem をどこどこ Ruby 環境下にインストールしてしまいました。。。

自分のポンコツ具合からするとこれからいくらでも同じようなことをやらかしそうなので、なにかいい方法がないかと探すと、まさにな方法があったので紹介します。


といってもすごく簡単で、rails new のデフォルトオプションを設定するだけ。
Rails 3.2 からできるようになっていたみたいです。知らなかった。

手順としては

$ echo "--skip-bundle -T" >> ~/.railsrc

とするだけ。
これで、 rails new した時にデフォルトで、「 bundle install しない。テストユニットをインストールしない 。」に設定できました。
-T オプションは任意ですが、 現在は Rspec の方が主流かと思いますので、この際デフォルトにしちまってもいいかと思います。

ちなみに、このデフォルト設定を無視したいときは、

rails new appname --no-rc

としてください。


以前にも書いた記事 rbenv と bundler で rails 環境構築メモ - gotagota日記 と合わせて gem をすっきりと管理したいと思います。

参考URL:

rails new のデフォルトオプションを設定しよう - willnet.in

ドットインストール Rails まとめ

ドットインストールさんで Rails について振り返ったので、勉強メモ。

Rails は前にちょこっとやった時は、ただコードを書き写していただけに等しかったのですが、今回、RubyやらActiveRecord やらを勉強しなおした後にやると理解、納得しながらできたので得るものは大きかったです。

これは、自分で何もみずに同じようなアプリを作るという試みの際の解答のようなものになればと思い残しておきます。

[1] 事前準備

実行環境

自分の実行環境は以下のとおり。

補足

gem に関しては全て Bundler を使って入れます。
詳しくは rbenv と bundler で rails 環境構築メモ に書きましたので宜しければ。
あと、毎回 bundle exec と打つのがめんどかったので be というエイリアスを設定しています。適宜読み替えてください。

手順

まずは事前準備をしていきます。

1. rails new してフォルダに移動

  • $ be rails new taskapp
  • $ cd taskapp

2. Gemfile を編集

  • gem 'therubyracer', platforms: :ruby のコメントを外す

3. gem をインストールする

  • $ bundle install

[2] Project に関するあれやこれ

タスク管理アプリということで、プロジェクト毎にタスクがあると思うので、まずは大本となるプロジェクト部分に関するあれやこれを作っていきます。

以下、実際の手順。

手順

1. Model と Controller を用意する

  • $ be rails g model Project title:string で Model を作成
    • Model の名前は単数形で最初を大文字にする必要がある
    • データ型のデフォルトは文字列なので :string は省略可能
  • $ be rake db:migrate でデータベースを作る。
  • $ be rails g controller Projects でController を作成
    • Controller の名前は複数形で最初を大文字にする必要がある

2. ルーティングをいじる

  • config/routes.rb を編集
Rails.application.routes.draw do
  resources :projects
  root 'projects#index'
end

3. View を作る

  • app/views/projects/index.html.erb を作成する
  • 現時点で中身は空でもおk

4. 共通点プレートをいじる

① 共通ロゴと共通リンクを設定する
  • app/assets/images に 適当な logo.png ファイルを入れておく
  • app/views/layouts/application.html.erb を編集
<!DOCTYPE html>
<html>
<head>
  <title>Taskapp2</title>
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
</head>
<body>
<%= image_tag "logo.png" %>
<%= yield %>
<%= link_to "Home", projects_path %>
</body>
</html>
  • image_tag ヘルパーと link_to ヘルパーを利用して上記のように書く
  • projects_path$ be rake routes でルーティングを確認した際の左端( Prefix カラム) に _path をつけたもの
② 共通スタイルシートを設定する
  • app/assets/stylesheets/application.css をいじる
  body {background-color: #eee;}

5. 新規作成フォームを作成し、データを保存し、表示する

app/controllers/projects_controller.rb をいじる
class ProjectsController < ApplicationController
  def index
    @projects = Project.all
  end
  
  def show 
    @project = Project.find(params[:id])
  end
  
  def new
    @project = Project.new
  end
  
  def create
    @project = Project.new(project_params)
    @project.save
    redirect_to projects_path
  end
  
  private
    def project_params
      params[:project].permit(:title)
    end
end
  • private 以下はセキュリティ対策のためのもの。
app/views/projects/new.html.erb を作成する
<h1>Add New</h1>
<%= form_for @project do |f| %>
<p>
  <%= f.label :title %><br>
  <%= f.text_field :title %>
</p>
<p>
  <%= f.submit %>
</p>
<% end %>
  • form_for ヘルパーを利用する
  • submit したものが Projects#create に送られる
app/views/layouts/application.html.erb をいじる
<!DOCTYPE html>
<html>
<head>
  <title>Taskapp2</title>
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
</head>
<body>
<%= image_tag "logo.png" %>
<%= yield %>
<%= link_to "Add New", new_project_path %> |
<%= link_to "Home", projects_path %>
</body>
</html>
app/views/projects/index.html.erb を編集する
<h1>Projects</h1>

<ul>
  <% @projects.each do |project| %>
  <li>
    <%= link_to project.title, project_path(project.id) %>
  </li>
  <% end %>
</ul>
app/views/projects/show.html.erb を作成する
<h1><%= @project.title %></h1>

6. Validation を設定してエラーメッセージを表示する

  • 今ままでは空の状態でも新規登録できてしまうので、 app/models/project.rb を編集
class Project < ActiveRecord::Base
  validates :title, presence: true
end
  • 今の設定のままだと、Validation に引っかかった時でも、 プロジェクト一覧にリダイレクトしてしまうので、 app/controllers/projects_controller.rb の create アクション を次のように編集
  def create
    @project = Project.new(project_params)
    if @project.save
      redirect_to projects_path
    else
      render 'new'
    end
  end
  • エラーメッセージを表示するとより親切なので app/views/projects/new.html.erb を編集していく
<h1>Add New</h1>

<%= form_for @project do |f| %>

<p>
  <%= f.label :title %><br>
  <%= f.text_field :title %>
  <% if @project.errors.any? %>
  <%= @project.errors.messages[:title][0] %>
  <% end %>
</p>

<p>
  <%= f.submit %>
</p>

<% end %>
  • エラーが発生している場合、 @project.errors にエラーメッセージが入るので、 <% if @project.errors.any? %> で条件分岐をかけて、メッセージを表示させている。

  • また、エラーメッセージ、バリデーションルールは任意のものに設定できる。 app/models/project.rb を編集

class Project < ActiveRecord::Base
  validates :title,
  presence: {message: "入力してください"},
  length: {minimum: 3, message: "短すぎ!"}
end

7. 編集フォームを作ってデータを更新する

  • まずは編集フォームを作っていく。 app/views/projects/edit.html.erb を作成。といっても中身は <h1> タグだけ変えて、あとは app/views/projects/new.html.erb と一緒。
<h1>Edit</h1>

<%= form_for @project do |f| %>

<p>
  <%= f.label :title %><br>
  <%= f.text_field :title %>
  <% if @project.errors.any? %>
  <%= @project.errors.messages[:title][0] %>
  <% end %>
</p>

<p>
  <%= f.submit %>
</p>

<% end %>
  • 次に編集画面へのリンクを追加する。 app/views/projects/index.html.erb を編集
<h1>Projects</h1>

<ul>
  <% @projects.each do |project| %>
  <li>
    <%= link_to project.title, project_path(project.id) %>
    <%= link_to "[Edit]", edit_project_path(project.id) %>
  </li>
  <% end %>
</ul>
  • アクションをまだ作っていないので、 app/controllers/projects_controller.rb に edit アクションを追加
  def edit
    @project = Project.find(params[:id])
  end
  • この時点で編集画面へジャンプできるが、update アクションをまだ作っていないので、更新できない。 app/controllers/projects_controller.rb に update アクションを追加
  def update
    @project = Project.find(params[:id])
    if @project.update(project_params)
      redirect_to projects_path
    else
      render 'edit'
    end
  end

8. データを削除する

  • app/views/projects/index.html.erb を編集し、Delete リンクを追加する。 data: {confirm: "are you sure?"} と記述することで確認アラートを出すことができる。
<h1>Projects</h1>

<ul>
  <% @projects.each do |project| %>
  <li>
    <%= link_to project.title, project_path(project.id) %>
    <%= link_to "[Edit]", edit_project_path(project.id) %>
    <%= link_to "[Delete]", project_path(project.id), method: :delete, data: {confirm: "are you sure?"} %>
  </li>
  <% end %>
</ul>
  • destroy アクションを追加する。 app/controllers/projects_controller.rb に追加。
  def destroy
    @project = Project.find(params[:id])
    @project.destroy
    redirect_to projects_path
  end

9. 重複するコードを共通化し、 Dry を目指す

  • まずフォームで重複する部分があるので、共通化していく。

  • app/views/projects/new.html.erbapp/views/projects/edit.html.erb の共通部分をカットし、 <%= render 'form' %> で置き換える。

<h1>Add New</h1>
<%= render 'form' %>
<h1>Edit</h1>
<%= render 'form' %>
  • app/views/projects/_form.html.erb を作成し、共通部分をコピペ。
<%= form_for @project do |f| %>
<p>
  <%= f.label :title %><br>
  <%= f.text_field :title %>
  <% if @project.errors.any? %>
  <%= @project.errors.messages[:title][0] %>
  <% end %>
</p>
<p>
  <%= f.submit %>
</p>
<% end %>
  • 次に、Controller でも重複する部分があるので、共通化していく。

  • @project = Project.find(params[:id]) が大量に含まれるため、 before_action でまとめる。その際、set_project 関数を private に用意すると後々編集する際に便利。

class ProjectsController < ApplicationController

  before_action :set_project, only: [:show, :edit, :update, :destroy]

  def index
    @projects = Project.all
  end

  def show
  end
  
  
  (中略)
  
  
  private

    def project_params
      params[:project].permit(:title)
    end

    def set_project
      @project = Project.find(params[:id])
    end
end

[3] Task に関するあれやこれ

次に、プロジェクトに属するタスクを設定するあれやこれを作っていきます。

以下、実際の手順。

手順

1. Model と Controller を用意する

  • $ be rails g model Task title done:boolean project:references で Model を作る
    • 繰り返しになるが、モデル名は単数形、 stirng 型は省略可能
    • booelan は真偽値型で、 project:references は Project モデルと紐付けろ、という意味
    • タスクが終わっているかどうかという項目の done はデフォルトで 未完了を表す false であるのが望ましいので、 db/migrate/**********_create_tasks.rb を以下のように修正。
class CreateTasks < ActiveRecord::Migration
  def change
    create_table :tasks do |t|
      t.string :title
      t.boolean :done, default: false
      t.references :project, index: true

      t.timestamps
    end
  end
end
  • $ be rake db:migrate でデータベースを作る

  • $ be rails g controller Tasks で Controller を作る

2. Association を設定する

  • 先ほど Task モデルを作った際には Project モデルとの紐付けオプションをつけて実行したが、 Project モデルを作った際にそのようなオプションはつけなかったので手動で設定する必要がある。
  • app/models/project.rb を編集。
class Project < ActiveRecord::Base
  has_many :tasks
  validates :title,
  presence: {message: "入力してください"},
  length: {minimum: 3, message: "短すぎ!"}
end
  • これで、「一対多の関係」、つまり、「1つの Project に対して、複数の Task」 という関係ができた。

  • 続いて、Task に関するルーティングをいじっていく。とりあえず、 create アクションと、 destroy アクションができればいいので以下のように、 config/routes.rb を編集する

Rails.application.routes.draw do
  resources :projects do
    resources :tasks, only: [:create, :destroy]
  end
  root 'projects#index'
end

3. 新規作成フォームを作り、 task を保存する

  • task の新規作成フォームを作るために、 app/views/projects/show.html.erb を編集する
<h1><%= @project.title %></h1>
<u1>
<% @project.tasks.each do |task| %>
<li><%= task.title %></li>
<% end %>
<li>
  <%= form_for [@project, @project.tasks.build] do |f| %>
  <%= f.text_field :title %>
  <%= f.submit %>
  <% end %>
</li>
</u1>
  • create アクションがまだないので、app/controllers/tasks_controller.rb に追加
class TasksController < ApplicationController

  def create
    @project = Project.find(params[:project_id])
    @task = @project.tasks.create(task_params)
    redirect_to project_path(@project.id)
  end

  private

    def task_params
      params[:task].permit(:title)
    end
end
  • 空の task を弾くためのバリデーションを設定する。 app/models/task.rb を編集
class Task < ActiveRecord::Base
  belongs_to :project
  validates :title, presence: true
end

4. Task を削除する

  • Delete リンクを追加するために、app/views/projects/show.html.erb を編集する
<h1><%= @project.title %></h1>
<u1>
  <% @project.tasks.each do |task| %>
  <li>
    <%= task.title %>
    <%= link_to "[Delete]", project_task_path(task.project_id, task.id), method: :delete, data: {confirm: "are you sure?"} %>
  </li>
  <% end %>
  <li>
  <%= form_for [@project, @project.tasks.build] do |f| %>
  <%= f.text_field :title %>
  <%= f.submit %>
  <% end %>
  </li>
</u1>
  • destroy アクションをまだ作っていないので、 app/controllers/tasks_controller.rb に追加
  def destroy
    @task = Task.find(params[:id])
    @task.destroy
    redirect_to project_path(params[:project_id])
  end

5. チェックボックスを用意して done を切り替える

  • app/views/projects/show.html.erb のリストの中のタスクタイトルの前にチェックボックスを追加。
  • check_box_tag ヘルパーを利用。最初の引数には id を、2番目の引数には value を、3番目の引数にはチェックされているかどうかの true/false をそれぞれ指定できる。
  • その後ろには、 HTML に渡したい属性をいくらでも追加できる。今回は、後で jQuery で使いたいので、扱いやすいように task.idproject_iddata-iddata-project-id という属性で管理できるようにした。
  • 下のコードを追記してから、ブラウザでソースを見ると、 <input data-id="…" data-project_id="…" id="" name="" type="checkbox" value="" />というのが出てきており、data-id 属性と data-project_id 属性で Task の id と Project の id が管理できているのがわかる。
<h1><%= @project.title %></h1>
<u1>
  <% @project.tasks.each do |task| %>
  <li>
    <%= check_box_tag '', '', task.done, {'data-id' => task.id, 'data-project_id' => task.project_id} %>
    <%= task.title %>
    <%= link_to "[Delete]", project_task_path(task.project_id, task.id), method: :delete, data: {confirm: "are you sure?"} %>
  </li>
  <% end %>
  <li>
  <%= form_for [@project, @project.tasks.build] do |f| %>
  <%= f.text_field :title %>
  <%= f.submit %>
  <% end %>
  </li>
</u1>
  • 次に、チェックボックスが切り替わるたびに、 POST を送りたい。そのための toggle アクションを jQuery を用いつつ、用意する。 app/views/projects/show.html.erb の一番下に以下のスクリプトを追加する。
  • このスクリプトの意味は、「チェックボックスがクリックされた際に、 Ajax/projects/xxx/tasks/xxx/toggle に POST しなさい」という意味。
<script>
  $(function(){
    $("input[type=checkbox]").click(function(){
      $.post('/projects/'+$(this).data('project_id')+'/tasks/'+$(this).data('id')+'/toggle');
    });
  })
</script>
  • POST のルーティングが未設定なので config/routes.rb で設定する。以下を追記。
post '/projects/:project_id/tasks/:id/toggle' => 'tasks#toggle'
  • POST 先の toggle アクションがないので作る。 app/controllers/tasks_controller.rb に追加。また、今回は、 toggle アクションリクエストした際に、テンプレートを使わないので、その旨も書く。
  def toggle
    @task = Task.find(params[:id])
    @task.done = !@task.done
    @task.save
    render nothing: true
  end
  • 以上で、タスクの完了/未完了がページをまたいでも保持されるようになる。

6. Tasks の数を表示する

  • まず残タスク数を表示したいので、「 donefalse のものを検索条件として保存しなさい」という意味で、 app/models/tasks.rb に以下を追記。
  • これで :unfinished 関数ができるので、どこからでも残タスク数にアクセスできる。
  scope :unfinished, -> {where(done: false)}
  • 実際に表示するために、app/views/projects/index.html.erb を以下のように書き換える。タイトル部分に先ほど定義した :unfinished 関数 を用いて、残タスク数を表示している。
<h1>Projects</h1>

<ul>
  <% @projects.each do |project| %>
  <li>
    <%= link_to project.title, project_path(project.id) %> (<%= project.tasks.unfinished.count %>/<%= project.tasks.count %>)
    <%= link_to "[Edit]", edit_project_path(project.id) %>
    <%= link_to "[Delete]", project_path(project.id), method: :delete, data: {confirm: "are you sure?"} %>
  </li>
  <% end %>
</ul>