Ruby の private セッターメソッドは self. をつけても呼び出すことが出来る
さて、 Ruby の private メソッドは通常レシーバを付けて呼び出す事が出来ません。
class User private def age 42 end end user = User.new # NG : private method `age' called for #<User:0x0000562111c42218> (NoMethodError) p user.age
これは self.
を付けても同じです。
class User def output # OK p age # NG : private method `age' called for #<User:0x00005606cceabb80> (NoMethodError) p self.age end private def age 42 end end user = User.new user.output
セッターメソッドの場合は self.
を付けてもメソッドを呼び出すことが出来る
例外的に #age=
のようなセッターメソッドの場合は private メソッドでも self.
を付けて呼び出すことが出来ます。
class User def set # これだとローカル変数に対して代入される age = 42 # OK : private メソッドだけど self. を付けて呼び出せる self.age = 42 end private def age= value @age end end user = User.new user.set
代入式の場合、レシーバを付けないとローカル変数が定義されてしまうのでこれだけ例外的に呼び出す事が出来るようになっているんですかね。
Ruby 2.7 で `Enumerable#tally` というメソッドが追加される
Ruby 2.7 で Enumerable#tally
というメソッドが追加されます。
あまり聞き慣れない単語のメソッドですが、これは『同じ要素の数を Hash で返す』というメソッドになります。
pp [1, 1, 2, 2, 2, 3, 3, 4].tally # => {1=>2, 2=>3, 3=>2, 4=>1} pp ["homu", "homu", "mami", "mado", "mado", "mado"].tally # => {"homu"=>2, "mami"=>1, "mado"=>3}
https://wandbox.org/permlink/3nAez0P6CDys749b
重複する要素をキーとして、その要素数を値とする Hash
を返します。
配列の要素の数を数えたいことは稀によくあるので、そういう場合に一発で取得できるのは便利そうですね。
また、機能としては Enumerable#group_by
と似ていますが、 #tally
はブロックを受け取らないことに注意してください。
ちなみに tally
という単語は、日本語で数を数える時に使う 正
の字と同じような意味合いがあるらしいです。
Ruby のトップレベルメソッドって結局なんなの
以下のような質問があったのでちょっと解説。
@pink_bangbi
— あつき (@atsuki09130) 2019年2月18日
僕はselfの概念がつかめておらず、バンビさんが以前書かれた記事で4つ目と5つ目(■)の項目の意味が理解できませんでした。 暇な時で構いませんので解説をいただきたいです。 pic.twitter.com/ZgX3UQ6bjj
トップレベルメソッドを理解するためには、以下の3つの Ruby の機能について理解する必要があります。
self
- トップレベル
Object
クラス
self
ってなに
まず、トップレベルメソッドを理解する前に『 self
とは何か』を知る必要があります。
self
とはメソッド内で『メソッドを呼び出したレシーバ』を参照するキーワードになります。
また、レシーバとは『メソッド呼び出しの左辺のオブジェクト』を指します。
class X def foo "foo" end def hoge # meth を呼び出したレシーバを参照する pp self # => #<X:0x00005627e8a8a050> # レシーバ、つまり x の foo メソッドを呼び出す pp self.foo # => "foo" end end x = X.new # メソッドを呼び出す際に左辺にあるオブジェクトの事を『レシーバ』という # この場合は x オブジェクトがレシーバに該当する x.hoge
また、レシーバをつけずにメソッドを呼び出した場合は暗黙的に『self
をレシーバとして』メソッドを呼び出します。
class X def foo "foo" end def hoge # レシーバがなければ self のメソッドを呼び出す # self.foo と等価 pp foo # => "foo" end end
トップレベルって?
そもそも『Ruby のトップレベルって何?』っていう話なんですが、Ruby では『class
や module
の中で定義していないメソッド』がトップレベルメソッドに該当します。
# class や module 内で定義していないメソッド def topleve_method "topleve_method" end class X # これは X のインスタンスメソッドなのでトップレベルではない def meth end end
また、トップレベルでも self
は存在し、トップレベルで self
を呼び出した場合、 main
という名前のオブジェクトを返します。
pp self # => main
main
はトップレベルで定義されている特別なオブジェクトになります。
トップレベルでメソッドを定義するとどうなるの?
トップレベルでメソッドを定義した場合、暗黙的に『Object
クラスの private
メソッド』として定義されます。
どういうことかというと
def hoge "hoge" end
というトップレベルメソッドは、
class Object private def hoge "hoge" end end
という定義と等価になります。
ちなみに Ruby における private
とは『レシーバをつけて呼び出すことが出来ないメソッド』になります。
class X # hoge を private メソッドとして定義する private def hoge end def foo # OK : レシーバをつけずに呼び出す hoge # NG : self もつけて呼び出すことは出いない self.hoge end end x = X.new # NG : レシーバをつけて呼び出すことは出来ない x.hoge
Object
クラスって何
Object
クラスは『全てのクラス』が継承しているクラスになります。
例えば String
や Array
といった組み込みクラスやユーザが定義したクラスも暗黙的に Object
クラスを継承しています。
# Module#< で任意のクラスを継承しているかチェックする # 組み込みクラスは Object を継承している p String < Object p Array < Object # ユーザが定義したクラスも暗黙的に Object を継承している class X end p X < Object
つまり 『トップレベル定義したメソッド= Object
クラスの private
メソッド』は『 Object
を継承しているクラス=全てのクラス』の『private
メソッド』として参照できます。
# Object の private メソッドとして定義される def hoge "hoge" end class X def foo # Object は親クラスなので # 親クラスの private メソッドが呼べる hoge end end x = X.new p x.foo # => "hoge" # NG : private メソッドなので直接呼べない # p "foo".hoge # OK : send だと private メソッドも明示的に呼べる p "foo".send(:hoge) # => "hoge"
結局トップレベルでメソッドって?
- トップレベルでメソッドを定義すると Object のメソッドとして定義される
- Object は全てのクラスが継承しているのでどのクラスからでも呼び出せる
- 当然トップレベルの
self
(=main
)もObject
クラスを継承しているのでトップレベルでも呼び出せる main
とトップレベルメソッドは特に関連はない
トップレベルの self
が main
っていう特別なオブジェクトなので、どうしてもそっちの方に意識が向くんですが、トップレベルの self
とトップレベルのメソッドは特に関連性はありません。
まずは、『トップレベルメソッド』というのが先にあり、『main
』っていうのが後にあります。
ちなみに『トップレベルメソッド』の他に『トップレベル定数』というものがありトップレベル定数はトップレベルメソッドの100000万倍ぐらいややこしい仕様なので知らないほうがいいです。
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 とかでアウトプット出来るような年にしたいですねえ。
それではよいお年をー