for文とeachの違いってなんなん?

最近コードレビューをしていて、for文を使っているコードを読んだときに、「Rubyだったらeach使いなよ」とレビューしてました。

Rubyだったらeach。お決まりのように考えていたのですが…なんで?

違い

結論から言うと、for文は配列の要素を受け取る変数、for文の中で作成したローカル変数がfor文の外でも扱うことができるということ

each文

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

numbers.each do |n|
  sum += n
  sumsum = sum += n
end

irb(main):014:0> sum
=> 10

irb(main):025:0> sumsum
NameError: undefined local variable or method `sumsum' for main:Object
    from (irb):25
    from /Users/kei/.rbenv/versions/2.4.1/bin/irb:11:in `<main>'

for文

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

for n in numbers
  sum += n
  sumsum = sum += n
end

irb(main):016:0> n
=> 4
irb(main):017:0> sum
=> 10
irb(main):018:0> sumsum
=> 10

for文の外でも使えてる!

なぜかも説明できないのに「そういうもんだから」っていうのはだめだなぁ…と感じました。もっと勉強しよ。

f:id:takakudakei:20181129005549j:plain

do...end と {} の違い

do...end と {} の違い

基本的にはどっちを使っても結果は同じ。

違いは、 do...end よりも {} のほうが

結合度が強い

どういうことか?

配列のdeleteメソッドの例

a = [1, 2, 3]

a.delete(100)
=> 0

a.delete(100) do
  'できない'
end
=> "できない"

ブロックを渡さない時は指定した値が見つからないとnilが返る

ブロックを渡すと指定した値が見つからない時はブロックの戻り値が戻り値となる

Ruby は引数の()を省略することができる

a.delete 100 do
  'できない'
end
=> "できない"

これは動く

a.delete 100 { 'できない' }
=> syntax error, unexpected '{', expecting end-of-input

結合度が強いとはこういうことらしい

a.delete 100 ではなく 100 { 'できない' } と解釈される

このように引数付きのメソッド呼び出しで{}をブロックとして使う場合は、メソッド引数の()を省略することができない

  • Ruby2.4.0, 2.4.1はRuby側の不具合でエラーが出ない。

普段 {} のほうがワンライナーで書けるーくらいしか思ってなくて、何が違うのかなんて考えたこともなかった…

ブロックローカル変数

ブロックローカル変数とは?

あまり使う機会はない。というか使わないと思う。

だけどもまたRuby勉強し直してて初めて知ったことなのでめも。

ブロック引数を;で区切り、続けて変数を宣言すると、ブロック内でのみ有効な独立した変数を扱うことができる

numbers = [1, 2, 3, 4]
sum = 0
# ブロック外のsumとは別物の変数sumを用意する
numbers.each do |n; sum|
  sum = 10
  sum += n
  p sum
end

=>

11
12
13
14

# ブロック内で使ったsumとブロック外のsumは別物なのでブロック外sumは変化なし

sum => 0

変数に適切な名前をつければブロックローカル変数なんて使わなそう…

チェリー本読んでるけどまだまだ知らないこと多い。

rake taskで可変長引数を使う

rake taskで可変長引数を使う

約1年間Railsエンジニアやってて初めてrake task書いた。

その時のめも。

to_a メソッドを使う

namespace :test_task do
  desc '可変長引数を受け取るtask'
  task :hoge, ['huga'] => :environment do |_task, args|
    p args.to_a
    p args.huga
  end
end

実行する。

$ bundle exec rake test_task:hoge[1,2,3,4]
["1", "2", "3", "4"]
"1"

argsHashではなくRake::TaskArgumentsであり、Rake::TaskArgumentsインスタンスメソッドである to_aを使うことで引数の値をすべて取得する事ができる

  • リファレンス

http://www.rubydoc.info/gems/rake/12.0.0/Rake/TaskArguments#to_a-instance_method

Ruby勉強し直してみた 3

メソッドの可変長引数

Ruby勉強し直してて普段あんま使ったことないのでめも。

自分で定義するメソッドで可変長引数を使いたければ引数名の手前に*をつける

def メソッド名(引数1, 引数2, *可変長引数)
  # メソッド処理
end

みたいに。

def test(*name)
  name
end

test('tanaka', 'fuzita', 'yamada')
=> ['tanaka', 'fuzita', 'yamada']

可変長引数で渡された引数は配列になる

さっそく使ってみたい…!

Ruby勉強し直してみた 2

inject/reduce

たたみ込み演算なるもの… 例を見たほうが早い

numbers = [1, 2, 3, 4]
sum = numbers.inject(0) { |result, n| result + n }

ブロックの第一引数(result)には初回のみinjectメソッドの引数が入る。

2回目以降は前回のブロックの戻り値が入る。

繰り返し処理が最後まで終わると、ブロックの戻り値がinjectメソッドの戻り値になる。

チェリー本にて復習中だけども、以外と知らなかったことが多い…

Danger 導入した

機械的なレビューを自動で行ってくれるDangerというgemを最近導入したので、その時の

Dangerってそもそも何?

image.png

 

DangerのGithubには以下のことが書かれています

 Stop saying "you forgot to …" in code review

 Formalize your Pull Request etiquette.

  

「あなたは〜をやることを忘れているよ。」ということをやめる。

プルリクエストの礼儀作法を形式化

 

つまり

「プルリクエストの礼儀作法を形式化し、指摘を自動化する」ための便利なgemです

 

導入背景

チーム開発をしていると、そのチームごとにプルリクエストの書き方の決まりやルールなどができてくると思います。例えば

  • テストは必ず書きましょう
  • レビューしてもらいたい時はWIPを外しましょう
  • labelを必ずつけましょう
  • 変更が多い場合はプルリクエストを分けましょう

…などなど

もちろん自分のチームでも上記のようなルールがあったのですが、

  • テストを書いていなかったり
  • WIPつけっぱなしのままレビュー依頼出しちゃったり
  • labelつけ忘れてたり
  • 1つのプルリクエストでの変更が多すぎたり

…などなどが発生しており、そのようなプルリクエストが上がるたびに同じ指摘を繰り返していました。

しかし、上記で上げたような指摘って誰にでもできるものですよね? いちいち同じ指摘する工数工数なので自動化しよう!!というのがきっかけです

 

Danger導入

Circle CiでDangerを実行し、プルリクエストに自動でコメントします

 

Gemfileの設定

source "https://rubygems.org"
gem 'danger'

Gemfile編集後に'bundle install'コマンドを実行します

 

Dangerfile設定

'bundle exec danger init'コマンドを実行します

'danger init'を実行すると初期セットアップ実行され、'Dangerfile'が作成されます。

'Dangerfile'としてはクックパッド開発者ブログを参考に以下のように編集しました。

# ===== PR title =====
warn('PR is classed as Work in Progress') if github.pr_title.include? '[WIP]'

# ===== diff size =====
warn('PRの変更量が多すぎます。PRを分割しましょう!') if git.lines_of_code > 500

# ===== Test =====
raise('テストが書かれていません!') if `grep -r fdescribe specs/ `.length > 1
raise('テストが書かれていません!') if `grep -r fit specs/ `.length > 1
# ===== Label ====
labels = github.pr_labels
warn('labelを選択してください!') if labels.empty?

 

  • WIPがつけっぱなしのとき
  • プルリクエストの変更差分が多すぎるとき
  • テストが書かれていないとき
  • labelをつけていないとき

に自動でレビューを行ってくれる設定です。

 

'Dangerfile'を作成すると長文の説明文が流れてくるので、読みつつ、設定しつつenterで進めていきます

 

  • Step1

'Dangerfile'を作成しましたというメッセージ

 

  • Step2

bot用のGithubアカウントの作成を促されます。

'Danger'からプルリクエストに対してコメントの書き込みが行われるので、そのためのアカウントを作成しておくと良いです。

 

  • Step3

botアカウントでアクセストークンを作るように、と言われます。

リポジトリにアクセス可能なアカウントでAPI トークンを作成します。

個人のアカウントでも問題はないのですが、bot用のアカウントだと機械的に指摘されたコメントというように区別できてわかりやすいです

 

  • Step4

Ciの設定を行います。

詳しい案内はないのでGetting Set Upを参照します

Ciのsettingsで対象のリポジトリAPI permissionsにトークンを登録します。

 

'.circleci/config.yml'に以下のように追記します

- run:
name: Run Danger file
command: bundle exec danger --verbose

 

最後に!

Circle Ciでdangerを使う場合は 'Only Build pull requests'を有効にし、プルリクエスト作成時のスキップを回避してください

 

これで設定は終了です。

プルリクエストをWIPつけっぱなしで作成するとちゃんと指摘してくれてますね! image.png  

まとめ

このような感じで誰でもできるようなレビューはdangerにまかせて、本当にレビューする必要がある箇所に意識が集中できるようにどんどんしていきたいですね!