【一人 Ruby Advent Calendar 2017】Ruby に Method#=== が入った!!【13日目】

一人 Ruby Advent Calendar 2017 13日目の記事になります。

と、言うことでわたしが提案してパッチを投げていた Method#=== が本体にマッジされました!!やった!!!
これにより以下のように case-whenwhen 節で Method オブジェクトを使えるようになります。

# when で使用する
def check value
    case value
    when 0.method(:<)
        "plus"
    when 0.method(:>)
        "minus"
    when 0.method(:==)
        "zero"
    end
end

p check 3   # => "plus"
p check -6  # => "minus"
p check 0   # => "zero"

やった!!!

経緯

元々以前から『Method#=== がなくてつらいぽよ〜』みたいな事を言っており『Method#=== を定義するだけの gem』なんかもつくっていました。
個人的に『外部で解決が出来るなら』別に本体になくてもいいかなーと思っている派なので特にパッチなどを書くつもりはありませんでした。
でもやっぱり『Proc#=== あるのに Method#=== がないのはおかしいよなー本体でもほしいなー』と思い Ruby 2.5 のドサクサに紛れて入れてしまおうかと急遽パッチを投げることに

Ruby のビルドとパッチの作成

Ruby のビルドやパッチを作ったことがなかったので知人のコミッターに聞いたり以下のサイトを参考にして手元で動作するようにしました。



今回は Method#===Method#call を呼び出すだけだったので実装自体は30秒ぐらいで終わりました。
あとはテストを追加したぐらいですね。
で、作成したパッチは以下のような感じです。

diff --git proc.c proc.c
index cf6e1e85c7..046c0bdf97 100644
--- proc.c
+++ proc.c
@@ -3124,6 +3124,7 @@ Init_Proc(void)
     rb_define_method(rb_cMethod, "hash", method_hash, 0);
     rb_define_method(rb_cMethod, "clone", method_clone, 0);
     rb_define_method(rb_cMethod, "call", rb_method_call, -1);
+    rb_define_method(rb_cMethod, "===", rb_method_call, -1);
     rb_define_method(rb_cMethod, "curry", rb_method_curry, -1);
     rb_define_method(rb_cMethod, "[]", rb_method_call, -1);
     rb_define_method(rb_cMethod, "arity", method_arity_m, 0);
diff --git test/ruby/test_method.rb test/ruby/test_method.rb
index 778fcdc9bc..b3d4098a1c 100644
--- test/ruby/test_method.rb
+++ test/ruby/test_method.rb
@@ -1017,4 +1017,10 @@ def test_argument_error_location
     # without trace insn
     assert_separately [], "RubyVM::InstructionSequence.compile_option = {trace_instruction: false}\n" + body
   end
+
+  def test_eqq
+    assert(Method.instance_methods(false).include? :===)
+    assert_operator(0.method(:<), :===, 5)
+    assert_not_operator(0.method(:<), :===, -5)
+  end
 end

パッチ自体はすごく短いです。
どうでもいいですが、初めて Ruby をビルドしたんですが手元のマシンだと1〜2分で終了したのですごかった…。
Vim をビルドするよりも短い…。

チケットの登録

今回はバグではなくて『新しい機能(メソッド)』の提案だったのでトラッカーは『Feature』を指定。
あとは『概要』だったり『既存の問題点』だったり『ユースケース』だったり『パッチファイル』などをまとめて『日本語』で作成しました。
実際に作成したチケットはこんな感じ。

ちなみに Rubygithub『にも』ソースコードはあるんですが、今回は github に pull request は行わないで直接パッチファイルを添付しました。

チケット登録後…

どちらかといえば難しかったのがチケット登録後。
登録したのは先月の 11/29 なんですが、スパムが速攻で返信した以外は特にレスポンスがなくてどうするべきなのか困っていました。
一応知人がコメントしてくれたんですがそれも特に反応がなく、どうアプローチするべきか全くわからず…。
まあ 2.5 リリース前で忙しいだろうからしゃーないなーと思っていたんですが、今日 Twitter愚痴っていたら Ruby コミッターの方に拾ってもらい無事に取り込んでもらいました(あと知人もプッシュしてくれたみたい?

いやー本当は今日あった DevelopersMeeting にでも乗り込んで直接聞いてみようかと思っていたんですが体調不良により死んでおり…。
なのでもうタイミングがないと思いほとんど諦めていたんですがなんとか Ruby 2.5 に間に合ってよかったよかった…。

そんな感じで Ruby コミッターの方々ありがとうございます。
Rubycase-when 楽しいのでみんなもっと使おう。

【一人 vimrc advent calendar 2017】現在のファイル名を変更する【12日目】

一人 vimrc advent calendar 2017 12日目の記事になります。
今回は個人的に使う機会が多い簡単な Ex コマンドの紹介でも。

現在開いているファイル名を変更する

さて、Vim を使っていると『現在開いているファイル名を変更したい』ということが稀によくあると思います。
そのような場合、以下のような Ex コマンドを定義しておくとサッとファイル名を変更する事ができます。

" 元ネタ:http://vim-jp.org/vim-users-jp/2009/05/27/Hack-17.html
" Rename {新しいファイル名}
command! -nargs=1 -complete=file Rename file <args> | call delete(expand('#'))

上記のようなコマンドを定義しておくことで :Rename {新しいファイル名} で現在開いているファイルを {新しいファイル名} で保存し直すことができます。
これは :file {ファイル名} で保存先名を変更し、call delete() で以前のファイルを削除する、というような処理になります。

参照

【一人 Ruby Advent Calendar 2017】Ruby の演算子について【12日目】

一人 Ruby Advent Calendar 2017 12日目の記事になります。 今回は演算子について簡単に。

Ruby演算子ってなに?

さて、プログラミング言語では +==<< などなど様々な演算子があります。
Ruby ではこれらの演算子は『メソッド』として定義されています。

# .+ という名前のメソッドを呼び出す
p 1.+ 2
# => 3

# #send 経由で呼び出したり
p 1.send(:==, 3)
# => false

# x[1] は #[] メソッドに引数 1 を渡しているので
# .[] 1 のように呼び出すことが出来る
p "homu".[] 1
# => 2

なので、これらの演算子はメソッドとして定義して使用することができます。

class X
    def initialize n
        @value = n
    end

    # + 演算子を定義
    # 右辺値を引数に受け取る
    def + other
        @value + other
    end

    # == 演算子を定義
    def == other
        @value == other
    end
end

x = X.new 42

p x + 35
# => 77

p x == 42
# => true

ポイントしては『左辺値は必ずレシーバ(自身)になる』ところですかねえ。
なので『右辺値のオブジェクトに対して演算子を定義する』みたいなことは難しいです。

単項演算子 -! なんかを定義する

-obj!obj みたいな単項演算子Ruby で再定義する場合は @+ のように定義します。

class String
    # 演算子 + @ 名でメソッドを定義する
    def -@
        to_i.-@.to_s
    end

    def !@
        empty?
    end
end

p -"1234"

# メソッド呼び出しの場合は @ を付ける
p "42".-@

p !"homu"
# => false
p !""
# => false

こんな感じで単項演算子を定義する場合は @ を付けて定義します。

再定義出来る演算子一覧

Ruby では以下の演算子を再定義することができます。

|  ^  &  <=>  ==  ===  =~  >   >=  <   <=   <<  >>
+  -  *  /    %   **   ~   +@  -@  []  []=  ` ! != !~

&&|| なんかは再定義できないことに注意してください。

まとめ

  • Ruby では殆どの演算子はメソッドとして定義されている
  • 左辺値はレシーバを指す
  • 単項演算子@! のようにして定義する


演算子の使用は用法用量を守って正しくお使いください。

【一人 Ruby Advent Calendar 2017】外部からインスタンス変数にアクセスする【11日目】

一人 Ruby Advent Calendar 2017 11日目の記事になります。
今回も昨日に引き続いて外部からクラスの内部へアクセスするネタです。

インスタンス変数

さて、Ruby では @value というような『@ + 名前』の変数を使用することで『そのオブジェクト内で共通して使用できる』インスタンス変数を定義することができます。

class X
    def initialize n
        @value = n
    end

    def to_s
        @value.to_s
    end
end

puts X.new 42

このようなインスタンス変数は通常は外部からは参照できず、インスタンス変数を書き換えたり取得したい場合は .attr_accessor などを使ってアクセッサメソッドを定義するのが一般的です。

class X
    # @value というインスタンスう変数に対する
    # X#value
    # X#value=
    # アクセッサメソッドを定義する
    attr_accessor :value

    def get
        @value
    end
end

x = X.new

# @value に対して書き換えたり
x.value = 42

# @value を取得したりする
p x.value
# => 42

p x.get
# => 42

このようにして .attr_accessor を利用することで @value のアクセッサを簡単に定義することができます。

インスタンス変数を外部から直接参照する

さて、先程は .attr_accessor でアクセッサメソッドを定義することで外部からアクセスするようにしましたが、次は直接アクセスしてみましょう。

#instance_exec#instance_eval を使う

みんな大好き #instance_exec を使用することで簡単にアクセスできます。

class X
    def initialize
        @value = -5
    end

    def get
        @value
    end
end

x = X.new

# instance_exec のブロック内で変数を参照したり
p x.instance_exec { @value }
# => -5

# 変数を書き換えたりする
x.instance_exec { @value = 42 }

p x.get
# => 42

#instance_exec を使えば Ruby でやりたい放題ですね。

#instance_variable_set#instance_variable_get を使う

#instance_variable_set#instance_variable_get を利用することができます。
これらのメソッドはその名の通り『直接インスタンス変数を参照する為』のメソッドです。

class X
    def initialize
        @value = -5
    end

    def get
        @value
    end
end

x = X.new

# 任意の名前のインスタンス変数を取得する
# @ を付けることに注意
p x.instance_variable_get(:@value)
# => -5

# 変数を書き換えたりする
x.instance_variable_set(:@value, 42)

p x.get
# => 42

このようにわざわざ #instance_exec などを利用せずにインスタンス変数にアクセスするための専用のメソッドが用意されています。

まとめ


Rubyインスタンス変数も割とガバガバなので使用する場合は注意しましょう。

【一人 vimrc advent calendar 2017】filetype 名を簡略化する【11日目】

一人 vimrc advent calendar 2017 11日目の記事になります。
今回は頻繁に手入力するコマンドを簡略化するというお話です。

Vimfiletype を設定する

Vim で雑なコードを書く場合に無名バッファを使うことがあります。
そういう場合は新しいバッファを開いて

:set filetype=javascript

コマンドラインで入力することが多いんですが、毎回 javascript と手打ちするのは手間ですね。
このような場合、簡略化する手段はいくつかあるんですが、今回は autocmd を利用して filetype 名を短くしてみようと思います。

autocmd FileType を利用する

autocmd FileType を利用することで任意の filetype に処理をフックする事ができます。
ですので『簡略化した名前』の filetype 名をフックすることで次のように filetype を設定することができます。

augroup vimrc
    autocmd!
   " set filetype=xxx した場合に任意の filetype を設定する
    autocmd FileType js set filetype=javascript
    autocmd FileType md  set filetype=markdown
    autocmd FileType py set filetype=python
augroup END

上記のようなスクリプトを記述することで、
1. :set filetype=md する
2. autocmd FileType md が呼ばれる
3. autocmd 内で set filetype=markdown が呼ばれる
4. 最終的に filetype=markdown になる

というような流れになります。
また、set filetype=xxxset ft=xxx と短く書くことができるので、最終的には

:set ft=js

みたいに書くことができます。
これでだいぶ短くなりましたね。
ちなみに 'filetype' は標準でバッファローカルなので :setlocal する必要はありません。

まとめ

  • autocmd FileType で任意の filetype=xxx に処理をフック出来る
  • 手入力するのが手間な場合はスクリプトで簡略化する
  • set ft=xxx すら手間な場合は Ex コマンドを定義してしまうのもあり


このような場合に簡略化する手段はいくつかあるんですが、いずれにしろ『手間』というのはどんどんなくしていくとよいですね。

【一人 vimrc advent calendar 2017】タブ文字を視覚化する【10日目】

追記


全く持ってそのとおりなので追記しておきました。


一人 vimrc advent calendar 2017 10日目の記事になります。
さて、コードを書いたり読んでいる時にインデントが『タブ文字』なのか『空白文字』なのかを気にすることがあると思います。
そのような場合、'listchars' オプションで『タブ文字』を視覚化する事ができます。

'listchars' でタブ文字を表示するようにする

例えば、タブ幅4の場合にタブ文字を ^--- と表記したい場合は次のように設定することができます。

" set listchars の設定が反映されるように ON にする
set list

" tab でタブ文字に対する設定を行う
" 1文字目がタブ文字の先頭になり
" 2文字目がタブ文字の1文字目以降に置き換わる
set listchars=tab:^-

また、タブ文字以外にも『空白文字』や『行末』に表示される文字なんかも指定することができます。

" タブ文字:"^---"
" 空白文字:"_"
" 行末文字:"_"
set listchars=tab:^-,space:_,eol:\

『タブ文字』や『空白文字』が入り乱れたコードを防ぐためにもタブ文字は視覚化しておくと便利です。

【一人 Ruby Advent Calendar 2017】private メソッドを呼びだそう【10日目】

一人 Ruby Advent Calendar 2017 10日目の記事になります。
さて、今回は Rubyprivate についていろいろ

Ruby で private メソッドを定義する

Ruby では private(という名前のメソッド)を使用することでメソッドを private として定義することができます。

class X
    def homu
        "homu"
    end
    # 任意のメソッドを private にする
    private :homu

    # 引数がなかった場合は以降に定義されるメソッドを private メソッドとして定義する
    private
    def mami
        # メソッド内であれば呼び出すことが出来る
        homu + mado
    end

    def mado
        "mado"
    end
end

x = X.new

# private メソッドは呼び出せない
# Error: private method `homu' called for #<X:0x00000000025cc1a8> (NoMethodError)
p x.homu

こんな感じでクラス外からはメソッドを呼び出すことができません。

private メソッドを呼び出す

さてさて、private メソッドを定義することでクラス外から呼び出せない…と、思いきや実はクラス外からも簡単に呼び出すことができます。

#send を使う

class X
    private
    def homu
        "homu"
    end
end

x = X.new

# send だと任意のメソッドで呼び出すことが出来る
p x.send :homu
# => "homu"

#instance_exec#instance_eval を使う

class X
    private
    def homu
        "homu"
    end
end

x = X.new

p x.instance_eval {
    # ブロック内で直接メソッド呼び出す
    homu
}
# => "homu"

特異メソッド経由で呼び出す

class X
    private
    def homu
        "homu"
    end
end

x = X.new

# メソッドを再定義して
def x.homu
    # 元のメソッドを呼び出す
    super()
end
p x.homu

どういうこと?

これは Rubyprivate の仕様がx.func のように『x. をつけて呼び出せないようにする』となっているためです。
なので

x = X.new
# インスタンスオブジェクトからメソッド呼び出す
x.func

のように通常はインスタンスオブジェクト経由で呼び出す事ができません。

しかし、逆にいえば x.func のような形式ではなくて

# #send 経由で呼び出す
x.send :func

のように #send 経由でメソッド呼び出したり、

# instance_eval 内ではコンテキストが x になるので
# x. を付ける必要なく func を呼び出せる
x.instance_eval { func }

みたいに #instance_exec を使うことで x. を付けることなくメソッドを呼び出す事ができます。

ちなみに次のように self. を付けて呼び出した場合はエラーになります。

class X
    def homu
        # OK
        homu

        # Error: private method `mado' called for #<X:0x0000000001934670> (NoMethodError)
        self.homu
    end

    private
    def mado
        "mado"
    end
end

まとめ

  • private メソッドにメソッド名を渡すとそのメソッドが private メソッドになる
  • private メソッドに引数を渡さなかった場合は以降に定義されるメソッドが private になる
  • private メソッドは x.func のように x. をつけて呼び出せない
  • x. をつけなければ呼び出せるので簡単に外部から呼び出すことが出来る


このように Ruby では『private メソッドが外部から呼び出せない』とは限らないので広い意味での『private メソッド』を定義したい場合は工夫が必要になってきます。