RubyとかRailsの小技をまとめるスクラップ
sendメソッド
class User
def hello
puts "hello from user"
end
end
user = User.new
user.hello # => hello from user
# sendを使った書き方
user.send(:hello) # => hello from user
user.send("hello") # => hello from user
data: {confirm: "削除しますか?"}
link_to
のオプションにdata: {confirm: "メッセージ"}
をつけると、リンク先に移動する前にjsのダイアログで確認ができる
<%= link_to '削除', task_path(task), method: :delete, data: {confirm: "削除しますか?"} %>
たしかこれはrails-ujsのおかげだった。
each_with_indexとeach.with_index
each_with_index
indexの初期値は0
array = %w(foo bar baz)
array.each_with_index do |x, index|
puts "#{index}番目#{x}"
end
=>
0番目foo
1番目bar
2番目baz
each.with_index
引数にindexの初期値をいれる
array = %w(foo bar baz)
array.each.with_index(1) do |x, index|
puts "#{index}番目#{x}"
end
=>
1番目foo
2番目bar
3番目baz
redirect_toとflashメッセージ
redirect_to hoge_url, notice: '成功しました'
という書き方で、リダイレクトとflashメッセージの代入を同時にできる。
これが使えるのはnotice
とalert
だけ。
renderとreturnを1行で書きたい
# OK
render(:new) && return
render :new and return
# NG
render :new && return
.to_s(:delimited)
3桁以上の数値にカンマをつけて出力したい時に使える。
10000000.to_s(:delimited)
=> "10,000,000"
1000.to_s(:delimited)
=> "1,000"
100.to_s(:delimited)
=> "100"
CSRF対策を一部解除
class ApplicationController < ActionController::Base
protect_from_forgery except: [:action_name]
end
truncate(文字数)
引数に指定した文字数で丸めてくれる。
'abcdefghtjklmn'.truncate(5)
=> "ab..."
'abcdefghtjklmn'.truncate(10)
=> "abcdefg..."
'abcdefghtjklmn'.truncate(15)
=> "abcdefghtjklmn"
gsubで文字列を置換
'abcdefg'.gsub(/abc/, 'ABC')
=> "ABCdefg"
'abcdefg'.gsub(/abc/, '')
=> "defg"
'1234567890'.gsub(/([0-9]{2})([0-9]+)/, '※※\2')
=> "※※34567890"
field_with_errors
フォームでバリデーションエラーになった要素はdiv.field_with_errors
で囲まれる。
エラー箇所を強調したい時に便利だけど、場合によってはスタイルが崩れることもある。
beginとendでコメントアウト
html.erb
ファイルのコメントアウトに
beginとendが使える
<%
=begin
%>
<p>普通のHTMLの記述もコメントアウト</p>
<%= 'Rubyの記述もコメントアウト' %>
<%
=end
%>
テキストの改行を維持して出力する
<%= simple_format(h(@hoge)) %>
リダイレクトして処理を終了する
return redirect_to hoge_url
# or
redirect_to hoge_url && return
日付や時刻のデータを生成する
Date.new(1993, 2, 24)
Date.parse('1993-02-24')
DateTime.new(2021, 2, 24, 12, 30, 45)
DateTime.parse('1993-02-24T12:30:45')
number_to_currency
number_to_currency(123456789)
# $123,456,789.00
number_to_currency(123456789, unit: "¥")
# ¥123,456,789.00
number_to_currency(123456789, format: "%u%n", unit: "¥")
# 123,456,789.00円
# config/locales/jp/yml
jp:
number:
currency:
format:
unit: "円"
format: "%n%u"
negative_format: "-%n%u"
precision: 0
number_to_currency(123456789, locale: 'jp')
# 123,456,789円
time_ago_in_words
現在時刻と引数に渡した時刻の差分を出力
time_ago_in_words(Time.current)
#=> less than a minute
time_ago_in_words(Time.current + 3.days)
#=> 3 days
キーワード引数
メソッドの初期値を指定したり、呼び出し側で何のために引数を代入してるかわかりやすくできる。
def hello(from: 'Me', to: 'World')
puts "#{from}: Hello, #{to}!"
end
hello(to: 'My World') #=> Me: Hello, My World!
hello(from: 'Tom') #=> Tom: Hello, World!
def hoge(a: )
p a
end
hoge(a: 'aaa')
#=> "aaa"
hoge('aaa')
#=> ArgumentError: missing keyword: a
reverse_each
(1..4).reverse_each do |i|
p i
end
4
3
2
1
strftimeで0埋めを回避
08月01日とかにならないように-
をつける
Time.now.strftime('%Y/%-m/%-d %-H:%-M:%-S')
#=> 2012/08/01 9:4:10
Layoutファイルを指定する
コントローラー単位で指定
class HogesController < ApplicationController
layout 'hoge'
end
アクション単位で指定
class FugasController < ApplicationController
def show
render layout: 'fugashow'
end
end
レイアウトを指定しない
class BooksController < ApplicationController
layout false
def show
end
end
class MusicsController < ApplicationController
def show
render layout: false
end
end
browser
ブラウザや端末を判別するgem
数値を0埋めする
"%02d" %
を使う
pry(main)> "%02d" % 5
=> "05"
pry(main)> "%02d" % 9
=> "09"
pry(main)> "%02d" % 10
=> "10"
pry(main)> "%02d" % 100
=> "100"
pry(main)> "%04d" % 5
=> "0005"
bin
ディレクトリ直下に以下のようなファイルを作ると
#!/bin/sh
rubocop -a
yarn stylelint:fix
$ bin/hogehoge
だけで、ファイル内に記述したコマンドをまとめて叩ける。
collection_select
プルダウンメニューをDBの値から生成できるフォームヘルパー
<%= f.collection_select :anime_id, Anime.all, :id, :title %>
- 第1引数[:anime_id]
- 値を登録するDB上のカラム名
- 第2引数[Anime.all]
- プルダウンを構成するデータを取得するためのActiveRecordの取得メソッド
- 第3引数[:id]
- DBに登録するカラム
- 第4引数[:title]
- プルダウンに表示するカラム
-e
オプション
rspecの# 指定したディレクトリの'自動更新'という文字列が名前に入っているexample(it)だけを実行する
$ rspec spec/models -e '自動更新'
throw :abort
Rails5.1以降ではbefore_xxx
で false を返してもそのまま処理が続行される。
処理を停止したい場合は throw :abort する。
ENV[]
とENV.fetch()
の違い
HOGE='ハローハロー'
ENV['HOGE']
=> "ハローハロー"
ENV.fetch('HOGE')
=> "ハローハロー"
ENV['FUGA']
=> nil
ENV.fetch('FUGA')
=> KeyError: key not found: "FUGA"
ENV.fetch('FUGA'){'うううう'}
=> "うううう"
to_sql
Blog.joins(:articles).where(articles: { name: "hoge" }).to_sql
# => SELECT "blogs".* FROM "blogs" INNER JOIN "articles" ON "articles"."blog_id" = "blogs"."id" WHERE "articles"."name" = 'hoge'
クエリを確認できる。
includes使って関連テーブルのデータを取ってこようとしてる場合、1つ目のSQL文しか表示されないので注意。
Answer.where(question_id: 181).includes(:user)
Answer Load (0.8ms) SELECT `answers`.* FROM `answers` WHERE `answers`.`question_id` = 181
User Load (0.9ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (34, 28, 50, 93)
=> [#<Answer:0x00007f5ee608f588
Answer.where(question_id: 181).includes(:user).to_sql
=> "SELECT `answers`.* FROM `answers` WHERE `answers`.`question_id` = 181"
.map(&:method_name)
29. # array.map(&:メソッド名)の記法
['a','b'].map(&:upcase) #=> ["A", "B"]
# 省略しない記法
['a','b'].map do |string|
:upcase.to_proc.call(string)
end
#=> ["A", "B"]
trip_tags
30. > ApplicationController.helpers.strip_tags('<p>あああ</p>')
=> "あああ"
find
メソッド
31.ActiveRecordの方じゃなくて、Rubyの配列操作のメソッド
select
メソッドと違い、一致する最初の要素だけを返す
> [1, 2, 3, 4, 5, 6].find { |n| n % 2 == 0 }
=> 2
as
を使ってroutingに名前をつける
32. get 'exit', to: 'sessions#destroy', as: :logout
上のルーティングではlogout_pathとlogout_urlがアプリケーションの名前付きルーティングヘルパーとして作成されます。logout_pathを呼び出すと/exitが返されます。
db:seed:replant
33. Rails6からの新機能。
dbのレコードを全削除した後に、seedを実行することができる。
ステージングやレビュー用アプリで、データベースをDROPせずにseedを再実行したい場合に特に便利。
date_select
34. formで年月日を選択させるときなどに使える。
35. bodyタグのクラスに、コントローラー名とアクション名を動的にセット
<body class="<%= "#{controller_name} #{action_name}" %>">
application.html.erb
のbody
タグにコントローラー名とアクション名のクラスを付与するようにしておくと、CSSの名前空間ができる。
こうすることで、item
、list
みたいな単純なクラス名を、重複を気にすることなく使うことができる。BEMから解放されてCSS設計が楽になる。またSCSS内のクラス名の記述が&__element
みたいにならないので、ファイル検索も楽になる。
.users {
//
&.new {
//
}
&.index {
//
}
}
.articles {
//
&.edit {
//
}
&.show {
//
}
}
strip_heredoc
36. ヒアドキュメント冒頭のインデントを消す
puts <<-'EOS'
おじいさんは、山へ芝刈りに。
おばあさんは、川へ洗濯物に。
EOS
おじいさんは、山へ芝刈りに。
おばあさんは、川へ洗濯物に。
↓
puts <<-'EOS'.strip_heredoc
おじいさんは、山へ芝刈りに。
おばあさんは、川へ洗濯物に。
めでたし、めでたし。
EOS
おじいさんは、山へ芝刈りに。
おばあさんは、川へ洗濯物に。
めでたし、めでたし。
after_action
37. レンダリング後に共通処理を入れたい時とかに使える。before_action
と同じノリで書く。
38. 自己代入
hoge || = 'hogeがnilならこれを代入'
hoge = foo || bar || baz || '全部nilならこれを代入'
find_or_initialize_by
39. #太郎という名前のユーザーが存在すればfind、しなければnew
User.find_or_initialize_by(first_name: '太郎')
40. Numbered parameter(ナンパラ)
# ナンパラを使わない場合
%w(goro, toshiya, gibson).map{ |i| i.upcase }
%w(goro, toshiya, gibson).map(&:upcase)
# ナンパラを使う場合
%w(goro, toshiya, gibson).map{ _1.upcase }
in?
メソッド
41. "aaa".in?(["aaa", "bbb", "ccc"]) # => true
42. RubyでHTTPリクエストを投げる方法
require 'uri'
require 'net/http'
uri = URI('https://hogehoge.com/fuga')
params = {
token: 'aaabbbccc',
user_id: 12345
}
uri.query = URI.encode_www_form(params)
res = Net::HTTP.get_response(uri)
puts res.body if res.is_a?(Net::HTTPSuccess)
require 'uri'
require 'net/http'
uri = URI('https://hogehoge.com/fuga')
params = {
token: 'aaabbbccc',
user_id: 12345
}
res = Net::HTTP.post_form(uri, params)
puts res.body if res.is_a?(Net::HTTPSuccess)
43. Rubyにおけるtrue/falseの判定
以下のような条件分岐があったとき
if hoge
puts "trueだよ!"
else
puts "falseだよ!"
end
「falseだよ!」が出力されるのは、hoge == false
, hoge == nil
の時だけ。
それ以外([]
とか)は全部trueとして扱われる。
==
、===
、eql
、equal?
の違い
44. /foo/ == "foo" #=> false
/foo/ === "foo" #=> true
(0..10) == 1 #=> false
(0..10) === 1 #=> true
1 == 1.0 #=> true
1.eql?(1.0) #=> false
a = b = "foo"
a.equal?(b) #=> true
c = "bar"
d = "bar"
c.equal?(d) #=> false
ポイント
- 基本的に
==
を使っていれば問題なさそう - case式の条件の比較は、内部的に
===
が使われている - hashのキーは、
eql?
がtrueのときに同一のキーだとみなされる-
hash[1]
とhash[1.0]
は異なるキーだとみなされる
-
/:id
の代わりに/:hogehoge
を使う
45. ルーティングでresources :users, param: :hogehoge
$ rails routes
Prefix Verb URI Pattern Controller#Action
edit_user GET /users/:hogehoge/edit(.:format) users#edit
user GET /users/:hogehoge(.:format) users#show
PATCH /users/:hogehoge(.:format) users#update
PUT /users/:hogehoge(.:format) users#update
DELETE /users/:hogehoge(.:format) users#destroy
class UsersController < ApplicationController
def show
# URL上の値をparams[:hogehoge]で値を取得できる
@user = User.find_by(username: params[:hogehoge])
end
end
46 if文の戻り値を代入
greeting = if japanese?
'こんにちは'
else
'Hello'
end
参考演算子で書くとこう。状況に応じて使い分ける
greeting = japanese? ? 'こんにちは' : 'Hello'
47 長いコードを改行する方法
式で長くなるとき
+
や=
などを行末に置けば、式が終わっていないものとみなされるので改行を入れることができる
total = english_score +
math_score +
sciense_score
文字列で長くなるとき
\
を使う。Ruby Style Guideでは「良い例(ただし、それでも極めて醜い)」とされている書き方
long_string = "hogehogehogehoge fugafugafugafuga"
long_string = "hogehogehogehoge "\
"fugafugafugafuga"
48 transactionを使うときの注意点
transaction中にrescueするとロールバックしない
例外の捕捉をトリガーにロールバックが行われるので、自分で例外をキャッチして処理すると、ロールバックが行われなくなってしまう。
class User < ApplicationRecord
validates :name, uniqueness: true
end
def exec_transaction
ApplicationRecord.transaction do
User.create!(name: '山田太郎')
User.create!(name: '山田太郎')
end
end
def exec_transaction
ApplicationRecord.transaction do
User.create!(name: '山田太郎')
User.create!(name: '山田太郎')
rescue
#エラー発生時に行いたい処理
end
end
def exec_transaction
ApplicationRecord.transaction do
result1 = User.create(name: '山田太郎')
result2 = User.create(name: '山田太郎')
unless (result1 && result2)
raise ActiveRecord::Rollback #明示的に例外を発生させる
end
end
end
# ロールバックが発生したら例外は再度送出される、という性質を利用する
def exec_transaction
ApplicationRecord.transaction do
User.create!(name: '山田太郎')
User.create!(name: '山田太郎')
end
rescue
#エラー発生時に行いたい処理
end
49 配列の末尾に値を追加
# 以下2つは同じ意味
array.push('hoge')
array << 'hoge'
50 意図的に例外を発生させる方法
raise
メソッドを使う
# エラークラスを指定しないと、RuntimeErrorが発生する
raise
# エラーメッセージをセットするとログに出力される。またrescue文の中でe.messageとして参照できる
raise 'ooでエラーが発生しました'
# 発生させるエラークラスを指定することもできる
raise ArgumentError, 'ooでエラーが発生しました'
raise ArgumentError.new('ooでエラーが発生しました')