最近、スタートアップ系や新規開発で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 ★★★★★
多分C#をやっている人以外は、まずなんて読むのか、から始まると思います。
"エニュメラブル"と読むらしいですが、これはRubyの繰り返し(each)を行うオブジェクトに対して使用する事ができます。これらは組み込みオブジェクトなので、普通にeach内でゴニョゴニョやるよりは、パフォーマンスがいいので、知っておいて損はないです。
使い分け
- もとの配列の各要素を変換して、変換後の新しい配列を作成する ->
map(collect)
- 要素が特定の条件を満たしているか ->
all?
,none?
,any?
,one?
- 正規表現にマッチする要素を取得する ->
grep
- 要素に対して演算した結果を取得する ->
reduce(inject)
- 要素のグルーピング ->
group_by
- 要素の最小値や最大種を取得する ->
max
,min
,minmax
- 要素をソートした結果を取得する ->
sort
,sort_by
- 要素を特定の条件で検索した結果を取得する ->
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との違いとして、return
とbreak
の挙動の違い、引数の厳密さが違います。
メソッド | 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文を記述する事ができます。
使い時としては、引数のチェックでreturn
やraise
をする時等、役割を明確にしておくことで、後から見たときにスッキリとしたコードになるのではと思います。
def hoge(name)
return "No name" if name.nil?
@name = name
end
オブジェクトが持っているmethodを調べる ★★★☆☆
_methods
メソッドは所有しているメソッドを配列で返してくれるので、既存アプリケーションのコードを調べる際に役立ちます。返して来た結果に対してselect
やfind
等で、効率的にコードを調べることが出来ると思います。
- 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
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