2014年7月5日土曜日

6-4 イベントの登録機能を作る

 わからないながら、ずっとメモし続けていたら話が少し見えるようになってきた気がする。メモを簡略化して、メモにかける時間を少し短くしようと思います。

 ここでやりたいこと。

  • ヘッダーにイベント作成リンクを設置。
  • ログイン状態で押すとイベント登録フォームへ移る。
  • 未ログインならトップページへリダイレクト。(これなんか親切じゃないね。)
  • イベント登録で誤った入力があるとエラー表示。
  • 正しければ登録処理。

タイムゾーンを設定する

デフォルトではUTC(協定世界時)に設定されているため、config/application.rbでコメントアウトされているタイムゾーンの設定をTokyoに変更します。こうしないと、イベントの登録時間等が登録者の意図しない時間になってしまう。
    config.time_zone = 'Tokyo'

イベント用のモデルを作成する

rails g resource event owner_id:integer name:string place:string start_time:datetime end_time:datetime content:text
  む。rails g resourceは初めて見ました。モデルとコントローラとルーティングを同時に作成するのか。ビューのhtmlファイルとかは作らないみたい。

参考:
[Ruby][Rails] Railsのgenerate scaffoldとgenerate resource

 マイグレーションファイルを修正してNOT NULL制約とインデックスを追加。このインデックスってのはなんなの?って今頃聞いちゃいけないこと?
 つーかマイグレーションつーのが未だにボンヤリしてるな。もっかい説明読めばイメージつかめるかな。

 今回は文字数制限等をvalidationで決めるって。NOT NULL制約はここに書いちゃ行けないの?ここにもpresence: trueとか書いてるじゃん。両方あれば間違いないとかそんなことかい?ググるとマイグレーションファイルは触っていない人も多い様子。
 validationはapp/models以下、event.rbで設定。
class Event < ActiveRecord::Base
validates :name, length: { maximum: 50 }, presence: true
validates :place, length: { maximum: 100 }, presence: true
validates :content, length: { maximum: 2000 }, presence: true
validates :start_time, presence: true
validates :end_time, presence: true
validate :start_time_should_be_before_end_time
private
def start_time_should_be_before_end_time
return unless start_time && end_time
if start_time >= end_time
errors.add(:start_time, 'は終了時間よりも前に設定してください')
end
end
end
 あれ?validateって検査の定義とかするんだっけ?いいのかな。開始時間が終了時間の前にちゃんと設定されているかどうかチェックするんだな。

参考:

 合わせてビューにもイベント作成のリンクを追加。リンク先のnew_event_pathはrails g resourceと同時にルーティングが追加されているので何もしなくて良い。確かにあった。

ログイン状態を管理する処理を作る

 複数のコントローラで利用する汎用的な処理は、一般的にApplicationControllerに書くそうです。そうですって、まぁ、そうでしょうね。感覚としてはわかります。でも本見ずに進めていたら、思考停止しちゃうかもしれない。
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  helper_method :current_user, :logged_in?
  private
  def current_user
  return unless session[:user_id]
  @current_user ||= User.find(session[:user_id])
  end
  def logged_in?
  !!session[:user_id]
  end
  def authenticate
  return if logged_in?
  redirect_to root_path, alert: 'ログインしてください'
  end
end
 application_controller.rbをこんな風に書き換えろとまず言われたんだけど、||=ってなに?ググったら、左辺の変数が偽か未定義の場合に右辺の値を代入する、だって。ふーん。初めて変数使うときはいつでもこうするべきなのかね。説明してよ。=だけだとなんか不都合あるんだろうか。

参考:

 authenticateのアラートを表示させるためにレイアウトも修正。コードは前後省略。
 <div class="container">
  <% if flash[:notice] %>
  <div class="alert alert-success">
  <%= flash[:notice] %>
  </div>
  <% end %>
      <% if flash[:alert] %>
        <div class="alert alert-danger">
        <%= flash[:alert] %>
        </div>
      <% end %>
  <%= yield %>
 </div>
 まただ。このflashって変数、なんだよ…。

参考:

 説明読んでもイマイチ。flash無しではダメ?前のデータが残っちゃうからか。そういうことかな。
 続いて、authenticateメソッドをEventsControllerに対して設定する。そろそろわかんなくなってきたぞ。
 authenticateをbefore_actionに設定して、未ログインユーザーがEventsControllerのアクションにアクセスしたとき、トップページにリダイレクトする。と打ち込んで3回くらい読む。
 ログインユーザーだけしかアクセスできないアクションにアクセスがあった場合には、そのアクションを起こす前にユーザーがログインしているかチェックするわけだね。未ログインならトップページに飛ばす。やっとかよ。これの理解に何分もかかっちゃうのかよ。
 今日はただでさえ頭痛が酷いのに、そこへこういったテキストを読むなんて馬鹿げているけど、気が焦ってしまって仕方ない。最近気になり始めた武藤彩未さんのアルバムでも小音量で聴いて癒されようかな。つーかBABYMETALのファンになったら次から次へと関連アイドルの知識が耳に入ってくるので困る。

 events_controller.rbについては以下の通り。
class EventsController < ApplicationController
  before_action :authenticate
  def new
  @event = current_user.created_events.build
  end
  def create
  @event = current_user.created_events.build(event_params)
  if @event.save
  redirect_to @event, notice: '作成しました'
  else
  render :new
  end
  end
  private
  def event_params
  params.require(:event).permit(
  :name, :place, :content, :start_time, :end_time
  )
  end
end
 newとcreateアクションではapplication_controllerに追加したcurrent_userメソッドを使ってログインユーザーを取得している。んー。んー。わかんね。
 あとは、ここで出てきたcreated_eventsをmodel/user.rbで定義します。
class User < ActiveRecord::Base
has_many :created_events, class_name: 'Event', foreign_key: :owner_id
以下略
 またこの項は何度も読み直す必要がありそうだな。日本語が読めていないのか、説明が悪いのか。いや、一応ベストセラーらしいし、原因は自分だよな。多分。

イベント登録用のフォームを作る

 次はイベント登録用フォームを作る。events/new.html.erbを新規作成。はい作った。
<% now = Time.zone.now %>
<div class="page-header">
<h1>イベント作成</h1>
</div>
<%= form_for(@event, class: 'form-horizontal', role: 'form') do |f| %>
<% if @event.errors.any? %>
 <div class="alert alert-danger">
  <ul>
  <% @event.errors.full_messages.each do |msg| %>
   <li><%= msg %></li>
  <% end %>
  </ul>
 </div>
<% end %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :place %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :start_time %>
<div>
<%= f.datetime_select :start_time, start_year: now.year, end_year: now.year + 1 %>
</div>
</div>
<div class="form-group">
<%= f.label :end_time %>
<div>
<%= f.datetime_select :end_time, start_year: now.year, end_year: now.year + 1 %>
</div>
</div>
<div class="form-group">
<%= f.label :content %>
<%= f.text_area :content, class: 'form-control', row: 10 %>
</div>
  <%= f.submit '作成', class: 'btn btn-default', data: { disable_with: '作成中…' } %>
<% end %>
 で、随分いろいろ打ち込みましたが、名前/場所/開始時間/終了時間/内容の入力フォームとイベント作成ボタンを作っています。
 ここで実際にブラウザで動作を確認したら打ち間違いによるエラーが死ぬほど出て死んだ。数時間かけてやっと原因がわかってげんなり。
 入力フォームが出るようにはなったけど、登録するとPlace can't be blankとか言われるな。空欄ではないんだけど…。
 再度ハマって、更に打ち間違いとコピペミスがあって、マイグレーションし直したりしてなんとかなった。酷いなオレ…。

i18nの設定をする

 なんすか、i18nって。ラベルとエラーの修正、ですか。ラベルやvalidationのエラーを日本語にしようよ、ということみたい。
 設定はconfig/spplication.rb。以下の部分をコメントアウトして、最後の右辺を:deから:jaに書き換える。
config.i18n.default_locale = :ja
汎用のエラー辞書データはrails-i18nのリポジトリにまとめられている。下記からダウンロード。
~/work/awesome_events$curl -o config/locales/ja.yml -L https://raw.github.com/svenfuchs/rails-i18n/master/rails/locale/ja.yml
以下はファイルの内容の一部。へーほー。
  errors:
    format: ! '%{attribute}%{message}'
    messages:
      accepted: を受諾してください。
      blank: を入力してください。
      present: は入力しないでください。
      confirmation: と%{attribute}の入力が一致しません。
      empty: を入力してください。
      equal_to: は%{count}にしてください。
      even: は偶数にしてください。
      exclusion: は予約されています。
  更に、同じくconfig/localesに、ar-ja.ymlとして今回のアプリ向けの辞書データを作ります。よく書式がわからないけど、スルーしとこうかな。
ja:
  acriverecord:
    models:
      event: イベント
    attributes:
      event:
        name: 名前
        place: 場所
        start_time: 開始時間
        end_time: 終了時間
        content: 内容
  あ、確かにエラーが日本語で表示される。

0 件のコメント:

コメントを投稿