【问题标题】:Nils and method chainingNils 和方法链
【发布时间】:2011-11-06 07:29:10
【问题描述】:

我刚刚进入红宝石世界,我可以伸出援助之手。

假设bnil

我希望下面的代码返回 nil 而不是“NoMethodError: undefined method”

a.b.c("d").e

我尝试的第一件事是重载 NilClass 的 missing_method 以简单地返回一个 nil。这是我想要的行为,但我不想被打扰。

如果我能做这样的事情我会很高兴

SafeNils.a.b.c("d").e

所以这就像一种在本地重载 NilClass 行为的干净方法。

我很想听听一些想法或很好的资源来深入了解这一点。只要它相当干净,我也对其他方法持开放态度。

非常感谢。

【问题讨论】:

    标签: ruby-on-rails ruby null metaprogramming


    【解决方案1】:

    您可以使用inline rescue:

    x = a.b.c("d").e rescue nil
    

    如果bnilx 将是nil

    或者,正如有人在该链接中评论的那样,您可以使用andand gem(请参阅docs):

    x = a.andand.b.andand.c("d").andand.e
    

    【讨论】:

      【解决方案2】:

      我认为您可以在 Rails 中找到一个很好的解决方案,但该解决方案采用不同的方法。看看try 方法。这是一种干净的方法。

      【讨论】:

      【解决方案3】:

      从 ruby​​ v2.3.0 开始,语言中内置了另一种方法,即安全导航运算符 (&.) 你可以写:a&.b&.c&.d,如果链中的一个调用返回 nil,你将安全地得到 nil。 你可以在这里读更多关于它的内容: http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/

      【讨论】:

      • 从 2015 年开始,这是现代 Ruby 的实际答案。其他答案对于 Ruby v2.2 及更早版本就足够了。
      【解决方案4】:

      "try" 非常干净,正如 lucapette 所说。更一般地说,您也可以使用 begin-rescue-end 块,具体取决于您的情况。

      begin
        a.b.c("d").e
      rescue NoMethodError=>e
        return nil
      end
      

      【讨论】:

        【解决方案5】:

        查看Ickmaybe

        a.b.maybe.c("d").maybe.e
        

        或使用块:

        a.b.maybe { |b| b.c("d").e }
        

        【讨论】:

        【解决方案6】:

        提前说明:b是方法,不是变量。所以 b 'is' 不是 nil,它返回 nil。

        当'b'是一个方法时,为什么不修改b,所以它返回一些东西,什么可以处理nil。

        请参阅下面的示例。


        您可以定义缺少的方法:

        class A
          def b
            nil
          end
        end
        class NilClass
          def c(p);nil;end
          def e;nil;end
        end
        a = A.new
        
        a.b.c("d").e
        

        但我认为,救援可能更适合您的需要:

        class A
          def b
            nil
          end
        end
        a = A.new
        x = begin a.c.c("d").e 
        rescue NoMethodError
          nil
        end
        

        一个例子,你如何定义一个类似 nil 的例子。

        class A
          def b
            MyNil.new
          end
        end
        
        class MyNil
          def method_missing(m, *args, &block)
              if nil.respond_to?(m)
                nil.send(m)
            else
              self
            end
          end
          #Simulate nils bahaviour.
          def nil?;true;end
          def inspect;nil.inspect;end
          def to_s;nil;end
        end
        
        a = A.new
        x = a.b.c("d").e 
        p x
        puts x
        p x.nil?
        

        【讨论】:

          【解决方案7】:

          要使用与您写的类似的safe_nils

          def safe_nils &blk
            return blk.call
          rescue NoMethodError
            return nil
          end
          
          safe_nils { a.b.c("d").e }
          

          【讨论】:

            【解决方案8】:

            我为此制作了may_nil gem。 https://github.com/meesern/may_nil

            就像@Sony Santos 的 safe_nils 回答一样,它使用一个块来包装方法链并拯救 NoMethodError,但会检查 nil(因此会正确引发其他类的异常)。

            require `may_nil`
            may_nil {a.b.c("d").e}   => nil (or the end result)
            may_nil {0.b.c("d").e}   => Exception: NoMethodError (b on Fixnum)
            

            andandikemaybe 不同,您不需要在方法链中添加胡椒粉。

            a.andand.b.andand.c("d").andand.e
            ==>
            may_nil{ a.b.c("d").e }
            

            【讨论】:

              【解决方案9】:

              一种方法是对局部变量使用内联赋值:

              a && (ab = a.b) && (abcd = ab.c("d")) && abcd.e
              

              你在这里得到的链条不是很优雅,但对于较短的链条它可能很有用:

              def date_updated(entry)
                (updated = entry.updated) && updated.content
              end
              

              【讨论】:

                猜你喜欢
                • 2015-12-03
                • 2016-05-22
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-07-20
                • 1970-01-01
                • 2011-07-28
                • 2021-02-07
                相关资源
                最近更新 更多