Rails4 scopeでActiveRecordを簡潔に

ActiveRecordを簡潔に書くことができるscopeのメモ。


scopeの定義

class User < ActiveRecord::Base
  scope :created_before, ->(datetime) { where("created_at < ?", datetime) }
end

scopeはクラスメソッドのようにも、インスタンスメソッドのようにも使える。

# クラスメソッドのように使う
User.created_before(DateTime.now)

# インスタンスメソッドのように使う
user.created_before(DateTime.now)


scopeは複数組み合わせることができる

class User < ActiveRecord::Base
  scope :created_before, ->(datetime) { where("created_at < ?", datetime) }
  scope :order_id, -> { order(:id) }
end

使い方

# クラスメソッドのように使う
User.created_before(DateTime.now).order_id

# インスタンスメソッドのように使う
user.created_before(DateTime.now).order_id


参考:
Active Record Query Interface — Ruby on Rails Guides

Rails4 chart.jsのレーダーチャートをwicked_pdfでPDF出力 ※windows未対応

chart.jsで描画したレーダーチャートをwicked_pdfを使って、PDF出力する手順のメモ。

chart.jsで描画したレーダーチャートをwicked_pdfを使って、PDF出力しようとしたらうまくいかなかった。
wickee_pdf推奨のgem「wkhtmltopdf-binary」が更新されていないのが主な原因だった。
新たに「wkhtmltopdf-binary-11」というgemを見つけたので、これに差し替えた。


まず、Gemfileに下記を追加する。

gem 'wkhtmltopdf-binary-11'
gem 'wicked_pdf'


コマンドプロンプトでbundle installを実行する。

bundle insatall --path vendor\bundle


config/initializers/wicked_pdf.rbファイルを作成し、wkhtmltopdfのパスを記載する。

WickedPdf.config = {
  :exe_path => "#{Gem.loaded_specs['wkhtmltopdf-binary-11'].full_gem_path}/bin/wkhtmltopdf"
}


グラフの表示で、アニメーションをオフにする必要がある。

<!--レーダーチャートを描画する 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: [7,8,10,6,10]
        }
      ]
    };

    // Canvas にレーダーチャートを描画
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");
    var chart = new Chart(context);
    var rader = chart.Radar(radarChartData, {
      scaleShowLabels: true,   // 目盛を表示
      pointLabelFontSize: 10,  // ラベルを表示
      animation: false         // アニメーションをオフに
    });
  });
</script>


※注意
windows環境では動作しない。



参考:
Rails4 wicked_pdfでPDF出力 ※windows未対応 - ayaketanのプログラミング勉強日記
Rails4 Chart.jsでレーダーチャートを表示する - ayaketanのプログラミング勉強日記
wkhtmltopdf-binary-11 | RubyGems.org | your community gem host

Rails4 form_tagでactionとmethodを指定する

form_tagでactionとmethodを指定したかった。

ルーティングは下記のように設定してあった。

resource :test, only: [:edit, :update]

edit内で、form_tagを使用していたが、初期値では、methodがpostになってしまったため、自分でmethodにpatchを指定する必要があった。


まず、下記のように記載したが、うまくいかなかった。

<%= form_tag({action: 'update', method: 'patch'}) do %>

HTMLのソースを表示すると、下記のようにmethodがactionの一部になっていた。

<form accept-charset="UTF-8" action="/test?method=patch" method="post">


下記のようにハッシュを分けるとうまくいった。

<%= form_tag({action: 'update'}, {method: 'patch'}) do %>

HTMLのソースを表示すると、actionもmethodも正常に設定されていることが確認できた。

<form accept-charset="UTF-8" action="/test" method="post">
<input name="_method" type="hidden" value="patch" />

Rails4 複数のチェックボックスの入力結果を配列で取得すると、不要な"0"が含まれる

ユーザーをグループ分けするときに、そのグループに所属するユーザーをチェックボックスで選択したい。

複数のチェックボックスの入力結果を処理するときに、不要な"0"が取得できてしまったので、メモ。

まずは、ビューでチェックボックスを実装

<% @users.each do |user| %>
  <%= f.check_box :user_id, {:name => "grouping[user_id][]"}, user.id %>
  <%= f.label :user_id, user.name, :value => user.id %>
  <br>
<% end %>   

すると、HTMLは次のようになった

<input name="grouping[user_id][]" type="hidden" value="0" />
<input id="grouping_user_id" name="grouping[user_id][]" type="checkbox" value="1" />
<label for="grouping_user_id_1">ユーザー1</label>
<br>
<input name="grouping[user_id][]" type="hidden" value="0" />
<input id="grouping_user_id" name="grouping[user_id][]" type="checkbox" value="2" />
<label for="grouping_user_id_2">ユーザー2</label>
<br>
<input name="grouping[user_id][]" type="hidden" value="0" />
<input id="grouping_user_id" name="grouping[user_id][]" type="checkbox" value="3" />
<label for="grouping_user_id_3">ユーザー3</label>
<br>

意図しない

<input name="grouping[user_id][]" type="hidden" value="0" />

が追加されている。

チェックボックスでユーザー1を選択してsubmitすると、params[:grouping][:user_id]には["0","1","0","0"]と、不要な"0"が入っていた。
この"0"は未選択時のためにあるようですが、あると処理がしにくいので、コントローラで削除する必要があります。


コントローラで"0"を削除

params[:grouping][:user_id].delete("0")

すると、params[:grouping][:user_id]は、["1"]になった。


ついでに、文字列の配列を数値の配列に変換し、処理しやすくした。

user_ids = params[:grouping][:user_id].map{|s|s.to_i}


参考:
プログラマの覚書: Rails チェックボックスの配列化(修正)
Ruby on Railsで絞り込み用に複数のチェックボックスを作成する方法 | もっとクールにプログラミング

文字列の前後の空白を削除する

stripで文字列の前後の空白を削除しようとしたら、全角スペースが削除されなかった。

そこで、メソッドを自作することにした。

class String
  # 文字列前後の全角・半角スペースを削除
  def strip_with_full_size_space!
    self.gsub!(/^[ \s]*(.*?)[ \s]*$/, '\1')
  end
  
  # 文字列前後の全角・半角スペースを削除した文字列を返す
  def strip_with_full_size_space
    clone.strip_with_full_size_space!
  end
end

これで、全角・半角スペースにはもちろん、タブにも対応できた。


参考:
Ruby【全角空白も除去】できるstrip!()ありますか? s = " 全.. - 人力検索はてな
rubyで行頭や行末の全角スペースを除去する方法 - memo.yomukaku.net
正規表現の構文

Rails4 アップロードしたファイルのテスト

前回、csvファイルをアップロードして読み込む方法を調べた。
Rails4 csvファイルをアップロードして読み込む - ayaketanのプログラミング勉強日記


そのテストをおこなうため、アップロードしたファイルのテスト方法を調べた。
fixture_file_uploadを使うと、\spec\fixturesに入っているファイルのアップロードがおこなえる。

テスト用にアップロードするファイルを準備

\spec\fixtures\filesにテスト用ファイルtest_user.csvファイルを入れる。

fixture_file_uploadを使用可能にする

\spec\models\user_spec.rbの先頭でinclude

include ActionDispatch::TestProcess

ファイルをアップロードして、テストをおこなう

\spec\models\user_spec.rbにテストを書く。

it "CSVファイルを読み込んで、ハッシュの配列に変換する" do
  csv_file = fixture_file_upload('files/test_user.csv', 'text/csv')
  csv_data = User.load_csv(csv_file)
  # ここにテストを書く
end


参考:
【ruby】Railsでファイルアップロードをテストする - tanihiro.log
test a file upload using rspec - rails - Stack Overflow

Rails4 csvファイルをアップロードして読み込む

ユーザーをCSVファイルから登録したかった。
csvファイルをアップロードして読み込む方法をメモする。

ルーティングの設定

アップロードするCSVファイルの指定画面と、CSVファイルの読み込み処理のルーティングを設定する。
config\routes.rbに下記のように記載する。

resources :users do
  collection do
    get 'import_csv_new'
    post 'import_csv'
  end
end

ビューの作成

CSVファイルの指定画面を作成する。
app\viewsにimport_csv_new.html.erbファイルを作成し、下記のように記載する。
※form_tagでは、multipartを別ハッシュにすること。

<h1>社員登録(CSVファイル)</h1>
<%= form_tag({controller: "users", action: "import_csv", method: "post"}, {multipart: true}) do %>
  <div class="field"> 
    <%= label_tag "CSVファイルを選択" %><br>
    <%= file_field_tag :csv_file %>
  </div>
  
  <div class="actions">
    <%= submit_tag "CSVファイルを読み込む"%>
  </div>
<% end %>

<%= link_to '社員一覧に戻る', users_path %>

コントローラの作成

app\controllers\users_controller.rbに下記を追記する。

def import_csv_new  
end
  
def import_csv
  respond_to do |format|
    if User.impotr_csv(params[:csv_file])
      format.html { redirect_to users_path }
      format.json { head :no_content }
    else
      format.html { redirect_to users_path, :notice => "CSVファイルの読み込みに失敗しました。" }
      format.json { head :no_content }
    end
  end
end

モデルのメソッドの作成

app\models\user.rbにメソッドを作成する。

まず、ファイルの先頭でライブラリを2つ読み込む

# coding: utf-8
require 'csv'   # csv操作を可能にするライブラリ
require 'kconv' # 文字コード操作をおこなうライブラリ
class User < ActiveRecord::Base

CSVファイルを読み込み、ユーザーを登録する関数を作成する。
※実際はデータチェックなどが必要

# CSVファイルを読み込み、ユーザーを登録する
def import_csv(csv_file)
  # csvファイルを受け取って文字列にする
  csv_text = csv_file.read
    
  data = []

  #文字列をUTF-8に変換
  CSV.parse(Kconv.toutf8(csv_text)) do |row|

    user = User.new
    user.name    = row[0]  #csvの1列目を格納
    user.kana    = row[1]  #csvの2列目を格納
    user.address = row[2]  #csvの3列目を格納
    user.tel     = row[3]  #csvの4列目を格納
       
    user.save
  end
end

参考:
Ruby on Rails 3.2: CSVをアップロードしてActiveRecordにつっこむ | DYO.JP ver.2
【前編】SHIFT-JISのCSVをUTF-8でRailsにインポート (Rails 3.2.13) | heathrow.lab
【後編】SHIFT-JISのCSVをUTF-8でRailsにインポート (Rails 3.2.13) ~ActiveModelの活用 | heathrow.lab
Railsでuploadされたfile(txt等)の内容を読み込む - 初心者エンジニアのメモ帳
ファイルをアップロードしてみよう(file_field) Ruby on Rails Pro