「モトスミHack vol.34」に参加した
http://connpass.com/event/2111/
目的は悩ましい問題を解決しに
1. Rails4 beta1でハマったエラー
「ActiveModel::ForbiddenAttributesError」うぜ!!!
・問題のコード
def create @train = Train.find(params[:train_id]) @route = Train.find(params[:train_id]).routes.create(params[:route]) # ←ここでエラーになった redirect_to train_path(@train) end
まず、「Forbidden」は権限系のエラー。
今回コードで怒られた、params[:route] の権限がないってことだ。
ちなみにHTMLが↓のようにid名が「new_route」になってたから、params[:new_route]とかに適当に書いてたのは秘密だ!
<form accept-charset="UTF-8" action="/trains/1/routes" class="new_route" id="new_route" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓" /><input name="authenticity_token" type="hidden" value="65+hYRPe0NvbtZA/e7Vw9G6oWO4Ev/jb8SxdbVAIIo4=" /></div> <div class='field'>
Rails4からはセキュリティ上の関係で、個々のmodelに対して権限を書かなければならないっぽい(?)
ちなみに、Rails4の勉強出来る本↓はこれが今のところない。
この問題は、Scaffoldingを使わずにモデルとかコントローラを作成するとハマる問題らしい・・・。
Scaffoldingを自動生成するコマンドであれば、勝手に権限を付与する部分を書いてくれるから。
試しに、Scaffoldingを使ったりして、コントローラのprivate〜以下の部分を見てそれを参考にするといい。
僕は今回別のモデルはScaffoldingをたまたま使ってたので参考にしてもらった。
「params.permit」の書き方は↓のURLが参考になる
URL: https://github.com/rails/strong_parameters
↓ 僕はこう書いた(routeのコントローラ)
・app/controllers/routes_controller.rb
private # Use callbacks to share common setup or constraints between actions. def set_route # ←これは単に書いたのだが、今回は要らない @route = Route.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def route_params # ←これがいる params.require(:route).permit(:name, :order, :price, :line_name, :user_id, :train_id) end
さらに、問題だった箇所のparams[:route] を route_params の呼び出しに置き換えた
※「route_params()」で、()が要らないことに注意
・app/controllers/routes_controller.rb
def create @train = Train.find(params[:train_id]) @route = Train.find(params[:train_id]).routes.create(route_params) # ←置き換え高所 redirect_to train_path(@train) end
これで解決!
ちなみに、先ほどのhoge_paramsの部分(※今回はroute_params)だが、
def route_params params.require(:route).permit(:name, :order, :price, :line_name, :user_id, :train_id) end
バリデーションでは、"line_name"を必須属性にしていたので、line_nameを↑から外してはならない。
rails上ではエラーも何も出さなかったので気が付かなかった。
class Route < ActiveRecord::Base belongs_to :train validates_numericality_of :order, :greater_than => 0 validates_presence_of :name validates_presence_of :line_name validates_numericality_of :price, :greater_than => 0 end
2. ログの書き方
putsで書ける
class RoutesController < ApplicationController def create @train = Train.find(params[:train_id]) @route = Train.find(params[:train_id]).routes.create(route_params) puts("*" * 10) #←場所がわかりやすいようにアスタリスク10個。 puts(route_params) #←今回確認したかった「route_params」の中身 puts("*" * 10) #←場所がわかりやすいようにアスタリスク10個。 redirect_to train_path(@train) end
これで解決!
・ログ結果
********** {"name"=>"吉祥寺", "order"=>"3", "price"=>"100", "line_name"=>"中央・総武線"} **********
3. ローカライズでハマった Rails4特有?
日本語にしたい!
「config/application.rbl」 にはこう書いた、ので当然できるはずですね?
・config/application.rb
module Multatio class Application < Rails::Application # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. # config.time_zone = 'Central Time (US & Canada)' # localize config.encoding = "utf-8" config.i18n.default_locale = :ja #←ここに日本語指定してる # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de end end
Viewにはこう記載しており、
・app/views/trains/show.html.haml
↓hamlファイルの一部抜粋
%table{:class => "table"} %tr %th = t('route.order') %th = t('route.name') %th = t('route.line_name') %th = t('route.price') %tbody - @train.routes.each do |route| %tr %td= route.order %td= route.name %td= route.line_name %td= route.price
ymlファイルは↓こう書いて
・config/locales/ja.yml
models: route: "経路" attributes: route: order: "順序" name: "駅名" line_name: "路線名" price: "値段"
うまくいかない・・・・
「解決策↓」
・config/locales/ja.yml
ymlファイルは↓こう書いて、、、
view: trains: route: order: "順序" name: "駅名" line_name: "路線名" price: "値段"
Hamlもこのように↓修正した。
・app/views/trains/show.html.haml
%table{:class => "table"} %tr %th = t('view.trains.route.order') %th = t('view.trains.route.name') %th = t('view.trains.route.line_name') %th = t('view.trains.route.price') %tbody - @train.routes.each do |route| %tr %td= route.order %td= route.name %td= route.line_name %td= route.price
結論。
まー、要はymlの階層通りにhaml側で守りゃいいんですね?!
ymlではこうかいて、
hoge: bar: fuga: "あいうえお"
このhamlで呼べた
%p = t('hoge.bar.fuga')
4. showやindexで使いまわしたい。
controllerにbefore_filterに書いたらいい。
Rails4では、before_action。
get_selectで追加した。
class BooksController < ApplicationController before_action :set_book, only: [:show, :edit, :update, :destroy] before_action :get_select #←こちら
get_selectを追加した
def get_select @book_severity_select = [['1. normal', 1],['2. hurryup', 2]] @book_howto_select = [['1. team', 1],['2. team', 2],['3. other', 3]] end
5.ヘッダがおかしい。
twitter-bootstrap-railsでのバグかもしれないけど・・・。
Chromeの「alt+command + I」でCSSをリアルタイムで確認しながら
CSSのpaddingを入れればOK?
scaffolds.cssのbodyに対してpadding: 80pxがちょうどいい気がする