今週の気になった bugs.ruby
書き溜めてはいたんですが、ブログに公開するのを忘れてました。
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
[Feature #16986] Anonymous Struct literal
s = ${a: 1, b: 2, c: 3} s.a # => 1 s.b # => 2 s.c # => 3
- いまはそんなに
Struct
は使わないけどこういう記法があるとガンガン使うと思う - 例えばこんな感じに雑にダックタイピング呼び出しするメソッドに値を渡す場合とか
def print(user) pp "#{user.id} #{user.name}" end name = "homu" age = 14 # Struct を経由してメソッド呼び出しされるようにする print(${ name: name, age: age })
Struct
だとobj.value
だけじゃなくてobj[:value]
みたいに添え字アクセスもできるので Hash の代わりとして使用できそう- Hash と違い存在しないキーにアクセスするとエラーになるのは便利そう
# Hash の場合は typo してても気づきづらい user = { name: "homu", age: 14 } # no error user[:nmae] # Struct だと存在しないキーにアクセスするとエラーになる user = Struct.new(:name, :age).new("homu", 14) # error user[:nmae]
${}
だとブロックと差別化できるのでよいp { a: 1, b: 2 }
とは書けないがp ${ a: 1, b: 2 }
とはかける
- リテラルじゃなくて
{a: 1, b: 2}.to_struct
みたいな変換メソッドがある方が便利そう? Hash リテラルと比較して以下の部分が気になるlabel: expr
みたいな定義のみ許可されていて${**h}
みたいなのは許可されていない
# Symbol でない値をキーにできるか(Symbol だけ?) ${ "a" => 1 } ${ 1 => 1 } # 変数をキーにできるか key = :a ${ key => 1 } # `**` で Hash が展開できるのか hash = { a: 1, b: 2 } ${ c: 3, **hash }
[Feature #13067] TrueClass,FalseClass to provide ===
to match truthy/falsy values.
TrueClass#===
FalseClass#===
を定義する提案true ==== obj
やfalse === obj
したときにobj
がtruthy
かfalsy
か判定するnil
false
だったらfalsy
、それ以外ならtruthy
ary.grep(true)
みたいなことができる- 以下の
case ~ when
だと挙動が変わってしまう
def normalize_hsts_options(options) # options が nil の場合 case false にマッチしてしまう case options when false # ... when nil, true # ... else # ... end end
ary.grep(true)
やary.grep(false)
はary.select(&:itself)
やary.reject(&:itself)
で置き換えられる- 互換性の面からクローズされた
[Feature #16985] Improve pp
for Hash
and String
pp
でHash
やString
の出力をよくしようとするチケット
pp({hello: 'My name is "Marc-André"'}) # 現状 # => {:hello=>"My name is \"Marc-André\""} # 提案 # => {hello: 'My name is "Marc-André"'}
- 普通に便利そう
[Feature #17004] Provide a way for methods to omit their return value
- 任意のメソッドが戻り値を受け取るか受け取らないかを判定するメソッドの追加
RubyVM.return_value_is_used?
というメソッドを追加- 戻り値を受け取る場合は
true
を返し、そうでない場合はfalse
を返す
- こんな感じで判定する事ができる
def hoge if RubyVM.return_value_is_used? pp "戻り値を受け取る" else pp "戻り値を受け取らない" end end hoge # "戻り値を受け取らない" value = hoge # "戻り値を受け取る" Array hoge # "戻り値を受け取る" hoge.nil? # "戻り値を受け取る" # 最後に呼び出したやつも? hoge # "戻り値を受け取る"
- これを利用すると次のように『戻り値を受け取らない場合は無駄な処理を省く』事ができる
class Hash def refresh(key) # 引数を受け取る場合のみ result を設定する if RubyVM.return_value_is_used? result = self[key] end self[key] = nil result end end homu = { name: "homu", age: 14 } homu.refresh(:name) p homu # => {:name=>nil, :age=>14} age = homu.refresh(:age) pp homu # => {:name=>nil, :age=>nil} pp age # => 14
class User < ActiveRecord::Base def update_name(name) update!(name: name) # reload した値を返す reload if RubyVM.return_value_is_used? end end
- ただし、次のように戻り値になる場合は『戻り値を受け取る』ことになるので注意
def hoge if RubyVM.return_value_is_used? pp "戻り値を受け取る" else pp "戻り値を受け取らない" end end def foo hoge end foo # "戻り値を受け取る" def bar hoge nil end bar # "戻り値を受け取らない"
- 便利そうっちゃ便利そうだけどメソッドごとに
RubyVM.return_value_is_used?
で処理を分岐するのはめっちゃきつそう- 実際には極端に重くなるようなメソッドぐらいで使いそうな気がするけど…どうだろう
- 別のアプローチはないかな…