Rails4 scopeでの並び順指定

scopeで並び順を昇順と降順に指定する方法のメモ。


例えば、scoreで並び順を指定する場合、昇順と降順は下記のように指定する。

# 昇順
scope :order_score, -> { order(:score) }
# 降順
scope :order_score_desc, -> { order(score: :desc) }


参考:
Rails 4 default scope - Stack Overflow
Rails4 scopeでActiveRecordを簡潔に - ayaketanのプログラミング勉強日記

Rails4 アクション毎にレイアウトを変更する

アクション毎にレイアウトを変更しようとして、1度失敗したので、メモ。


下記の2つのレイアウトファイルを作成した。
app/views/layouts/basic.html.erb
app/views/layouts/custom.html.erb


indexにbasic.html.erbのレイアウト、showにcustom.html.erbのレイアウトを適用しようと、下記のように記載した。

class UsersController < ApplicationController
  layout 'basic', only: [:index]
  layout 'custom', only: [:show]

結果は、showにはcustom.html.erbのレイアウトが適用されたが、indexにはレイアウトが適用されなかった。


アクション毎にレイアウトを適用したい場合は、下記のようにrenderを使う。

def index   
  @users = User.all

  render layout: 'basic'
end

def show   
  render layout: 'custom'
end

※アクションの最後にrenderを記載すること。

参考:
コントローラやアクション毎に使用するレイアウトを切り替える - Ruby on Rails入門

Rails4 1つのモデルに対して複数の参照がある場合のdependent destroy

ユーザーの組み合わせを定義したモデルをつくり、複数の参照を設定していた。
dependent destroyを設定しようとして躓いたので、メモ。

combinationモデルでは、user_idとpartner_idがuserモデルを参照しているものとする。

当初下記のように記載していた。
user.rb

has_many :combinations

combination.rb

belongs_to :user
belongs_to :partner_user, class_name: 'User', foreign_key: "partner_id"

ここで、ユーザーが削除されたら組み合わせも削除しようと、user.rbを下記のように修正した。

has_many :combinations, dependent: :destroy

すると、ユーザーを削除した場合、user_idが一致するデータは削除されるが、partner_idが一致するデータは削除されなかった。


次のように修正するとうまくいった。
user.rb

has_many :combinations, dependent: :destroy
has_many :partner_combinations, class_name: 'Combination', foreign_key: 'partner_id', dependent: :destroy

combination.rb

belongs_to :user
belongs_to :partner_user, class_name: 'User', foreign_key: 'partner_id'

参考:
複数の外部キーに対してdependent=>nullifyする方法 | Geekの逆襲

Rails4 date_selectにclassを指定する方法

date_selectにclassを指定したかったが、うまくいかなかった。

<%= f.date_select :deadline_at, order: [ :year, :month, :day ], use_month_numbers: true %>

オプションとHTMLのオプションを別の{}で囲むとうまくいった。

<%= f.date_select :deadline_at, {order: [ :year, :month, :day ], use_month_numbers: true}, {class: "form-control"} %>


参考
Rails の f.date_select とかで class を追加したい場合の件 - 牌語備忘録 - pygo

Rails4 レイアウトの継承

レイアウトを管理者と一般ユーザー用に分けたかった。
コントローラーを継承するのではなく、レイアウトで分ける方法を見つけたので、メモ。

app/views/layouts/application.html.erbに下記のように記載した。

<!DOCTYPE html>
<html>
  <head>
    <title>SampleProject</title>
    <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <%= csrf_meta_tags %>
  </head>
  <body>
    <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
      <div class="container">
        <div class="navbar-header">
          <button class="navbar-toggle" type="button" data-togle="collapse" data-target=".navbar-collapse">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>

          <%= link_to "SampleProject", root_path, class:  "navbar-brand" %>
        </div>

        <div class="navbar-collapse collapse">
          <% unless yield(:navbar).empty? %>
            <%= yield(:navbar) %>
          <% end %>
        </div>
      </div>
    </div>
    <div id="content">
      <%= content_for?(:content) ? yield(:content) : yield %>
    </div>
  </body>
</html>


ポイントは、contentのこの部分。
contentが指定されていたら、そちらを使い、指定されていなければ、yieldでそれぞれのビューを読み込みます。

<div id="content">
  <%= content_for?(:content) ? yield(:content) : yield %>
</div>


app/views/layouts/admin.html.erbを作成し、下記のように記載。
contentにヘッダーを追加してみました。

<% content_for :content do %>
  <h1>システム管理者専用ページ</h1>
  <%= yield %>
<% end %>
<%= render template: "layouts/application" %>


app/controllers/admin/users_controller.rbで、レイアウトに先ほど作成したadmin.html.erbを指定する。

class Admin::UsersController < ApplicationController
  layout 'admin'


すると、この管理者用のユーザー画面には、applicationレイアウトで指定したナビバーと、adminレイアウトで指定した「システム管理者専用ページ」というヘッダーが表示され、ヘッダーの下にユーザー独自のレイアウトが設定される。
f:id:ayaketan:20140212213611p:plain

ちなみに、adminレイアウトを指定していない他のコントローラのビューは今まで通りに表示されます。

これは色々と応用できそうです。


参考:
Layouts and Rendering in Rails — Ruby on Rails Guides

Rails4 Chart.jsでレーダーチャートの目盛ラベルを変更する

chart.jsで描画したレーダーチャートの目盛ラベルを変更するのに苦戦したので、その方法をメモ。


コントローラで、表示したい目盛ラベルを変数に代入する。

デフォルトと同じ
@scale_label = "<%= value %>"
デフォルトの目盛ラベルにPを付加する場合
@scale_label = "<%= value %>P"
アルファベットに置き換える場合
@scale_label = "<%= value.replace('5','A').replace('4','B').replace('3','C').replace('2','D').replace('1','E') %>"


erbファイルでコントローラで設定した変数をscaleLabelにエスケープをオフにして設定する。
この時、<%= %>をダブルクォーテーションで囲むこと。

<!--レーダーチャートを描画する Canvas-->
<canvas id="canvas" width="300px" height="300px">
</canvas>

<%= javascript_include_tag "Chart" %>
<script>
  $(function() {
    // レーダーチャートで表示するデータを用意
    var radarChartData = {
      labels: ["スタミナ", "スピード", "テクニック", "パワー", "メンタル"],
      datasets: [
        {
          fillColor: "rgba(151,187,205,0.5)", // 線で囲まれた部分の色
          strokeColor: "rgba(151,187,205,1)", // 線の色
          pointColor: "rgba(151,187,205,1)",  // 点の色
          pointStrokeColor: "#fff",           // 点を囲む線の色
          data: [3,4,5,2,3]
        }
      ]
    };

    // Canvas にレーダーチャートを描画
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");
    var chart = new Chart(context);
    var rader = chart.Radar(radarChartData, {
      scaleOverride : true,   // 目盛の設定を変更
      scaleSteps : 5,         // 目盛の数
      scaleStepWidth : 1,     // 目盛間隔
      scaleStartValue : 0,    // 目盛の最小値
      scaleShowLabels : true, // 目盛ラベルの使用フラグ
      scaleLabel : "<%= @scale_label.html_safe %>", // 目盛ラベル
      animation: false        // アニメーションの使用フラグ
    });
  });
</script>


参考:
[JavaScript] Chart.js を使って線グラフを描画する | unlinked log