Ruby的nil样对象

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

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

例如:

  1. fizz.buzz.foo.bar

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

编辑:

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

  1. class NilClass
  2. attr_accessor :forgiving
  3. def method_missing(name,*args,&block)
  4. return self if @forgiving
  5. super
  6. end
  7. def forgive
  8. @forgiving = true
  9. yield if block_given?
  10. @forgiving = false
  11. end
  12. end

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

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

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

解决方法

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

尝试

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

  1. class Object
  2. def try(*args,&b)
  3. __send__(*a,&b)
  4. end
  5. end
  6.  
  7. class NilClass # NilClass is the class of the nil singleton object
  8. def try(*args)
  9. nil
  10. end
  11. end

你可以像这样编程:

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

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

  1. class Object
  2. def try(*args)
  3. if args.length > 0
  4. method = args.shift # get the first method
  5. __send__(method).try(*args) # Call `try` recursively on the result method
  6. else
  7. self # No more methods in chain return result
  8. end
  9. end
  10. end
  11. # And keep NilClass same as above

那你可以做:

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

andand

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

  1. class Object
  2. def andand
  3. if self
  4. self
  5. else # this branch is chosen if `self.nil? or self == false`
  6. Mock.new(self) # might want to modify if you have useful methods on false
  7. end
  8. end
  9. end
  10.  
  11. class Mock < BasicObject
  12. def initialize(me)
  13. super()
  14. @me = me
  15. end
  16. def method_missing(*args) # if any method is called return the original object
  17. @me
  18. end
  19. end

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

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

结合一些花哨的重写

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

  1. class Object
  2. def method_missing(m,&blk) # `m` is the name of the method
  3. if m[0] == '_' and respond_to? m[1..-1] # if it starts with '_' and the object
  4. Mock.new(self.send(m[1..-1])) # responds to the rest wrap it.
  5. else # otherwise throw exception or use
  6. super # object specific method_missing
  7. end
  8. end
  9. end
  10.  
  11. class Mock < BasicObject
  12. def initialize(me)
  13. super()
  14. @me = me
  15. end
  16. def method_missing(m,&blk)
  17. if m[-1] == '_' # If method ends with '_'
  18. # If @me isn't nil call m without final '_' and return its result.
  19. # If @me is nil then return `nil`.
  20. @me.send(m[0...-1],&blk) if @me
  21. else
  22. @me = @me.send(m,&blk) if @me # Otherwise call method on `@me` and
  23. self # store result then return mock.
  24. end
  25. end
  26. end

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

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

  1. fizz._buzz.foo.bum.yum.bar_

残酷的猴子修补方法

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

  1. class NilClass
  2. attr_accessor :complain
  3. def method_missing(*args)
  4. if @complain
  5. super
  6. else
  7. self
  8. end
  9. end
  10. end
  11. nil.complain = true

这样使用:

  1. nil.complain = false
  2. fizz.buzz.foo.bar
  3. nil.complain = true

猜你在找的Ruby相关文章