👌
[Feature #20498] obj.!exits? のような構文を追加する提案
[Feature #20498] Negated method calls
- 次のように
#exists?
を否定する場合は呼び出し元の先頭に!
を追加する必要がある
must_create_user = !User.where(somelong: :condition, even_more: "thing").exists?
- これの視認性が悪いので改善したいという内容のチケット
- 現状だと上記のことを回避する場合は以下のような選択肢がある
- 変数に代入する
-
unless
を使う -
.!
をチェーンする.exists?.!
- 否定するメソッド名を定義する
- チケットの起票者は以下のような構文を追加する提案をしている
# 判定メソッドの直前に ! を追加することで否定する
must_create_user = User.where(somelong: :condition, even_more: "thing").!exists?
- 提案された構文は ruby-next ですでにプロトタイプとして実装されているみたいですね
- ただし、このプロトタイプは以下のようなケースには対応してないみたいです
# foo が nil のときには nil を返してほしいが
foo&.!empty?
- ちなみに
obj.!empty?
は現状でも有効な Ruby の構文で以下のような解釈がされます
# obj.! メソッドが呼び出されて、その引数に empty? メソッドの結果が渡される
obj.!(empty?)
- コメントでは
.not
や.!
を使うといいんじゃないか、と書かれていますが判定メソッドに引数がある場合はやはり視認性が悪いとも返信されています
puts A.new.exists?(with_friends: true, skip_administrator: true).!
puts A.new.exists?(with_friends: true, skip_administrator: true).not
- その他、コメントででていた代替案
foo&.empty?&.!
foo&.non&.empty?
foo&.empty?&.not
foo&.!(&:empty?)
-
.not.empty?
や.!(&:empty?)
は既存の機能で便利メソッド定義するような形ですねー
class MethodNegator < BasicObject
def initialize(obj)
@obj = obj
end
def method_missing(method, ...)
@obj.public_send(method, ...)
end
def respond_to_missing?(include_all = false)
@obj.respond_to?(include_all)
end
end
module Kernel
def non(sym=nil)
if sym
proc { |*args,**kwargs,&block| !sym.to_proc.call(*args,**kwargs,&block) }
else
MethodNegator.new(self)
end
end
end
p "".non.empty?
# => true
p ["", "a", "b"].select(&non(:empty?))
# => ["a", "b"]
- 個人的にはこういう機能がほしいことは多いんだけど、構文的にちょっと過剰にならないかは気になる
- 他の言語とかこういうケースはどうカバーしているんですかねー
- ちなみに代替案としては以下のようにかけなくもないかなあ…
must_create_user = User.where(somelong: :condition, even_more: "thing").then { !_1.exists? }
Discussion