万年素人からHackerへの道

万年素人がHackerになれるまで殴り書きするぜ。

  • ・資産運用おすすめ
    10万円は1000円くらい利益
    資産運用ブログ アセマネ
    • ・寄付お願いします
      YENTEN:YYzNPzdsZWqr5THWAdMrKDj7GT8ietDc2W
      BitZenny:ZfpUbVya8MWQkjjGJMjA7P9pPkqaLnwPWH
      c0ban:8KG95GXdEquNpPW8xJAJf7nn5kbimQ5wj1
      Skycoin:KMqcn7x8REwwzMHPi9fV9fbNwdofYAWKRo

    「モトスミ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="&#x2713;" /><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がちょうどいい気がする