Ruby
AdventCalendar
RubyDay 10

Rubyやってます、(`・ω・´)キリッ という為に押さえときたいテクニック

More than 1 year has passed since last update.

最近、スタートアップ系や新規開発でRuby(Ruby on Rails)を採用するところも増えてきており、Rubyやってる人がちらほら増えてきた感があるのですが、たま〜にRubyやってて何故それ知らないんだという事もたまにあり、Rubyやってます(`・ω・´)キリッ とそれでよく言えるなと呆れる事もありました。。。

そこで、少なくともこれは押さえておいて欲しいテクニックを紹介したいと思います。


クラスメソッド、インスタンスメソッド ★★★★★

これはテクニックではないですが、Rubyで最初に躓いたり、混乱する元の一つなので、Rubyをやっている以上、ちゃんと理解しておくべきことであると思ってます

* 定義したClassから見た表

名称
説明

インスタンスメソッド
クラス内で定義されたメソッドのこと

インスタンス変数
インスタンス内で参照可能な変数

クラスメソッド
クラスに対して呼び出すことの出来るメソッド(インスタンス生成せずにアクセス可能)

class Sample

# クラスメソッド
def self.hoge
end

def initialize
@foo = "foo" # インスタンス変数
end

# インスタンスメソッド
def foo
@foo
end

end


Enumerable ★★★★★

公式doc

多分C#をやっている人以外は、まずなんて読むのか、から始まると思います。

"エニュメラブル"と読むらしいですが、これはRubyの繰り返し(each)を行うオブジェクトに対して使用する事ができます。これらは組み込みオブジェクトなので、普通にeach内でゴニョゴニョやるよりは、パフォーマンスがいいので、知っておいて損はないです。


使い分け


  1. もとの配列の各要素を変換して、変換後の新しい配列を作成する -> map(collect)

  2. 要素が特定の条件を満たしているか -> all?, none?, any?, one?

  3. 正規表現にマッチする要素を取得する -> grep

  4. 要素に対して演算した結果を取得する -> reduce(inject)

  5. 要素のグルーピング -> group_by

  6. 要素の最小値や最大種を取得する -> max, min, minmax

  7. 要素をソートした結果を取得する -> sort, sort_by

  8. 要素を特定の条件で検索した結果を取得する -> find, select, find_all

もちろんHashに対してもeachが使えるオブジェクトに使用可能です。以前Blogで投稿した内容のほうがイメージし易いので、貼っておきます

rubyでEnumerableを用いたSQLライクな操作 - のんびりSEの議事録


Proc/lambda ★★★☆☆


Proc

ブロックを引数として渡す場合に利用する

@num = 0

counter = Proc.new { @num += 1 }
counter.call # 1
counter.call # 2


ブロックをProcオブジェクトとして受け取る

# &blockを引数として渡すと、メソッド内部でProcオブジェクトを呼び出すことが出来る

def agree(&block)
printf "Hello,"
block.call if block_given?
end

# yieldを使うことで、上記を省略した書き方ができる
def agree2
printf "Hey!,"
yield if block_given? # blockが与えられたかを判定するので、必ず入れておいたほうがいい
end

agree { p "Tom" }
agree2 { p "Sam" }

# Hello,"Tom"
# Hey!,"Sam"

ActiveRecordのtransaction等、いい例だと思います。


lambda

lambdaもProcオブジェクトを作成する事ができます。

Proc.newとの違いとして、returnbreakの挙動の違い、引数の厳密さが違います。

メソッド
return
break

Proc.new, Kernel.#proc
メソッドを抜ける
例外が発生する

Kernel.lambda
制御を抜ける
制御を抜ける

Proc.new { |x, y| }.call(1) # 1

lambda { |x, y| }.call(1) # ArgumentError

また、lambdaは引数を厳密にチェックするので、Rubyで関数型プログラミングっぽいことをするのには、個人的には向いてると思います。

以下がよくまとめられています。


nilガード ★★★★☆

||(or演算子)を利用したテクニックで、左辺がnil, false または未定義の場合、右辺にデフォルト値をリテラル指定しておくことで、引数の初期化等に役立ちます。

def calc(num)

num ||= 0
num += 1
end


1行if文(if修飾子) ★★★★☆

Rubyでは1行でif文を記述する事ができます。

使い時としては、引数のチェックでreturnraiseをする時等、役割を明確にしておくことで、後から見たときにスッキリとしたコードになるのではと思います。

def hoge(name)

return "No name" if name.nil?
@name = name
end


オブジェクトが持っているmethodを調べる ★★★☆☆

_methodsメソッドは所有しているメソッドを配列で返してくれるので、既存アプリケーションのコードを調べる際に役立ちます。返して来た結果に対してselectfind等で、効率的にコードを調べることが出来ると思います。


  • methods : オブジェクトに対して所有しているメソッドを全て返す

  • private_methods : privateメソッド一覧を返す

  • protected_methods : protectedメソッド一覧を返す

  • public_methods : publicメソッド一覧を返す


tap ★★☆☆☆

tapメソッドはselfを引数としてブロックを評価し、selfを返します。

メソッドチェーンの途中経過のデバッグにも使えます。

自分はメソッドチェーンを作成するときに利用することも有ります。


  • 一般的なメソッドチェーンの例

# coding: utf-8

class Calc

attr_reader :num

def initialize(num)
@num = num
end

def plus
@num += 1
self
end

def minus
@num -= 1
self
end

end

calc = Calc.new(1)
calc.plus.plus.plus.minus.plus.minus
p calc.num # 3


  • tapを利用した場合

selfを返す特性を利用して、メソッドチェーン実装を行ってます

# coding: utf-8

class Calc

attr_reader :num

def initialize(num)
@num = num
end

def plus
self.tap { @num += 1 }
end

def minus
self.tap { @num -= 1 }
end

end

calc = Calc.new(1)
calc.plus.plus.plus.minus.plus.minus
p calc.num

TapがRubyの新たな制御構造の世界を開く


send ★★☆☆☆

sendメソッドは、レシーバが持っているメソッドを動的に呼び出すことが出来ます。

privateメソッドでさえ呼び出すことが出来るので、用法用量を守り、正しくお使いください。

例えばRSpecで、privateメソッド単体をテストする場合や、渡す引数の数が同じで細部の役割が違うメソッドを動的に呼び出すレシーバを作成するといったことが出来ます。


  • 例) Restfulなメソッドを持つクラスのレシーバ

class HttpRest

def get(url, params, opts)
end

def post(url, params, opts)
end

def put(url, params, opts)
end

def delete(url, params, opts)
end

end

class Client

attr_reader :response

def initialize(action, url, params, opts)
@response = HttpRest.new.send(action.to_sym, url, params, opts)
end

end


_eval ★★★☆☆

evalは基本的に文字列を評価してコードを実行するメソッドですが、rubyには幾つか用途に合わせた_evalメソッドが存在します。


  • instance_eval : Object内でコードを実行

  • class_eval : クラスの中でコードを実行

  • module_eval : モジュールの中でコードを実行

これらは、メタプログラミングを行う場合、最低限押さえておきたいメソッドになるかと思います。

class_evalを利用した動的にmethodを追加する例


余談

主にRubyで作られているOSSではよく、DSLを用いる事が多いのですが、最近ではドキュメントを見るより先に、DSLモジュールを探して見たほうが、やってる事が分かるので、調べるのに早いと言うことに気づきました。


補足


  • grep


ご指摘いただいた通り、===で比較した真となる要素の配列を返すので、正規表現以外でもつかえます



  • インスタンスメソッド


ご指摘いただいたとおり、あるクラスから見た場合は、インスタンスメソッドということになります。

https://docs.ruby-lang.org/ja/latest/doc/spec=2fdef.html#class_method