编程规范-ruby中我不熟识的

2016-04-27  本文已影响202人  john_ny

今天review了一次ruby编程规范,把一些之前我不熟识的在这里记录下来。

# 差
class FooError < StandardError
end

# 勉强可以
class FooError < StandardError; end

# 好
FooError = Class.new(StandardError)
# 差
case
  when song.name == 'Misty'
    puts 'Not again!'
  when song.duration > 120
    puts 'Too long!'
  when Time.now.hour > 21
    puts "It's too late"
  else
    song.play
end

# 好
case
when song.name == 'Misty'
  puts 'Not again!'
when song.duration > 120
  puts 'Too long!'
when Time.now.hour > 21
  puts "It's too late"
else
  song.play
end
# 差 - 非常费解
kind = case year
when 1850..1889 then 'Blues'
when 1890..1909 then 'Ragtime'
when 1910..1929 then 'New Orleans Jazz'
when 1930..1939 then 'Swing'
when 1940..1950 then 'Bebop'
else 'Jazz'
end

result = if some_cond
  calc_something
else
  calc_something_else
end

# 好 - 结构清晰
kind = case year
       when 1850..1889 then 'Blues'
       when 1890..1909 then 'Ragtime'
       when 1910..1929 then 'New Orleans Jazz'
       when 1930..1939 then 'Swing'
       when 1940..1950 then 'Bebop'
       else 'Jazz'
       end

result = if some_cond
           calc_something
         else
           calc_something_else
         end

# 好 - 并且更好地利用行宽
kind =
  case year
  when 1850..1889 then 'Blues'
  when 1890..1909 then 'Ragtime'
  when 1910..1929 then 'New Orleans Jazz'
  when 1930..1939 then 'Swing'
  when 1940..1950 then 'Bebop'
  else 'Jazz'
  end

result =
  if some_cond
    calc_something
  else
    calc_something_else
  end
# 差 - 有几个零?
num = 1000000

# 好 - 方便人脑理解
num = 1_000_000
# 差
if something
  something = something.downcase
end

# 差
something = something ? something.downcase : nil

# 勉强可以
something = something.downcase if something

# 好
something = something && something.downcase

# 更好
something &&= something.downcase
# 差
result = hash.map { |k, v| v + 1 }

def something(x)
  unused_var, used_var = something_else(x)
  # ...
end

# 好
result = hash.map { |_k, v| v + 1 }

def something(x)
  _unused_var, used_var = something_else(x)
  # ...
end

# 好
result = hash.map { |_, v| v + 1 }

def something(x)
  _, used_var = something_else(x)
  # ...
end
# 差
'%d %d' % [20, 10]
# => '20 10'

# 好
sprintf('%d %d', 20, 10)
# => '20 10'

# 好
sprintf('%{first} %{second}', first: 20, second: 10)
# => '20 10'

format('%d %d', 20, 10)
# => '20 10'

# 好
format('%{first} %{second}', first: 20, second: 10)
# => '20 10'
# 差
paths = [paths] unless paths.is_a? Array
paths.each { |path| do_something(path) }

# 好
[*paths].each { |path| do_something(path) }

# 好 - 并且更具可读性
Array(paths).each { |path| do_something(path) }
# 差
do_something if x >= 1000 && x <= 2000

# 好
do_something if (1000..2000).include?(x)

# 好
do_something if x.between?(1000, 2000)
# 差
all_songs = users.map(&:songs).flatten.uniq

# 好
all_songs = users.flat_map(&:songs).uniq
# 差
array.reverse.each { ... }

# 好
array.reverse_each { ... }

关于注释的一些说明

类定义

class Person
  # 首先是 extend 与 include
  extend SomeModule
  include AnotherModule

  # 内部类
  CustomErrorKlass = Class.new(StandardError)

  # 接着是常量
  SOME_CONSTANT = 20

  # 接下来是属性宏
  attr_reader :name

  # 跟着是其他宏(如果有的话)
  validates :name

  # 公开的类方法接在下一行
  def self.some_method
  end

  # 初始化方法在类方法和实例方法之间
  def initialize
  end

  # 跟着是公开的实例方法
  def some_method
  end

  # 受保护及私有的方法等放在接近结尾的地方
  protected

  def some_protected_method
  end

  private

  def some_private_method
  end
end
# 差

# foo.rb
class Foo
  class Bar
    # 定义 30 多个方法
  end

  class Car
    # 定义 20 多个方法
  end

  # 定义 30 多个方法
end

# 好

# foo.rb
class Foo
  # 定义 30 多个方法
end

# foo/bar.rb
class Foo
  class Bar
    # 定义 30 多个方法
  end
end

# foo/car.rb
class Foo
  class Car
    # 定义 20 多个方法
  end
end
# 差
class SomeClass
  def self.some_method
    # 省略主体
  end

  def self.some_other_method
    # 省略主体
  end
end

# 好
module SomeModule
  module_function

  def some_method
    # 省略主体
  end

  def some_other_method
    # 省略主体
  end
end
# 差
module Utilities
  extend self

  def parse_something(string)
    # 做一些事情
  end

  def other_utility_method(number, string)
    # 做一些事情
  end
end

# 好
module Utilities
  module_function

  def parse_something(string)
    # 做一些事情
  end

  def other_utility_method(number, string)
    # 做一些事情
  end
end
class Person
  def self.create(options_hash)
    # 省略主体
  end
end
# 差
def foo
  begin
    # 主逻辑
  rescue
    # 异常处理逻辑
  end
end

# 好
def foo
  # 主逻辑
rescue
  # 异常处理逻辑
end
# 差
STATES = ['draft', 'open', 'closed']

# 好
STATES = %w(draft open closed)
# 差
STATES = [:draft, :open, :closed]

# 好
STATES = %i(draft open closed)
# 差
Regexp.last_match[1]

# 好
Regexp.last_match(1)
# 差
html = ''
html += '<h1>Page title</h1>'

paragraphs.each do |paragraph|
  html += "<p>#{paragraph}</p>"
end

# 好 - 并且效率更高
html = ''
html << '<h1>Page title</h1>'

paragraphs.each do |paragraph|
  html << "<p>#{paragraph}</p>"
end
url = 'http://example.com'
str = 'lisp-case-rules'

# 差
url.gsub('http://', 'https://')
str.gsub('-', '_')

# 好
url.sub('http://', 'https://')
str.tr('-', '_')
# 差 - 使用 Powerpack 程序库的 String#strip_margin
code = <<-END.strip_margin('|')
  |def test
  |  some_method
  |  other_method
  |end
END

# 差
code = <<-END
def test
  some_method
  other_method
end
END

# 好
code = <<~END
  def test
    some_method
    other_method
  end
END
# 差
/(first|second)/

# 好
/(?:first|second)/
/(regexp)/ =~ string
...

# 差
process $1

# 好
process Regexp.last_match(1)
string = "some injection\nusername"
string[/^username$/]   # 匹配成功
string[/\Ausername\z/] # 匹配失败
regexp = /
  start         # some text
  \s            # white space char
  (group)       # first group
  (?:alt1|alt2) # some alternation
  end
/x
# 差 - 不存在插值
%(<div class="text">Some text</div>)
# 应当使用 '<div class="text">Some text</div>'

# 差 - 不存在双引号
%(This is #{quality} style)
# 应当使用 "This is #{quality} style"

# 差 - 多行字符串
%(<div>\n<span class="big">#{exclamation}</span>\n</div>)
# 应当使用 heredocs

# 好 - 同时存在插值与双引号,且是单行字符串
%(<tr><td class="name">#{name}</td>)
# 差
name = %q(Bruce Wayne)
time = %q(8 o'clock)
question = %q("What did you say?")

# 好
name = 'Bruce Wayne'
time = "8 o'clock"
question = '"What did you say?"'
quote = %q(<p class='quote'>"What did you say?"</p>)
# 差
%r{\s+}

# 好
%r{^/(.*)$}
%r{^/blog/2011/(.*)$}

避免使用 method_missing。它会使你的调用栈变得凌乱;其方法不被罗列在 #methods 中;拼错的方法可能会默默地工作(nukes.launch_state = false)。优先考虑使用委托、代理、或是 define_method
来替代。如果你必须使用 method_missing 的话,务必做到以下几点: [link]

# 差
def method_missing?(meth, *params, &block)
  if /^find_by_(?<prop>.*)/ =~ meth
    # ... 一堆处理 find_by 的代码
  else
    super
  end
end

# 好
def method_missing?(meth, *params, &block)
  if /^find_by_(?<prop>.*)/ =~ meth
    find_by(prop, *params, &block)
  else
    super
  end
end

# 最好的方式可能是在每个需要支持的属性被声明时,使用 define_method 定义对应的方法
上一篇 下一篇

猜你喜欢

热点阅读