StyleGuide
RubyStyleGuide
error
catch/throw allows you to quickly exit blocks back to a point where a catch is defined for a specific symbol, raise rescue is the real exception handling stuff involving the Exception object.
raise_rescue
raise SomeException, 'message'
# 捕获异常返回 nil
do_something rescue nil
def method
# run code
rescue Timeout::Error
# do something ...
rescue => ex
# do something ...
end
throw_catch
def throw_some
throw :some,'some_throw'
end
def fire
msg = catch(:some) do
# do something ...
throw_some
# do something ...
end
end
swap var
a = 'foo'
b = 'bar'
a, b = b, a
puts a # => 'bar'
puts b # => 'foo'
数组
flatten
def flatten_once!
res = []
each do |e|
[*e].each { |f| res << f }
end
replace(res)
end
获取
# good - use with splat
first, *list = [1, 2, 3, 4] # first => 1, list => [2, 3, 4]
hello_array = *'Hello' # => ["Hello"]
a = *(1..3) # => [1, 2, 3]
||=
||=
# WARNING: would set enabled to true even if it was false
enabled ||= true
&&=
&&=
如果存在则赋值
# bad
something = something ? something.downcase : nil
# ok
something = something.downcase if something
# better(just nil)
something &&= something.downcase
解构
# good - this comma is meaningful for array destructuring
[[1, 2, 3], [4, 5, 6]].map { |a,| a }
# => [
# [0] 1,
# [1] 4
# ]
Class
Class
definitions
class Person
# extend and include go first
extend SomeModule
include AnotherModule
# inner classes
CustomError = Class.new(StandardError)
# constants are next
SOME_CONSTANT = 20
# afterwards we have attribute macros
attr_reader :name
# followed by other macros (if any)
validates :name
# public class methods are next in line
def self.some_method
end
# initialization goes between class methods and other instance methods
def initialize
end
# followed by other public instance methods
def some_method
end
# protected and private methods are grouped near the end
protected
def some_protected_method
end
private
def some_private_method
end
end
Class
structrue
# foo.rb
class Foo
# 30 methods inside
end
# foo/bar.rb
class Foo
class Bar
# 30 methods inside
end
end
# foo/car.rb
class Foo
class Car
# 20 methods inside
end
end
Class
namespace
module Utilities
class Queue
end
end
# bad
class Utilities::Store
Module.nesting # => [Utilities::Store]
def initialize
# Refers to the top level ::Queue class because Utilities isn't in the
# current nesting chain.
@queue = Queue.new
end
end
# good
module Utilities
class WaitingList
Module.nesting # => [Utilities::WaitingList, Utilities]
def initialize
@queue = Queue.new # Refers to Utilities::Queue
end
end
end
to_s
Always supply a proper to_s
method for classes that represent domain objects.
Module
module_function
# good
module Utilities
module_function
def parse_something(string)
# do stuff here
end
def other_utility_method(number, string)
# do some more stuff
end
end
Hash
# bad
hash.keys.each { |k| p k }
hash.values.each { |v| p v }
hash.each { |k, _v| p k }
hash.each { |_k, v| p v }
# good
hash.each_key { |k| p k }
hash.each_value { |v| p v }
获取
heroes = { batman: 'Bruce Wayne', superman: 'Clark Kent' }
# bad
heroes[:supermann] # => nil
# good - fetch raises a KeyError making the problem obvious
heroes.fetch(:supermann)
heroes.fetch(:supermann, 'Defualt Value')
# good - blocks are lazy evaluated, so only triggered in case of KeyError exception
batman.fetch(:powers) { obtain_batman_powers }
转换
# good
{a: 1, b: 2}.transform_values { |v| v * v }
{a: 1, b: 2}.transform_keys { |k| k.to_s }
Number
# good - much easier to parse for the human brain
num = 1_000_000
# bad
a.to_f / b.to_f
# good
a.fdiv(b)
# bad
x == 0.1
x != 0.1
# good - using BigDecimal
x.to_d == 0.1.to_d
# good
tolerance = 0.0001
(x - 0.1).abs < tolerance
# bad
do_something if x >= 1000 && x <= 2000
# good
do_something if (1000..2000).include?(x)
# good
do_something if x.between?(1000, 2000)
String
引号
a = 1
"#{a}" # => "1"
'#{a}' # => "\#{a}"
"'#{a}' test" # => "'1' test"
new
# bad
email_with_name = user.name + ' <' + user.email + '>'
# good
email_with_name = "#{user.name} <#{user.email}>"
拼接
When you need to construct large data chunks, use String#<<
. String#+
creates a bunch of new string objects.
html = ''
html += '<h1>Page title</h1>'
# bad
paragraphs.each do |paragraph|
html += "<p>#{paragraph}</p>"
end
# good and also fast
paragraphs.each do |paragraph|
html << "<p>#{paragraph}</p>"
end
同时也不应该在长字符串中使用+
,因为这会创建多个字符串
# bad
str = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. " +
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, " +
"when an unknown printer took a galley of type and scrambled it to make a type."
# good
str = <<~LOREM
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a type.
LOREM
长字符替换
str = <<~LOREM.gsub('Lorem','AAAAA')
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a type.
LOREM
# good
foo(<<~SQL)
select foo from bar
SQL
替换
url = 'http://example.com'
str = 'lisp-case-rules'
# bad
url.gsub('http://', 'https://')
str.gsub('-', '_')
# good
url.sub('http://', 'https://')
str.tr('-', '_')
匹配
foo = 'I am an example string'
# bad - using a regular expression is an overkill here
foo ~= /example/
# good
foo['example']
foo[/a.*s/]
# bad
/(regexp)/ =~ string
# some code
process Regexp.last_match(1)
# good
/(?<meaningful_var>regexp)/ =~ string
# some code
process meaningful_var
str = "111 100a 11bb"
r = %r|\w{1,2}\d{2,5}| # => /\w{1,2}\d{2,5}/
str.match(r) # => #<MatchData "111">
r.match(str) # => #<MatchData "111">
ruby
中的 Regex
## 字符匹配
[0-9a-zA-Z_] => \w
[0-9] => \d
white space => \s # (tabs, regular space, newline)
# 对应的取反可用大写字母
^[0-9a-zA-Z_] => \W
^[0-9] => \D
not white space => \S # (tabs, regular space, newline)
## 分组匹配的字符串可以用数组下标或者命名来获取
m = "David 30".match /(?<name>\w+) (?<age>\d+)/
m.to_a
# => ["David 30", "David", "30"]
# m[1] == m[name] => 'David'
## 位置锚定
str = 'aa bb 1a432 ll nnn* 123 fff __'
# 匹配以【一个字母后跟着一个空格的字符串】开头的【一个或多个字母】
str[/(?<=\w )(\w+)/] # => "bb"
# 匹配【一个或多个字母】且后面跟着【一个空格后面跟着一个或多个数字的字符串】
str[/(\w+)(?= \d+)/] # => "bb"
# 匹配【不】以【一个字母后跟着一个空格】开头的【一个或多个数字】
str[/(?<!\w )(\d+)/] # => "432"
# 匹配【一个或多个字母】且后面【不】跟着【一个字母或者一个空格】
str[/(\w+)(?![\w ])/] # => "ooo"
## Options
/[a-z]/i # => 忽略【正则】中的大小写进行匹配
m # => 不匹配新行
/ab c/x # => 忽略【正则】中的空格进行匹配(这里匹配 ’abc‘)
RailsStyleGuide
Macro Style Methods
class User < ActiveRecord::Base
# keep the default scope first (if any)
default_scope { where(active: true) }
# constants come up next
COLORS = %w(red green blue)
# afterwards we put attr related macros
attr_accessor :formatted_date_of_birth
attr_accessible :login, :first_name, :last_name, :email, :password
# Rails 4+ enums after attr macros
enum role: { user: 0, moderator: 1, admin: 2 }
# followed by association macros
belongs_to :country
has_many :authentications, dependent: :destroy
# and validation macros
validates :email, presence: true
validates :username, presence: true
validates :username, uniqueness: { case_sensitive: false }
validates :username, format: { with: /\A[A-Za-z][A-Za-z0-9._-]{2,19}\z/ }
validates :password, format: { with: /\A\S{8,128}\z/, allow_nil: true }
# next we have callbacks in the order in which they will be executed
before_save :cook
after_commit :after_commit_callback
# other macros (like devise's) should be placed after the callbacks
...
end
最后更新于