Rails の touch 時に処理をフックする
任意のレコードの updated_at
のみを更新する際に ActiveRecord の #touch
を使うことはあると思います。
class User < ActiveRecord::Base end user = User.create(name: "Homu") pp user.updated_at.iso8601(10) # => "2019-02-18T12:36:37.2315494900Z" # updated_at のみを更新する user.touch pp user.updated_at.iso8601(10) # => "2019-02-18T12:36:37.2315494900Z"
#touch
時に処理をフックする
#touch
がやっていることは『updated_at
の更新』なので after_save
等でフックしたくなるんですが残念ながら #touch
時には after_save
は呼ばれません。
class User < ActiveRecord::Base # #touch 時に after_save は呼ばれない after_save { pp "after_save" } end
#touch
時に処理をフックする場合は after_touch
を使用します。
class User < ActiveRecord::Base # touch で更新した後に after_touch が呼ばれる after_touch { pp "after_save" pp updated_at.iso8601(10) } end user = User.create(name: "Homu") pp user.updated_at.iso8601(10) user.touch pp user.updated_at.iso8601(10) # output: # "2019-02-18T12:40:49.6699328380Z" # "after_save" # "2019-02-18T12:40:49.6741588740Z" # "2019-02-18T12:40:49.6741588740Z"
#touch
がやっていることは更新なので after_save
が呼ばれて当然と思っていてハマりました。
#touch
時には、
after_touch
after_commit
after_rollback
のみが呼ばれます。
Rails で default_scope を複数定義すると…
Rails で default_scope
を複数定義した場合、『両方の default_scope
』が適用されます。
class Article < ActiveRecord::Base default_scope { where(published: true) } default_scope { where(rating: 'G') } end Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
最近 default_scope
を使い始めましたけど便利ですね。
みんなもどんどん使いましょう(白目。
参照
2018年振り返り
2018年振り返り
ということで今年やった事振り返りでも。
今年も今日で最後。
Ruby にパッチいっぱい投げた
Ruby にパッチいっぱい投げました。
その結果、Ruby 2.6 ではパッチが4つ取り込まれました、わーい。
どんなパッチを投げたのかはこちらを参照。
その他、bugs.ruby に投げたのは以下のようになります。
- Bug #15427: Assertion Failed: vm_method.c:858:prepare_callable_method_entry:callable_method_entry_p(cme)
- Feature #15374: Proposal: Enable refinements to
#method_missing
- Feature #15373: Proposal: Enable refinements to
#method
and#instance_method
- Feature #15327: Proposal: Enable refinements to
#respond_to?
- Feature #15326: Proposal: Enable refinements to
#public_send
- Feature #15286: Proposal: Add Kernel.#expand(*args)
- Bug #15114: Ruby で定義したメソッドに
&:hoge
を渡しても refinements が有効にならない - Feature #14973: Proposal of percent literal to expand Hash
- Feature #14916: Proposal to add Array#===
- Feature #14869: Proposal to add Hash#===
- Feature #14417: String#sub / String#gsub に『キーが Symbol の Hash』を渡せるようにする提案
Hash#===
と Array#===
はほしいんじゃがなあ。
しばらくは Ruby にお世話になると思うので、来年も思いついたらパッチ投げたり修正できるバグ直したりとか貢献していきたいし、使い勝手をよくしていきたい。
Rails
生きるために Rails を始めました。
Rails 自体は全く触ったことがないわけではないけどほぼ何も知識がない状態で1年近く Rails やってました。
1年近く Rails やってわかってきたかというとぶっちゃけまだまだわからないことの方が多い。
けど、Rails の調べ方や書き方、コンセプトなんかは最近やっとわかってきたようには感じる。
あと最近は ActiveRecord のコード読みまくっているのでだいたいわかった…わけはなく、むしろなにもわからない…ActiveRecord なにもわからない…。
Rails に限っていえば偏った知識ばかり学んでいるので来年はもうちょいそのあたりアウトプットしていきたい。
rubygems
rubygems に関してはいろいろとつくりたいものはあるんだけど、あんまりつくれなかった感じ。
それでも今年前半はいろいろとつくっていた。
- toplevel
- トップレベルでローカルメソッドみたいなのを定義するやつ
- binding-debug
- 変数名 + 値でデバッグ出力する
- binding-expand
Symbol
からその名前のローカル変数をHash
に展開する
binding-debug
は割と使う機会が多いのでもうちょい使い勝手よくしたいんですけどねえ。
元々 Ruby でおもしろライブラリつくるのが中心だったので来年はもうちょい役に立たないおもしろライブラリつくっていきたい。
iolite の v2 も開発せな…。
勉強会にがんばっていった
今年後半はあまり行けなかったんですが、前半はちょいちょい勉強会に行って LT してました。
後半に勉強会にあんまり行かなかった理由として『勉強会に行くなら LT をする』という目的をキメているんですが、あんまり LT 出来るような勉強会がなかったのもありましたね。
そもそも明確な目的とかがないとあんまり外に出たり人と会いたくないのはありますし…。
まあ、あとは単純に忙しくてあんまり余裕がなかったのもありますが。
地域.rb とかはテーマとかが合えばまた LT とかしに行きたいですねえ。
以下、今年つくったスライド
- Ruby のトップレベルについておさらい 表参道.rb #30
- LINE でお天気bot をつくった話 表参道.rb #31
- 9年間ブログを書き続けて得たもの 第8回 学生エンジニア限定LT大会!!!
- C++er が Rust をやってみた Shinjuku.rb #58 Rust
- RSpec x Vim 表参道.rb #32
- 最近の C++ 事情
- reveal.js + markdown でスライドをつくろう!! 第9回 学生エンジニア限定LT大会!!!
- 受付ちゃん(仮)をつくった 第11回 学生エンジニア限定LT大会!!!
- Ruboty で Discord-Bot をつくろう [CombNaf x 学生LT] 第12回 学生エンジニアLT大会!!!
こうしてみるとスライド自体はかなり書いていますね。
来年も LT したいけどここまで書ける余裕はなさそう…。
買ってよかったもの
今年は開発環境回りとかで購入したものが多かったので買ってよかったものをいくつか上げます
4Kディスプレイ 27インチ + 31.5インチ
今年はディスプレイをめっちゃ買った記憶があるんですが、最終的には 4K 27インチ + 31.5インチに落ち着きました。
使うまでは 4K って言われても『画質が綺麗なんやなー』っていうぐらいの印象しかなかったんですが、実際使ってみると解像度が高いのでめっちゃ画面を広く使えてめっちゃよいです。
わたしとかは GUI マンなので何個もウィンドウを開いて開発しているので、1つのディスプレイにいくつもウィンドウが置けるのはだいぶよいですね。
ただ、Type-C が使えないのはちょっと不便かなーと最近思うようになりました。
流石にしばらくは新しいディスプレイは買わないと思いますが…。
ちなみにディスプレイはメルカリで買ったんですが、どっちも定価の1万円以上安く買えてよかったです。
あとモニタアームは HP シングルモニターアーム を使っています。
よさ過ぎて3つ買いました。
これ、Amazonベーシック モニターアーム シングルとものは同じなんですが HP の方が4000円近く安く買えるのでおすすめです。
モニタアームだと机を広く使えたり、ちょっとした微調整とかしやすいのがめっちゃいいですね。
デジタルメモ帳
タブレットでメモ取りたいなーでもその為にタブレット買うのもなーと思っていたらデジタルメモ帳っていうのを見つけたので試しに買ってみました。
実際に使ってみると書捨てのメモとかがササッと書けるので思ったよりも便利です。
脳内でまとまらないことも図にしてまとめてみたりとか。
タブレットと違って雑に扱えるのもいいですね。
ただ、10インチだとちょっと小さいので15インチぐらい大きいのがほしいんですけど、あんまり安いの売ってないんですよねえ。
関 兼次 ステーキナイフ
肉を切る時に便利。
来年目標
来年はもうちょいブログや LT とかでアウトプット出来るような年にしたいですねえ。
それではよいお年をー
Ruby でワンライナーでズンドコキヨシ
元ネタ
Javaの講義、試験が「自作関数を作り記述しなさい」って問題だったから
— てくも (@kumiromilk) 2016年3月9日
「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ!」って出力した後終了って関数作ったら満点で単位貰ってた
元ネタが2年以上前でめちゃくちゃ今更なんですが、やってみた。
脳内で考えたコードがワンライナーでそのまま動作したのでちょっとうれしかった
Ruby でズンドコキヨシ
puts (1..).lazy.map { %w(ズン ドコ).sample }.each_cons(5).map(&method(:p)).find(&%w(ズン ズン ズン ズン ドコ).method(:==)).join puts "キ・ヨ・シ!" # => # ["ズン", "ドコ", "ズン", "ドコ", "ドコ"] # ["ドコ", "ズン", "ドコ", "ドコ", "ズン"] # ["ズン", "ドコ", "ドコ", "ズン", "ズン"] # ["ドコ", "ドコ", "ズン", "ズン", "ドコ"] # ["ドコ", "ズン", "ズン", "ドコ", "ズン"] # ["ズン", "ズン", "ドコ", "ズン", "ズン"] # ["ズン", "ドコ", "ズン", "ズン", "ドコ"] # ["ドコ", "ズン", "ズン", "ドコ", "ズン"] # ["ズン", "ズン", "ドコ", "ズン", "ズン"] # ["ズン", "ドコ", "ズン", "ズン", "ドコ"] # ["ドコ", "ズン", "ズン", "ドコ", "ドコ"] # ["ズン", "ズン", "ドコ", "ドコ", "ズン"] # ["ズン", "ドコ", "ドコ", "ズン", "ズン"] # ["ドコ", "ドコ", "ズン", "ズン", "ズン"] # ["ドコ", "ズン", "ズン", "ズン", "ズン"] # ["ズン", "ズン", "ズン", "ズン", "ドコ"] # ズンズンズンズンドコ # キ・ヨ・シ!
ワンライナーで書いているのでちょっと複雑かもしれませんが、やっていることは至極単純で
(1..) # Ruby 2.6 から入った (1..) で無限配列を生成 .lazy # 遅延処理するようにする .map { %w(ズン ドコ).sample } # ランダムな "ズン" "ドコ" な配列に変換 .each_cons(5) # 無限リストを5個ずつの配列に分割 .map(&method(:p)) # この時点で一旦出力 .find(&%w(ズン ズン ズン ズン ドコ).method(:==)) # ["ズン", "ズン", "ズン", "ズン", "ドコ"] の組み合わせを探す .join # 結果を結合
無限リスト + #lazy
を使って遅延処理しつつ、#each_cons(5)
で分割して ["ズン", "ズン", "ズン", "ズン", "ドコ"]
という並びがあれば終了する、っていう処理です。
やってることは割と力技。
ちなみに map(&%w(ズン ドコ).method(:sample))
と書きたかったんですが、#sample
が引数を受け取ってしまうのでダメでした。
Ruby 2.6 に投げたパッチが取り込まれた話
先日 Ruby 2.6 がリリースされましたが、それにわたしが投げたパッチが4つ含まれています。
と、いうことで具体的にどんなパッチを投げてどんな機能が取り込まれたのかを簡単に書いてみようかと
Refinements で定義した #to_proc が &hoge 時に呼ばれないのを緩和する提案
表題そのままなんですが、次のように Refinements で to_proc
を定義した場合、&hoge
で to_proc
が呼ばれなかったのを呼ばれるようにしたパッチです。
using Module.new { # Refinements で to_proc を定義 refine String do def to_proc proc { |it| it.send self } end end } p "upcase".to_proc.call "homu" # => HOMU # "upcase".to_proc が呼ばれるようになった p %w(homu mami mado).map &"upcase" # => ["HOMU", "MAMI", "MADO"]
このパッチを投げたのは Ruby 2.5 のリリース直前で、ドサクサに紛れて取り込まれないかなーという気持ちで投げました。
まあ流石に直前だと無理だったので Ruby 2.5 には入りませんでしたが、パッチ自体は特に問題なく取り込まれて Ruby 2.6 で正式に使えるようになりました。
ここから怒涛の Refinements パッチが始まります。
Proposal: Enable refinements to #public_send
先程の to_proc
のパッチが取り込まれてからだいぶ時間が経って、11月取り込まれたパッチになります。
前回からだいぶ間が空いているんですが、この間にもいくつかパッチを投げており…。
matz に『日本語よりも英語で上げたほうがいいよー』とツッコまれたのでこのあたりからがんばって英語でパッチを投げるようにしました。
パッチも diff
ファイルを直接添付していたんですが、CI を回したかったので github 経由で pull request を投げるようにしています。
パッチ自体は以下のように『public_send
でも Refinements を有効にする』と言うような内容です。
using Module.new { # Refinements でメソッドを定義 refine String do def twice self + self end end } # public_send 経由でもメソッドが呼ばれるようになった p "homu".public_send(:twice) # => "hogehoge"
まあ #send
も Refinements が有効になっているから #public_send
も有効になってもいいよねーという気持ちで提案しました。
提案したものの(Refinements 的な意味で)議論されて取り込まれるまでに時間がかかるかなー、と心配していたんですが割とあっさり入ったのでちょっと面食らいました。
感触としては Ruby の方針としてはこのあたりはそこまで厳密に Refinements を制限する、という感じではなさそうな感じなんですかね。
Proposal: Enable refinements to #respond_to?
これも先程の #public_send
と同じで『#public_send
で Refinements を有効にする』というパッチになります。
using Module.new { # Refinements でメソッドを定義 refine String do def twice self + self end end } p "homu".respond_to?(:twice) # => true
こちらも #public_send
と同じタイミングで取り込まれました。
Ruby で定義したメソッドに
&:hogeを渡しても refinements が有効にならない
これは今回投げた中では一番大変なパッチでした。
このパッチを投げた経緯はこちらを参照してもらうと詳しく書いてあります。
パッチ自体は9月ぐらいに書いてすぐに投げたんですが、その後特に進展がなく、先月頭ぐらいにバグ認定されて、今月の頭に取り込まれたんですが、テストで落ちてたみたいで一旦 revert されて…という経緯があります。
Ruby 2.6 に絶対に入れたかったのでテストで落ちていた箇所を直してもう一度パッチを投げたんですが、そしたら別の場所でテストが失敗してしまったらしくて…。
けど、原因を調べてみたら実際はこのパッチとは全然関係ない箇所で、しょうがないのでこっちで原因を調べてみたら実は Ruby のバグを踏んでいてそれを報告して…。
というのがここ1〜2週間のハイライトになります。
まさか trunk で Ruby のバグを踏んでいるとは思わなくて実はかなり悩んでいました。
まあ結果的にこの不具合が修正されたので Ruby 2.6 ではいい感じに Refinements が使えるようになると思います。
using Module.new { # Refinements でメソッドを定義 refine String do def twice self + self end end } def meth &block block.call "homu" end # Ruby で定義したメソッドのブロック引数でも有効になった p meth &:twice
これが今まで動かなかったんですよねー。
何が嬉しいかって ActiveRecord のリレーションで定義されている map
のブロック引数とかでも Refinements が有効になったのが大きいかなーと思います。
ってか、それで使えなくて困っていたのでパッチ書いて投げました。
所感
と、言う感じで Ruby 2.6 で取り込まれたパッチをいくつか紹介しました。
改めて見ると全部 Refinements のパッチですね、これ。
Refinements はもっと使い勝手よくしたいんですよねえ。
ちなみに今回取り込まれたパッチは4つでしたが、実際にはこの倍近くのパッチを投げています。
本当は #method
と #method_missing
で Refinements が有効になるパッチも投げていたんですが、残念ながら Ruby 2.6 には間に合いませんでした。
このあたりが Ruby 2.7 で取り込まれるとだいぶ Refinements が使いやすくなるかなーと思います。
Ruby 2.7 で入るといいですねえ。
そろそろ Ruby のパッチを書くのにも慣れてきたので、何か思いついたらまたパッチを投げていきたいですねー。
パッチを書くよりも取り込まれるまでが大変ですが…。
今日は Ruby 2.6 がリリースされる日!!
今日は年に一度の Ruby がリリースされる日ですね!!!
クリスマス?知らないですねえ…。
と、言うことで Ruby 2.6 が無事にリリースされました。やったー!
そんなわけで気になった機能をいくつか上げてみようかと思います。
ちなみに Ruby 2.6 にパッチを10個ぐらい投げた結果 4つ取り込まれました。わーい。
Kernel#then
が Kernel#yield_self
のエイリアスとして追加
Kernel#yield_self
のエイリアスとして Kernel#then
が新しく追加されました。
yield_self
が then
と短く書けるやったー、と思う一方 then
って名前はどうなんですかね…という思いもあり…。
まあ文字数が半分以下になるのは喜ばしいこと。
Enumerable#filter
Enumerable#filter!
が追加
Enumerable#select
のエイリアスとして Enumerable#filter
が追加されました。
これで同機能の Enumerable
の絞り込みメソッドが
Enumerable#select
Enumerable#find_all
Enumerable#filter
の3つに増えました。
知らない人から見るとますます混乱しそうな気もしますが…。
Ruby をやり始めた頃は『なんで filter
がないんだよ』と思っていた思い出。
Proc#<<
、Proc#>>
が追加
Proc
にはいわゆる関数合成を行う演算子が新しく追加されました。
f = proc{|x| x + 2} g = proc{|x| x * 3} p (f << g).call(3) # -> 11; identical to f(g(3)) p (f >> g).call(3) # -> 15; identical to g(f(3))
f << g
は f(g())
になり、f >> g
は g(f())
になります。
また、Method
にも同等の演算子が追加されています。
f = proc{|x| x + 2} g = proc{|x| x * 3} p (1.method(:+) << 2.method(:*)).call(3) # => 7 (1 + (2 * 3)) p (1.method(:+) >> 2.method(:*)).call(3) # => 8 ((1 + 3) * 2) # Proc と組み合わせたり p (1.method(:+) << g).call(3) # => 10 ((3 * 3) + 1) p (f >> 2.method(:+)).call(3) # => 7 ((3 + 2) + 2)
Proc#<<
、Proc#>>
ともに『#call
が定義されている』オブジェクトを渡すことが出来ます。
class G def call x x * 3 end end f = proc{|x| x + 2} g = G.new p (f << g).call(3) # => 11 f(g(3))
所感
と、いう感じで少しですが Ruby 2.6 の機能を取り上げてみました。
他には終端なし Range が追加されたり #to_h
にブロック引数が追加されたりなどなど。
気になる方はリリースノートを見てみるといいともいます。
さて、Ruby 2.6 がリリースされたということは Ruby 2.7 の開発が始まったことになります。
Ruby 2.6 ではパッチを10個投げた結果 4つ取り込まれました。
Ruby 2.7 でもまだまだ追加したい機能がたくさんあるので、Ruby 2.7 ではもう少しがんばってパッチを投げていきたいと思います。
あと直せる範囲であればバグ修正とかも手伝えるといいですねえ…。
そんな感じで来年も Ruby 2.7 を楽しみにしています!!