【转】ruby 的那些绕不过的坑_Ruby_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > Ruby > 【转】ruby 的那些绕不过的坑

【转】ruby 的那些绕不过的坑

 2016/5/12 5:31:17  RoomFourteen224  程序员俱乐部  我要评论(0)
  • 摘要:转自:https://ruby-china.org/topics/17742首发:http://zhaowen.me/blog/2014/03/04/ruby-gotchas/原文:RubyGotchasthatwillcomebacktohauntyou大多数RubyonRails的初学者们都会为这个出色的框架着迷,在缺乏Ruby语言知识的情况下就开始开发应用程序。这也无可厚非。至少,除非这些初学者们坚持了下来,然后摇身一变,成了没有Ruby基础知识的「senior」开发者。不论如何
  • 标签:Ruby

转自:https://ruby-china.org/topics/17742

?

首发:http://zhaowen.me/blog/2014/03/04/ruby-gotchas/
原文:Ruby Gotchas that will come back to haunt you

?

?

?

大多数 Ruby on Rails 的初学者们都会为这个出色的框架着迷,在缺乏 Ruby 语言知识的情况下就开始开发应用程序。这也无可厚非。至少,除非这些初学者们坚持了下来,然后摇身一变,成了没有 Ruby 基础知识的「senior」开发者。

不论如何,不管初学者还是有经验的程序员,迟早都会遇到传说中的「Ruby 的坑」。这些平时埋伏很深的语言上的细微之处将会耗费我们数个小时的死命调试(?puts "1"?),查明真相后我们会惊呼「怎么会这样??!」,「好吧,我发誓这一次我会去看那本镐头封面的书!」,又或者,我们喊了声「操!」然后就去睡觉了。

我在这篇文章中列举了开发者们需要警惕的 Ruby 中常见的坑。我在每个条目中都给出了示例代码,包括了让人迷惑的或容易出错的代码。

另外,我也给出了最佳实践来简化你(和维护你代码的人)的生活。如果你对「最佳实践」不感冒,你也可以选择阅读详细的解释来了解为什么这个坑会引发 bug(多数情况下是因为它和你所想的不一样)。

monospace; font-size: 12px !important; padding: 1px 2px !important; color: #444444 !important; border-radius: 0px; border: 0px; margin: 2px; line-height: 20px; background-color: #f5f5f5 !important;">and/or?不同于?&&/||

class="highlight ruby">surprise = true and false # => surprise 的值为 true
surprise = true && false  # => surprise 的值为 false

最佳实践

只使用?&& / ||?运算符。

详情

  • and / or?运算符的优先级比?&& / ||?低
  • and / or?的优先级比?=?低,而?&& / ||?的优先级比?=?高
  • and?和?or?的优先级相同,而?&&?的优先级比?||?高

我们来给上述示例代码加上括号,这样就可以明显地看出?and?和?&&?在用法上的不同之处了。

(surprise = true) and false # => surprise is true
surprise = (true && false)  # => surprise is false

也有这样的说法:and / or?用于流程控制,而?&& / ||?用于布尔型运算。但我认为:不要使用这些运算符的关键字版本and / or / not),而使用更为清晰的?if?和?unless?等。更加明确,更少困惑,更少 bugs。

延伸阅读:Difference between “or” and || in Ruby?

eql??不同于?==(也不同于?equal??或?===

1 == 1.0   # => true
1.eql? 1.0 # => false

最佳实践

只使用?==?运算符。

详情

=====eql??和?equal??都是互不相同的运算符,各自有不同的用法,分别用于不同的场合。当你要进行比较时,总是使用==,除非你有特殊的需求(比如你?真的?需要区分?1.0?和?1)或者出于某些原因重写(override)了某个运算符。

没错,eql??可能看起来要比平凡的?==?更为?聪明?,但是你真的需要这样吗,去?区分两个相同的东西??

延伸阅读:What’s the difference between equal?, eql?, ===, and ==?

super?不同于?super()

class Foo
  def show
    puts 'Foo#show'
  end
end

class Bar < Foo
  def show(text)
    super

    puts text
  end
end

Bar.new.show('test') # ArgumentError: wrong number of arguments (1 for 0)

最佳实践

在这里,省略括号可不仅仅是品味(或约定)的问题,而是确实会影响代码的逻辑。

详情

  • 使用?super(没有括号)调用父类方法时,会将传给这个方法的参数原封不动地传给父类方法(因此在?Bar#show?里面的?super?会变成?super('test'),引发了错误,因为父类的方法不接收参数)
  • super()(带括号)在调用父类方法时不带任何参数,正如我们期待的那样。

延伸阅读:Super keyword in Ruby

自定义异常不能继承?Exception

class MyException < Exception
end

begin
  raise MyException
rescue
  puts 'Caught it!'
end

# MyException: MyException
#       from (irb):17
#       from /Users/karol/.rbenv/versions/2.1.0/bin/irb:11:in `<main>'

(上述代码不会捕捉到?MyException,也不会显示?'Caught it!'?的消息。)

最佳实践

  • 自定义异常类时,继承?StandardError?或任何其后代子类(越精确越好)。永远不要直接继承?Exception
  • 永远不要?rescue Exception。如果你想要大范围捕捉异常,直接使用空的?rescue?语句(或者使用?rescue => e?来访问错误对象)。

详情

  • 当你使用空的?rescue?语句时,它会捕捉所有继承自?StandardError?的异常,而不是?Exception
  • 如果你使用了?rescue Exception(当然你不应该这样),你会捕捉到你无法恢复的错误(比如内存溢出错误)。而且,你会捕捉到 SIGTERM 这样的系统信号,导致你无法使用 CTRL+C 来中止你的脚本

延伸阅读:Why is it bad style to `rescue Exception => e` in Ruby?

class Foo::Bar?不同于?module Foo; class Bar

MY_SCOPE = 'Global'

module Foo
  MY_SCOPE = 'Foo Module'

  class Bar
    def scope1
      puts MY_SCOPE
    end
  end
end

class Foo::Bar
  def scope2
    puts MY_SCOPE
  end
end

Foo::Bar.new.scope1 # => "Foo Module"
Foo::Bar.new.scope2 # => "Global"

最佳实践

总是使用长的,更清晰的,module 把 class 包围的写法:

module Foo
  class Bar
  end
end

详情

  • module?关键字(class?和?def?也一样)会对其包围的区域创建新的词法作用域(lexical scope)。所以,上面的?module Foo?创建了?'Foo'?作用域,常量?MY_SCOPE?和它的值?'Foo Module'?就在其中。
  • 在这个 module 中,我们声明了?class Bar,又会创建新的词法作用域(名为?'Foo::Bar'),它能够访问父作用域('Foo')和定义在其中的所有常量。
  • 然而,当你使用了这个?::?「捷径」来声明 Foo::Bar 时,class Foo::Bar?又创建了一个新的词法作用域,名字也叫?'Foo::Bar',但它没有父作用域,因此不能访问?'Foo'?里面的东西。
  • 因此,在?class Foo::Bar?中我们只能访问定义在脚本的开头的?MY_SCOPE?常量(不在任何 module 中),其值为?'Global'

延伸阅读:Ruby – Lexical scope vs Inheritance

多数?bang!?方法如果什么都没做就会返回?nil

'foo'.upcase! # => "FOO"
'FOO'.upcase! # => nil

最佳实践

永远不要依赖于内建的?bang!?方法的返回值,比如在条件语句或流程控制中:

@name.upcase! and render :show

上面的代码会造成一些无法预测的行为(或者更准备地说,我们可以预测到当?@name?已经是全部大写的时候就会失败)。另外,这个示例也再一次说明了为什么你不应该使用?and/or?来控制流程。敲两个回车吧,不会有树被砍的。

@name.upcase!

render :show

attribute=(value)?方法永远返回传给它的?value?而无视?return?值

class Foo
  def self.bar=(value)
    @foo = value

    return 'OK'
  end
end

Foo.bar = 3 # => 3

(注意这个赋值方法?bar=?返回了?3,尽管我们显式地在最后?return 'OK'。)

最佳实践

永远不要依赖赋值方法的返回值,比如下面的条件语句:

puts 'Assigned' if (Foo.bar = 3) == 'OK' # => nil

显然这个语句不会如你所想。

延伸阅读:ruby, define []= operator, why can’t control return value?

private?并不会让你的?self.method?成为私有方法

class Foo

  private
  def self.bar
    puts 'Not-so-private class method called'
  end

end

Foo.bar # => "Not-so-private class method called"

(注意,如果这个方法真的是私有方法,那么?Foo.bar?就会抛出?NoMethodError。)

最佳实践

要让你的类方法变得私有,你需要使用?private_class_method :method_name?或者把你的私有类方法放到?class << self?block 中:

class Foo

  class << self
    private    
    def bar
      puts 'Class method called'
    end    
  end

  def self.baz
    puts 'Another class method called'
  end
  private_class_method :baz

end

Foo.bar # => NoMethodError: private method `bar' called for Foo:Class
Foo.baz # => NoMethodError: private method `baz' called for Foo:Class

延伸阅读:creating private class method

我才不怕这些 Ruby 的坑!

上面列举的 Ruby 的坑可能看上去没什么大不了的,乍一看似乎只是属于代码风格和约定的范畴。

但相信我,如果你不重视它们,终有一天你会在 Ruby on Rails 的开发过程中碰到诡异无比的问题。它会让你心碎。因为你已经累觉不爱。然后孤独终老。永远。

发表评论
用户名: 匿名