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 のトップレベルメソッドって結局なんなの

以下のような質問があったのでちょっと解説。

トップレベルメソッドを理解するためには、以下の3つの Ruby の機能について理解する必要があります。

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 では『classmodule の中で定義していないメソッド』がトップレベルメソッドに該当します。

# 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 クラスは『全てのクラス』が継承しているクラスになります。
例えば StringArray といった組み込みクラスやユーザが定義したクラスも暗黙的に 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 とトップレベルメソッドは特に関連はない

トップレベルの selfmain っていう特別なオブジェクトなので、どうしてもそっちの方に意識が向くんですが、トップレベルの 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 を複数定義すると…

Railsdefault_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 を使い始めましたけど便利ですね。 みんなもどんどん使いましょう(白目。

参照

https://api.rubyonrails.org/classes/ActiveRecord/Scoping/Default/ClassMethods.html#method-i-default_scope

Ruby でメソッド呼び出しの末尾に空の , があってもエラーにならない

来年はいっぱいアウトプットするぞ〜〜〜といいながら全然やってませんがわたしは元気です。
ところで Ruby って以下のコードみたいに meth(1, 2, ) っていう呼び出しはエラーにならないんですね。
普通に動いてて驚きました。

def meth a, b
    a + b
end

p meth(1, 2, )
# => 3

Ruby には知らないことが多いなあ。

2018年振り返り

2018年振り返り ということで今年やった事振り返りでも。
今年も今日で最後。

Ruby にパッチいっぱい投げた

Ruby にパッチいっぱい投げました。
その結果、Ruby 2.6 ではパッチが4つ取り込まれました、わーい。
どんなパッチを投げたのかはこちらを参照。

その他、bugs.ruby に投げたのは以下のようになります。

Hash#===Array#=== はほしいんじゃがなあ。
しばらくは Ruby にお世話になると思うので、来年も思いついたらパッチ投げたり修正できるバグ直したりとか貢献していきたいし、使い勝手をよくしていきたい。

Rails

生きるために Rails を始めました。
Rails 自体は全く触ったことがないわけではないけどほぼ何も知識がない状態で1年近く Rails やってました。
1年近く Rails やってわかってきたかというとぶっちゃけまだまだわからないことの方が多い。
けど、Rails の調べ方や書き方、コンセプトなんかは最近やっとわかってきたようには感じる。
あと最近は ActiveRecord のコード読みまくっているのでだいたいわかった…わけはなく、むしろなにもわからない…ActiveRecord なにもわからない…。
Rails に限っていえば偏った知識ばかり学んでいるので来年はもうちょいそのあたりアウトプットしていきたい。

rubygems

rubygems に関してはいろいろとつくりたいものはあるんだけど、あんまりつくれなかった感じ。
それでも今年前半はいろいろとつくっていた。

binding-debug は割と使う機会が多いのでもうちょい使い勝手よくしたいんですけどねえ。
元々 Ruby でおもしろライブラリつくるのが中心だったので来年はもうちょい役に立たないおもしろライブラリつくっていきたい。
iolite の v2 も開発せな…。

勉強会にがんばっていった

今年後半はあまり行けなかったんですが、前半はちょいちょい勉強会に行って LT してました。
後半に勉強会にあんまり行かなかった理由として『勉強会に行くなら LT をする』という目的をキメているんですが、あんまり LT 出来るような勉強会がなかったのもありましたね。
そもそも明確な目的とかがないとあんまり外に出たり人と会いたくないのはありますし…。
まあ、あとは単純に忙しくてあんまり余裕がなかったのもありますが。
地域.rb とかはテーマとかが合えばまた 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 に貢献したい
  • Ruby 以外も書きたい

来年はもうちょいブログや LT とかでアウトプット出来るような年にしたいですねえ。
それではよいお年をー