Ruby的nil样对象

前端之家收集整理的这篇文章主要介绍了Ruby的nil样对象前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
如何在 ruby中创建一个Object,该对象将在类似于nil的逻辑表达式中被评估为false

我的意图是在其他对象的某个地方下一个值通常为零,但允许所有的调用继续 – 返回我的零对象而不是零本身的其他对象的嵌套调用.对象将返回自身,以响应任何收到的消息,它不知道如何处理,我预计我将需要实现一些覆盖方法,如nil?

例如:

fizz.buzz.foo.bar

如果fizz的嗡嗡声属性不可用,我会返回我的零对象,这样就可以接受呼叫来阻止返回自身.最终,上述声明应该评估为假.

编辑:

根据下面所有的很好的答案,我想出了以下几点:

class NilClass
  attr_accessor :forgiving
  def method_missing(name,*args,&block)
    return self if @forgiving
    super
  end
  def forgive
    @forgiving = true
    yield if block_given?
    @forgiving = false
  end
end

这允许一些恶劣的技巧,如:

nil.forgiving {
    hash = {}
    value = hash[:key].i.dont.care.that.you.dont.exist
    if value.nil?
        # great,we found out without checking all its parents too
    else
        # got the value without checking its parents,yaldi
    end
}

很明显,你可以将这个块透明地包裹在一些函数调用/ class / module / wherever中.

解决方法@H_502_24@
这是一个很长的答案,一系列的想法和代码示例如何解决问题.

尝试

Rails有一个try method,让你像这样程序.这是如何实现的:

class Object
  def try(*args,&b)
    __send__(*a,&b)
  end
end

class NilClass        # NilClass is the class of the nil singleton object
  def try(*args)
    nil
  end
end

你可以像这样编程:

fizz.try(:buzz).try(:foo).try(:bar)

您可以想象地修改这个工作有点不同,以支持更优雅的API:

class Object
  def try(*args)
    if args.length > 0
      method = args.shift         # get the first method
      __send__(method).try(*args) # Call `try` recursively on the result method
    else
      self                        # No more methods in chain return result
    end
  end
end
# And keep NilClass same as above

那你可以做:

fizz.try(:buzz,:foo,:bar)

andand

andand使用更恶毒的技术,黑客实际上无法直接实例化NilClass子类:

class Object
  def andand
    if self
      self
    else               # this branch is chosen if `self.nil? or self == false`
      Mock.new(self)   # might want to modify if you have useful methods on false
    end
  end
end

class Mock < BasicObject
  def initialize(me)
    super()
    @me = me
  end
  def method_missing(*args)  # if any method is called return the original object
    @me
  end
end

这样可以用这种方式编程:

fizz.andand.buzz.andand.foo.andand.bar

结合一些花哨的重写

再次,您可以扩展这种技术:

class Object
  def method_missing(m,&blk)        # `m` is the name of the method
    if m[0] == '_' and respond_to? m[1..-1] # if it starts with '_' and the object
      Mock.new(self.send(m[1..-1]))         # responds to the rest wrap it.
    else                                    # otherwise throw exception or use
      super                                 # object specific method_missing
    end
  end
end

class Mock < BasicObject
  def initialize(me)
    super()
    @me = me
  end
  def method_missing(m,&blk)
    if m[-1] == '_'  # If method ends with '_'
      # If @me isn't nil call m without final '_' and return its result.
      # If @me is nil then return `nil`.
      @me.send(m[0...-1],&blk) if @me 
    else 
      @me = @me.send(m,&blk) if @me # Otherwise call method on `@me` and
      self                                  # store result then return mock.
    end
  end
end

解释发生了什么:当您调用一个下划线的方法时,您触发模拟模式,_meth的结果将自动包装在Mock对象中.每当你调用这个模拟的方法,它会检查它是否不持有一个零,然后将你的方法转发到该对象(这里存储在@me变量中).然后,模拟器将使用函数调用的结果替换原始对象.当你调用meth_它结束模拟模式并返回实际的返回值.

这允许像这样的api(我使用下划线,但是你可以使用任何东西):

fizz._buzz.foo.bum.yum.bar_

残酷的猴子修补方法

这真的非常讨厌,但它允许一个优雅的API,并不一定会纠正您的整个应用程序的错误报告:

class NilClass
  attr_accessor :complain
  def method_missing(*args)
    if @complain
      super
    else
      self
    end
  end
end
nil.complain = true

这样使用:

nil.complain = false
fizz.buzz.foo.bar
nil.complain = true

猜你在找的Ruby相关文章