respond_to?でメソッドがあるかを確認する
タイトルの通りです。
respond_toの書き方
class Foo def foo p 'foo' end private def private_foo p 'private foo' end end x = Foo.new x.respond_to?(:foo) => true x.respond_to?(:foo, true) => true x.respond_to?(:private_foo) => false x.respond_to?(:private_foo, true) => true
private method
は第二引数にtrue
を渡さないと判定してくれないようです。
public_method
はどっちでも反応しますね。
以上です。
shared_examples_forに引数を渡して処理をさらにまとめる
ここ最近はリファクタリングやより短い書き方を考えてます。
一行でもコードから文字が減るのは嬉しい。
そんなわけで、カラム名が違うだけで、内容同じじゃん・・・って場合のテストの書き方です。
前提条件
カラムの:login, :passwordは半角英数字のみで文字数制限を含んでいます。 ただし、文字数制限は違います。 けど、やってることは同じなので、処理をまとめます。
shared_examples_forの書き方
shared_examples_for 'alphanumeric' do |column_name, min_len, max_len| let(:factory_name) { described_class.name.underscore } let(:record) { build(factory_name) } context "#{column_name}" do context '登録できる' do context '半角英数字' do it '_を含むもの' do record[column_name] = 'foo_' expect(record).to be_valid end context '文字列の長さ' do it "#{min_len}文字" do record[column_name] = 'a' * min_len expect(record).to be_valid end it "#{max_len}文字" do record[column_name] = 'a' * max_len expect(record).to be_valid end end end end end
こんな感じですかね。
ここでのポイントはrecord[column_name]
で値を入れるようにすることです。
record.column_name
ではno method error
になります。
あとはit_behaves_like
に引数を渡します。
it_behaves_like 'alphanumeric', :login, 4, 20 it_behaves_like 'alphanumeric', :password, 8, 20
これで同じ処理をまとめることができました。
ただ、password
がpassword_confirmation
がいるよ!ってエラーになるので、そこはif文
で分岐処理を書きました。
context '半角英数字' do it '_を含むもの' do value = 'foo_' if column_name == :password expect(record.update(password: value, password_confirmation: value)).to be_truthy else record[column_name] = value expect(record).to be_valid end end end
ここをうまく書ける方法があれば教えていただきたいですm( )m
返ってくる値を真偽値に変換する
例えば、match
を使用して、正しいかどうかの確認だけをしたいと思います。
REG_EXP = /foo/ x = 'foo' x.match(REG_EXP) => #<MatchData "foo"> x.match('bar') => nil
通常だと判定した結果が返ってきます。
これを真偽値に変換します。
!!(x.match(REG_EXP)) => true !!(x.match('bar')) => false
以上です。
rspecでprivate methodを呼び出したい時の方法
privateメソッドを実行したいけど、できないよ!って場合の方法です。
send
を使えばできます。
class Foo private def foo puts 'hoge' end end x = Foo.new x.send(:foo) hoge => nil x.foo NoMethodError: private method `foo' called for #<Foo:0x007fa279135010>
こうなるので、send
を使ってメソッドを呼び出せば、private methodもテストできます。
これは便利だ。
オブジェクトのインスタンス変数名とメソッド一覧の取得方法
タイトルの通りです。
オブジェクトのインスタンス変数名とメソッド一覧の取得方法を探っていきます。
まずはインスタンス変数名からです。
インスタンス変数名の取得方法
instance_variables
class Foo def initialize @foo = 1 @bar = 2 end end x = Foo.new x.instance_variables => [:@foo, :@bar]
このメソッドはインスタンス変数を配列で返してくれます。
メソッド一覧の取得
メソッド名 | 取得できるメソッド |
---|---|
Object#methods | オブジェクトの持つプライベートメソッド以外 |
Object#public_methods | オブジェクトの持つパブリックメソッド |
Object#private_methods | オブジェクトの持つプライベートメソッド |
Object#protected_methods | オブジェクトの持つプロテクテッドメソッド |
Object#singleton_methods | オブジェクトの持つ特異メソッド |
class Foo def foo; end private def bar; end protected def baz; end end class Bar < Foo def foo_b; end private def bar_f; end protected def baz_f; end end x = Foo.new y = Bar.new def x.foo; end x.methods => [:foo, :baz, :to_yaml, :to_yaml_properties, ... x.public_methods => [:foo, :to_yaml, :to_yaml_properties, :pry, x.private_methods => [:bar, :DelegateClass, :default_src_encoding, :sprintf, x.protected_methods => [:baz] y.methods => [:foo_f, :baz_f, :foo, :baz, y.public_methods => [:foo_f, :foo, :to_yaml, :to_yaml_properties, y.private_methods => [:bar_f, :bar, :DelegateClass, :default_src_encoding, y.protected_methods => [:baz_f, :baz] x.singleton_methods => [:foo]
ちなみに特異メソッドのみを呼び出すのは、methods
からでも呼び出せます。
その場合は、引数にfalse
を渡せばいいです。
参考
instance method Object#methods (Ruby 2.3.0)
x.methods(false) => [:foo]
まとめ
メソッド呼び出し方法を知っておけば、デバック時に役立つ可能性が生まれます。
以上です。
rubyのthreadについて
スレッドは同じプロセス上でメモリを共有しつつ、処理を並列に実行することができます。
スレッドを用いることで、単独で時間がかかる処理を早くすることができる可能性があります。
スレッドの生成
スレッドを作成し、処理を実行するには、Thread.fork/Thread.new/Thread.startを使用します。
下記は複数ファイルの行数を出力する例です。
files = %w(ruby.rb test.rb) threads = files.map do |file| Thread.fork do num = File.readlines(file).length "#{file}: #{num}" end end p threads.map(&:value)
変数の扱い
スレッドは同じプロセス上のスレッドとメモリを共有します。
for item in %w(foo bar baz) Thread.fork do sleep 1 puts item end end (Thread.list - [Thread.current]).each &:join =>baz =>baz =>baz
メモリが共有されているので、最後に当たったものを参照しています。
他のスレッドと共有したくない値は、ブロックの仮引数として受け取るようにしましょう。
for item in %w(foo bar baz) Thread.fork item do |value| sleep 1 puts value end end (Thread.list - [Thread.current]).each &:join =>foo =>baz =>bar
以上です。
eval族
evalについてです。
|メソッド名|動作| |Kernel.#eval|selfが呼び出された式を評価する| |Module#class_eval|レシーバのクラスをselfとして式を評価する| |Module#module_eval|レシーバのモジュールをselfとして式を評価する| |Basic#Object#instace_eval|レシーバのオブジェクトをselfとして式を評価する|
Kernel.#eval
foo = 'bar' eval 'foo' => "bar"
evalとBindingオブジェクト
Bindingオブジェクトを使うことで、コンテキストに名前がないものでも評価されるようになります。
class Foo def initialize @foo = 'foo' end def bar local_val = 'bar' binding end end x = Foo.new y = x.bar eval "@foo", y => 'foo' eval "local_val", y => 'bar'
ただし、initializeにbindingをさしてもうまくいきませんでした。
eval "@foo", y TypeError: wrong argument type String (expected binding)
以上です。
参考
includeとprependで読み込む順番の確認
includeとprependで読み込む順番を確認していきたい思います。
moduleの複数読み込みをした場合に、どのような順番で読み込まれるのかを確認していきます。
includeの順番に依存
何も考えないでincludeしていく場合は、読み込む順番に依存していることがわかります。
module Foo def foo puts 'foo' end end module Bar def foo puts 'foo bar' end end class Baz include Foo include Bar end Baz.ancestors => [Baz, Bar, Foo, Object, Kernel, BasicObject] x = Baz.new x.foo =>foo bar
ancestorsで確認しても、[Baz, Bar, Foo, Object, Kernel, BasicObject]
下から読み込みされていることがわかります。
prependで優先的に読む
prependで試してみます。
module Foo def foo puts 'foo' end end module Bar def foo puts 'foo bar' end end class Baz prepend Foo include Bar end Baz.ancestors => [Foo, Baz, Bar, Object, Kernel, BasicObject] x = Baz.new x.foo =>foo
これを見るとBazより先に読み込まれていることがわかります。
ということは、同じメソッドを用意したらどうなるか確認してみます。
module Foo def foo puts 'foo' end end module Bar def foo puts 'foo bar' end end class Baz prepend Foo include Bar def foo puts 'foo baz' end end Baz.ancestors => [Foo, Baz, Bar, Object, Kernel, BasicObject] x = Baz.new x.foo =>foo
Fooの方が先にあるので、当然moduleのFooのmethodが読まれます。
まとめ
既存のメソッドをオーバーライドする・優先的に読む必要があるのなら、prependを使うのがいいでしょう。
lambdaについて
lambaはProcオブジェクトの別の書き方です。
Procオブジェクトやブロックに関してはこちらの記事をみてください。
ブロックについて - mikami's engineer diary
x = lambda { 'foo' } x.call =>foo
lambdaは->
でも書けるので上はこのように書き直せます。
x = -> { 'foo' } x.call => "foo"
railsだとこっちの方が見かけると思います。
ブロックを渡していたんですね。
引数も渡せます。
x = -> (x) { x + 100 } x.call(5) => "105"
Procオブジェクトとの挙動の違いはあります。
詳細はこちらをご覧ください。