たわらの技術ブログ

Railsエンジニアの忘備録

rake タスク内で処理を止めたい場合は return ではなく、next を利用するみたい。

こんにちは。

とある案件のコードの rake タスクにこんなコードがありまして、、、

next unless Time.zone.today.on_weekday?

next? なんじゃこりゃ。となりまして、、、

先輩「rake タスクには return じゃなくて next を使うんだよ!」

と教えてもらいました。

で、調べてみると、、、こんな記事がありました。

stackoverflow.com

いいねがたくさんついている回答いわく、

ブロックのなかでは、returnは使えない(ただし例外はある)

みたいですね。なので next を使うそうです。

ただし別の箇所でメソッドを定義して、そのなかで returnを使用する、というやり方もあるみたいです。

こんなふうなコードが紹介されてました。なるほど。

task :foo do
  do_something
end

def do_something
  puts "startd"
  return
  puts "end"
end

またひとつ知識が増えてよかったです!

(了)

git log -p ファイル名 で該当ファイルの変更履歴が見れる

こんにちは。

職場の先輩に教えてもらった技をメモしときます。

git log -p ファイル名

上記コマンドで、該当ファイルの変更履歴が見られます。

f:id:KentaroTawara:20210119214147p:plain
こんな感じです

誰が、いつ、何を変更して、どんなコミットメッセージをしているかが一目瞭然ですね!

コードリーディングに困ったときに役立つかもしれません。

調べてみると、git log コマンドにオプションをいろいろ付けてカスタマイズできるみたいですね。 git log -p で物足りなくなったら調べてみます。

(了)

letter_opener でメールを受信できない、、、あ、ジョブ管理ライブラリ起動してなかったからか

こんにちは。

とある日仕事をしていて、、、

「なんで会員登録のメールはすぐに受信するのに、こっちの処理で飛ばしてるはずのメールはすぐに受信できないんだろう?」と不思議がっていました。

で、先輩に質問すると、、

「メールとか、重い処理は本流からはずれて処理をするんだよ。bundle exec rake job:workをローカルで実行すれば、メールを確認できるはずだよ」

と言われて、そのとおり実行すると、無事にメールを受信できて、開発が前進しました。

ということで本記事は、ジョブ管理のお話。

ActionJob というものがある

時間のかかる処理をリアルタイムで行うと、その間、ユーザーは待たされてしまいます。大量データの集計処理や外部サービスの連携、そしてメール送信などが例に挙げられます。

リアルタイムで行う必要がない処理もあります。そのような処理(ジョブ)は待ち行列(キュー)に登録しておき、あとから実行(非同期実行)することで、アプリ本体のレスポンスを改善できます。

標準実装されている ActionJob はジョブの登録から実行までを管理するためのモジュールです。ただし、ActionJobそれ自体は、ジョブ操作のためのインターフェースにすぎません。

実際にジョブを実行するのは、サードパーティから提供されているジョブ管理ライブラリです。たとえば、

  • Delayed Job
  • Sidekiq

などがあります。詳しくは下記参照。

ActiveJob::QueueAdapters

上記の説明は、下記の本のpp 536参照。

www.amazon.co.jp

アダプターを切り替えると、アプリのコードをそれほど修正しなくても、ジョブ管理ライブラリを変更できます。便利!

config/application.rb
# delyaed_jobの場合
config.active_job.queue_adapter = :delayed_job

# sidekiqの場合
config.active_job.queue_adapter = :sidekiq

キューに登録してあるジョブを実行するには

ジョブ管理ライブラリによって違うようなので、利用しているライブラリのGithubを確認しましょう。

delayed_jobの場合は、bundle exec rails jobs:workのようです。

github.com

deliver_now と deliver_later

Action Mailerによるメール送信にはいくつかのメソッドが用意されています。

deliver_nowは、同期的にその場でメール送信を行うメソッドです。

deliver_laterは、非同期でメールを送信します。これにより、Webのリクエスト/レスポンスサイクルの外でメールを送信できるので、ユーザーは送信完了を待つ必要がないのです。 また、deliver_latar(wait: 5.minutes)のようにすると5分後に送信するように指示もできます。いろいろなオプションがあるみたいです。

(このメソッドの違いはRUNTEQというプログラミングスクールのカリキュラムにありました、、、身についていなかった、、、oh )

ちなみにログにも、、、

[ActiveJob] Enqueued ActionMailer::DeliveryJob (Job ID: hogehogehogehogeho) to DelayedJob(mailers) with arguments: "Hogehogehoge", "notify_hogehoge", "hogehoge", 33, 44

メール送信の処理(ジョブ)が待ち行列に追加(Enqueued)されたログが表示される。たぶん。

まとめと学び

deliver_laterがメール送信を非同期処理で行うこと、待ち行列のジョブの実行の仕方を知らなかった、という2つの原因が重なって開発が止まってしまった。

ActionJobと非同期処理について理解が深まったので良しとしよう。

参考文献

非同期処理とPromise(Deferred)を背景から理解しよう - hifive

非同期処理ってどういうこと?JavaScriptで一から学ぶ - Qiita

ruby on rails - bundle exec rake jobs:work - Stack Overflow

【Rails】ActiveJob/DelayedJobを使いこなす【基本】 - Qiita

Action Mailer の基礎 - Railsガイド

Ruby on Rails 5アプリケーションプログラミング | 山田 祥寛 |本 | 通販 | Amazon

パーフェクト Ruby on Rails 【増補改訂版】 (Perfect series) | すがわらまさのり, 前島真一, 橋立友宏, 五十嵐邦明, 後藤優一 |本 | 通販 | Amazon

RUNTEQでLT会を共催して司会をやったときに気をつけてたこと

こんにちは。たわらです。

RUNTEQというプログラミングスクールの話です。

深夜のDiscordで、ある卒業生と「業務駆け出しエンジニアの気づき、でLT会しようか」みたいな話になりました。4人くらいの発表者に聴講する人が10人くらいかなーと思ってRUNTEQで募集すると、、、

発表者9人、聴講者35人くらいになりました。

授業の空き時間にヒソヒソ話すつもりだったのが、体育館で発表する、みたいになってしまいました。あわあわ。

で、司会をしました。「わりとよかったよ」と言われたので、気をつけてたことをメモにして残しておきます。

(終わったあとに言語化してるので、思い出補正がかかってます。割り引いて参考にしてください)

1 主催っぽい立場なので、やりにくい順番を自ら選ぶ

企画立案に関わってるので、あんまりみんながやりたがらない順番に発表しようと思いました。自分はトップバッター、もうひとりのRUNTEQ生にはトリをお願いしました。結局トリは飛び入り参加のひさじゅさんになりましたけど、、、

2 タイムキープはわりとしっかりやる

だらだら発表すると、終わりのない校長先生の話みたいな感じが出て、よくないと思ったので、ベルが鳴るアプリを用意しといて、発表前に全員に説明しました。何分何秒になったら1回、何分何秒になったら2回、というふうにデモしました。

使ったのはコレ。時間指定できるので便利でした。

プレゼンタイマー

プレゼンタイマー

  • Takuya Murakami
  • ユーティリティ
  • 無料
apps.apple.com

3 うなずく

発表者がいちばん嫌うのは聞き手の反応がわからない状態なので、、、

オンラインコミュニケーションでいつも心がけていることですが、発表者に認知されるかどうかはわからないですが、めちゃくちゃうなずくようにしています。軽い腹筋くらい動かします。「聞いてますよ」と相手に視覚的に伝えるためです。自分はされるとめちゃうれしいです。

「わかったら。うなずくように。オレがわかんないじゃん」と中学校の変な塾講師に言われたのを思い出します。

4 画面共有の準備が整うあいだは司会が場をつなぐ

はじめて画面共有するときに、勝手がわからないときがあります。自分がなってみればわかりますが、あの時間ってすごいストレスになります。自分の不手際を大人数に見られてるような気分になってしまいます。

なので、画面共有に多少手こずっても発表者のストレスにならないようにしたいです。聞き手の意識を少しでも発表者から話す努力をします。「ゆっくりやってください」とかは逆効果な気がしてます。発表者に意識が向いてしまうので。

なので、その前の発表の話をはさんだりします。「あれ、すごかったすねー」と。

で、画面共有の状況を率先して伝えてあげます。「いま全部のスライド見えてます。あ、1枚のスライドしか見えてないので大丈夫ですね」みたいな感じです。発表者が「いま映ってます?」と質問する不安と時間を省けます。

5 定形表現を使う

声を出して発表するタイミングがわからないと、発表者にはストレスです。なので、タイミングが明確になるようにしました。(これは途中から気づいた)

具体的には、 「発表者の準備ができたら合図してください! (合図をもらう)ではお願いします!」 と定形表現を使いました。

こうすると、聞き手も発表者も、開始のタイミングがつかめてよいかなーと思います。

6 発表者が終了したら速攻で聞き手の反応を伝える

発表者がいちばん嫌うのは聞き手の反応がわからない状態なので、、、

質疑応答に入る前に、なるべく聞き手の反応をフィードバックするようにしました。司会する自分個人の感想ではなくて、聞き手の反応です。発表者が知りたいのは司会の反応ではなくて、聞き手の反応だと思うからです。

チャットのコメントで反応が箇所を伝えるとよいと思います。「〇〇って、みんな言ってますね。〇〇って言う人もいます」みたいな。

みんな反応してますよ、ということを伝えるのですね。

7 コメントを拾う

ただコメントを拾うのではなく、コメントに反応してから、発表者に渡すのがよいかもしれません。

コメント「〇〇の箇所って△△ですか?」

司会ボク「(コメント読み上げ)あー、〇〇ってたしかに気になりますね。どうですか?」

みたいな。コメントする人も、コメントするのにちょっと勇気がいるので、内容に反応がないと、またコメントしようとする気が減ってしまいますよね。たぶん。自分ができたかどうかはわからないですが、、、

8 質問を考えておくこと

聞き手の反応がないことはめちゃくちゃつらいです。なので、司会者でいくつか質問を考えておきます。学会の発表などではよく見る光景でした。私の場合は、ですが。

なので、もし聞き手に質問がない場合は、用意した質問をします。じっくり聞いとかないといけません。当たり前!

また、難しい内容だったら、聞き手が理解しにくいだろうところを、「もういちど教えてもらってもいいですか」などと聞くのもアリです。これは使えます!

司会が質問すると、「オイラも質問しよっかな」と思ってくれる聞き手があらわれる可能性があります。

また発表をお願いしている人の場合、その人の発表の良さを最大限に引き出す質問をしましょう。

9 褒める

よかったところが必ずあります。何が新しい知識として発見だったかを発表者に伝える。 「〇〇は知らなかったです。なるほどそうなんですね」 「〇〇のたとえはめっちゃわかりやすかったです」 みたいな感じです。

10 他己紹介 or 今後話しかけやすいポイントを勝手に作る

発表に関連しているものがよいかもですが、その人固有の興味や仕事内容や技術をなるべく聞き手に伝えるようにしました。たぶん。

この人ってこんな人なんですよー、とワザトラシク正面から紹介はしませんが、、、

「普段〇〇の話をしてますよね」 「〇〇の技術はめっちゃ詳しい人ですよね」 「ハッシュログを作った遠藤さんですー」

などなどです。もちろん本人が嫌がらないことを選択します。うまい例が挙げられませんが、、、

理想としては、自分がいない場で、発表者と聞き手のひとりが会話するときに、聞き手の頭のなかで「そういえばあの発表会で、この人〇〇に興味があるって言ってたっけ、、、」となるようにしたいです。

こちらの動画は必見です! 

「自分のコメントがテロップにならなくてもカットされても、そのコメントで自分以外のふたりで笑いが生まれたなら良しとしないと」みたいなコメントがあります。

www.youtube.com

受け答えは、若林に勉強すればよいと思います!ほんとに!しびれる!

11 お酒を引っ掛けておく

ほろ酔いになっておくといいかもしれません。ビールを一本飲みながら司会しました!

あんまり褒められるようだったら、エンジニアLT会専門の司会フリーランスになります。

(了)

【Rails】findとfind_byと404エラーとnilと劇的な見せ方をされた知識って忘れそうにない、という話

こんにちは、たわらです。

Railsには似たようなクラスメソッドがあります。

findとかfind_byとか。

で、その違いは何なのかなーと勉強していて、

RUNTEQというプログラミングスクールのコミュニティのSlackにて

findfind_byって該当オブジェクトがない場合の挙動が違うんだ、、、」

と自分のTimesにつぶやいてみると、颯爽と先輩卒業生が現れて、、、

とある日

ボク「findfind_byって該当オブジェクトがない場合の挙動が違うんだ、、、」

先輩「findは例外起こす。find_bynilを返す」

ボク「(なんて速さのレスなんだ、、、)」

ボク「んー、なるほどそうなんですね、、、」

【そして次の瞬間】

先輩「ほれ」

Image from Gyazo

先輩「なっ!」

先輩「んで、ActiveRecord::RecordNotFoundRailsでは404で処理するんじゃ」

ボク「な、なるほどー!」

railsguides.jp

何かすごく頭に残る

このやりとりでfindは例外でfind_bynilを返す、そしてその例外を404エラーで補足する、ということが頭にするするっと入りました。たぶん忘れないだろうな、と思いました。

なんでかなーと考えると、、、

・びっくりしたから

チャットでのやりとりだったので、急にコンソールの画像が出てきて反射的にびっくりしました。油断してたというか。

もうひとつ、わざわざ手元で実行してスクショまで取ってくれたんだ、ありがたい、というびっくりもありました。

・実行前と実行後の結果がわかりやすい

こうやって実行前のメソッドと実行結果をさくっと見せられると思わず納得してしまいますね。findは例外でfind_bynilなんだ、と。

・アハ体験?

例外補足についてはカリキュラムで学習したのですがそれほど理解できていませんでした。なんとなーくもやもやしたカタチで頭に残っていました。

その状態で、

ActiveRecord::RecordNotFoundRailsでは404で処理するんじゃ」

と言われて頭のなかの知識の整理ができたように感じました。あ、そういうことだったのか!という喜びに似た感覚がありました。

・抽象化できた

これは未確認なので話半分ですが、

ActiveRecord::RecordNotFoundRailsでは404で処理するんじゃ」

これって「特定のフレームワークは特定のエラーをHTTPステータスコードに結びつけている」ということですよね。たぶん。

なので、別のフレームワークは別の仕方で定義したエラーを、任意のHTTPステータスに紐づけている、ということだと予想できます。

フレームワークのエラーとHTTPステータスはフレームワーク固有の結びつきをしている、っぽいと1段階 抽象的に考えることができました。

おそらくこの4つくらいが重ね合わさって、「何かすごく頭に残る」と思ったのかなーと。

最後に

誰かと技術の話をするってこういういい感じのことが起きるんですね。恥ずかしがらずにコミュニケーションするって大事です。

ちなみに、この先輩は

twitter.com

の人です。気になる方はフォローしてみてください。

(了)

コードに対する考え方がアップデートされた感

こんにちは、たわらです。

業務をはじめて二ヶ月経ちました。

そのなかで気づいたことのひとつをメモとして残しておきます。

コードに対する姿勢です。

いままで

これまでコードを理解していればよい、と考えていました。つまり、コードがどういう処理をしているのか、ということです。

ルーティングを読んで、コントローラーで処理し、モデルのスコープを動作を確認し、レンダリングするビューファイルを把握する、という一連の流れに注目してプロダクトを理解していました。

ただ、それだけでは仕事は務まらないなーと感じました。それは何かというと、、、

コードと業務フローの結びつきの理解が大切

ずばり、業務フローとコードの関係、なのかなと思っています。この関係の理解が大事です。

たとえばこんな場面、、、

事務の人 「このユーザーは〇〇処理をしたので、いったん無料会員になっています。そのため無料会員に表示されている△△を非表示にしてください」

みたいな場面です。

うまく言語化できないですが、これは、何かの機能を実装する、という類のものではないですよね。

〇〇の処理ってどうして行うのか、それによってどのテーブルのどのカラムが変更になるのか、そのユーザーのみを対象としたロジックは組めるのか、そのタスクの頻度はどれくらいなのか、個別対応するのがよいのか、システム全体で対応するのがよいのか、あるいは事務からユーザーに「表示は一過性のものなので無視してください」的なお知らせをしてもらうというコードに手を入れない対処法なのか、、、、

などなど疑問に思ってしまいます。

このあたりの業務フローの知識がないと、いけないなーと思った次第です。なので、新年の抱負のひとつは、コードと業務フローの関係を注視する、としました。

どうやったら効率よく学べるのだろうか、、、

事務の体験とかもしてみるといいかもですね、、、

進捗あったらまた記事にします。

bundle update airbrake をしたら「error: implicit declaration of function 'rb_thread_call_without_gvl' is invalid」が出た

こんにちは。

関わっている案件で「Airbrakeをアップデートしといて」と頼まれた。実行したらいろいろエラーになったけど解決したのでメモ。

特定のgem だけupdateする

bundle update gemの名前でできる。この場合、依存関係にあるgemもアップデートされる。

それを避けたい場合はbundle update --conservative gemの名前とする。

参考記事

bundle updateで特定のgemのみ更新する時に気をつけるべきポイント - 10nin blog

scrapbox.io

「bundle update --sourceで特定のgemをアップデート」は、何がダメで、「--conservative」とは何なのか - Qiita

qiita.com

今回は、「依存関係ごとたのむ」とのことなので、前者を実施。

bundle update airbrake

エラー発生 一発目

Fetching ffi 1.9.18
Installing ffi 1.9.18 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

略

current directory:
/Users/tawarakentarou/.anyenv/envs/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/ffi-1.9.18/ext/ffi_c
make "DESTDIR=" clean
current directory:
/Users/tawarakentarou/.anyenv/envs/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/ffi-1.9.18/ext/ffi_c
make "DESTDIR="
compiling AbstractMemory.c
compiling ArrayType.c
compiling Buffer.c
compiling Call.c
Call.c:355:5: error: implicit declaration of function 'rb_thread_call_without_gvl' is invalid in C99
[-Werror,-Wimplicit-function-declaration]
    rbffi_thread_blocking_region(call_blocking_function, data, (void *) -1, NULL);
    ^
./Thread.h:78:39: note: expanded from macro 'rbffi_thread_blocking_region'
# define rbffi_thread_blocking_region rb_thread_call_without_gvl
                                      ^
1 error generated.
make: *** [Call.o] Error 1
make failed, exit code 2
Gem files will remain installed in
/Users/tawarakentarou/.anyenv/envs/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/ffi-1.9.18 for
inspection.
Results logged to
/Users/tawarakentarou/.anyenv/envs/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-19/2.6.0/ffi-1.9.18/gem_make.out
An error occurred while installing ffi (1.9.18), and Bundler cannot continue.
Make sure that `gem install ffi -v '1.9.18' --source 'https://rubygems.org/'` succeeds before
bundling.

指示通りに

gem install ffi -v '1.9.18' --source 'https://rubygems.org/'

しかし、、、

エラー発生 二発目

❯ gem install ffi -v '1.9.18' --source 'https://rubygems.org/'5
Building native extensions. This could take a while...
ERROR:  Error installing ffi:
        ERROR: Failed to build gem native extension.
    current directory: /Users/tawarakentarou/.anyenv/envs/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/ffi-1.9.18/ext/ffi_c
/Users/tawarakentarou/.anyenv/envs/rbenv/versions/2.6.2/bin/ruby -I /Users/tawarakentarou/.anyenv/envs/rbenv/versions/2.6.2/lib/ruby/2.6.0 -r ./siteconf20210105-72618-ac0m5z.rb extconf.rb
checking for ffi_call() in -lffi... yes
checking for ffi_prep_closure()... yes
checking for ffi_raw_call()... yes
checking for ffi_prep_raw_closure()... yes
checking for shlwapi.h... no
checking for rb_thread_blocking_region()... no
checking for rb_thread_call_with_gvl()... yes
checking for rb_thread_call_without_gvl()... yes
checking for ffi_prep_cif_var()... yes
creating extconf.h
creating Makefile
current directory: /Users/tawarakentarou/.anyenv/envs/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/ffi-1.9.18/ext/ffi_c
make "DESTDIR=" clean
current directory: /Users/tawarakentarou/.anyenv/envs/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/ffi-1.9.18/ext/ffi_c
make "DESTDIR="
compiling AbstractMemory.c
compiling ArrayType.c
compiling Buffer.c
compiling Call.c
Call.c:355:5: error: implicit declaration of function 'rb_thread_call_without_gvl' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
    rbffi_thread_blocking_region(call_blocking_function, data, (void *) -1, NULL);
    ^
./Thread.h:78:39: note: expanded from macro 'rbffi_thread_blocking_region'
# define rbffi_thread_blocking_region rb_thread_call_without_gvl
                                      ^
1 error generated.
make: *** [Call.o] Error 1
make failed, exit code 2
Gem files will remain installed in /Users/tawarakentarou/.anyenv/envs/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/ffi-1.9.18 for inspection.
Results logged to /Users/tawarakentarou/.anyenv/envs/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-19/2.6.0/ffi-1.9.18/gem_make.out

なんでできひんのかい! と思う気持ちを抑えて、

error: implicit declaration of function 'rb_thread_call_without_gvl'

ググると、、、

オプション付けるといけるよ

同じような現象に困っている人を発見。で、回答を見ると、なかなかグッドポイントが高いし、比較的最近の回答だから信ぴょう性が高そう。

stackoverflow.com

ということで

gem install ffi -v '1.9.18' -- --with-cflags="-Wno-error=implicit-function-declaration"

上記コマンドを打つと、gem ffi をインストールできた!

その後、

bundle update airbrake

を実行。うまくいく! タスク完了!

-- --with-cflags="-Wno-error=implicit-function-declaration" ???

このオプションは何なんだ? ということでコピペしてぐぐると、、、

【puma】puma4台インストールできない問題の対処法 - Qiita

qiita.com

この記事の最下部にざっくり説明があった。

--with-cflags= はC言語で書かれたファイルをコンパイルするときに、次のオプションで実行してね、という意味らしい。

で、そのオプションが、、、

"-Wno-error=implicit-function-declaration"。 これの意味するところは、「いつもなら、特定の関数が宣言される前にエラーは出すけど、今回はエラー出さないで」とのことらしい。

詳しくは記事を参照願いたい。

そしてこの記事は、ボクが通っていたRUNTEQというプログラミングスクールの先輩だった。いつも有益なアウトプットをしているので、ぜひtwitterフォローを検討してくださいな。

(了)