ブロック「do...end」と「{}」/CSVを扱う/sendメソッド/respond_to/strftimeメソッド/send_dataメソッド

ブロックdo...end{}

ブロックの文法の種類を忘れていたので復習しました。

numbers = [1, 2, 3, 4]
sum = 0

(i)標準的なブロック文
numbers.each do |n|
  sum += n
end

(ii)改行しないブロック文
numbers.each do |n| sum += n end

(iii)「do...end」を使う代わりに「{}」を使ったブロック文
numbers.each { |n| sum += n }

(i), (ii), (iii)のどれも出力は同じ。
sum #=> 10

この使い分けとしては、

  • 改行を含む長いブロックを書く場合は → do...end

  • 1行でコンパクトに書きたい場合は → {}

(iii)の「{}」でブロックを実装する方法が多いのですが、この書き方を忘れていてつまづいた。

CSVを扱う

Rubycsvライブラリを利用する。(config/application.rb)に記述します。

require 'csv'
csv_data = CSV.read('member.csv', headers: true)

headers: trueオプションを使うと、CSVの初めの1行はheaderとして出力の際に無視されます。今回はこのオプションを使っていますが、headerがないときはこのオプションはいりません。

sendメソッド

sendとは、レシーバの持っているメソッドを呼び出してくれるメソッドのこと。 レシーバの持っているメソッドを呼び出し、そのメソッドの戻り値を返します。 以下はメソッドを定義したときの例です。

class User
  def name
    puts "taro"
  end
end

user = User.new
#定義したメソッドを呼び出す
user.name  # => taro

# sendを使った書き方
user.send(:name) # => taro
user.send("name")  # => taro

CSVをエクスポートするときに使った。

CSV.generate(headers: true) do |csv|
  csv << csv_attributes
  all.each do |task|
    csv << csv_attributes.map{ |attr| task.send(attr) }
end

respond_toにできること

def destroy
    @tweet = Tweet.find(params[:id])
    respond_to do |format|
      if @tweet.destroy
         format.html { redirect_to root_path }
         #root_pathへリダイレクトする
         format.js { render :action => "index" }
         #.index.html.erbで作成されたものをjson形式で表示する。
      end
    end  
end

destroy action が呼び出された時、formatがhtmlで指定されていたらroot_pathにリダイレクト、jsonで指定されていたらindex.html.erbをindex.html.jsで返す

strftimeメソッド

Rubyのstrftimeメソッドは、日付や日時のデータを編集して文字列に変換するためのメソッドです。

使い方はstrftimeメソッドの第1引数に書式を指定して次のように記述します。

> Time.zone.now
=> Tue, 12 Nov 2019 13:57:27 UTC +00:00
> Time.zone.now.strftime('%Y%m%d%S')
=> "2019111242"
> Time.zone.now.strftime("%Y年%-m月%-d日 %-H時%-M分%-S秒")
=> "2019年11月12日 14時11分17秒"

send_dataメソッド

CSV出力機能を実装したかったので、このsend_dataメソッドを使ってレスポンスを送り出し、送り出したデータをブラウザからファイルとしてダウンロードできるようにします。

send_data(data、options = {})

第一引数:生成するCSVデータ

オプション:filename -使用するブラウザのファイル名を提案します。

今回は下記の様に実装した。

def index
  @q = current_user.tasks.ransack(params[:q])
  @tasks = @q.result(distinct: true)
  
  respond_to do |format|
    format.html
    format.csv { send_data @task.generate_csv,
filename: "tasks-#{Time.zone.now.strftime('%Y%m%d%S')}.csv"}
  end
end

参照:send_data - Rails API