Skip to content

Latest commit

 

History

History
3896 lines (3095 loc) · 94.6 KB

README-viVN.md

File metadata and controls

3896 lines (3095 loc) · 94.6 KB

Lời giới thiệu

Các mô hình về vai trò là rất quan trọng.
-- Alex J. Murphy / thành viên RoboCop

Một điều luôn làm tôi suy nghĩ với vai trò của một Ruby coder - Các Python coder có một tài liệu gần như là chuẩn mực về phong cách viết code (PEP-8) và chúng ta, những Ruby coder, vẫn chưa có một tài liệu chính thức hướng dẫn về phong cách viết Ruby cũng như những mô hình thực hành tốt nhất. Và tôi tin rằng cần một phong cách viết code tốt. Tôi cũng tin rằng một cộng đồng lập trình lớn như cộng đồng Ruby, hẳn có đủ năng lực viết ra những tài liệu cần thiết này.

Bản tài liệu này ban đầu được viết để dùng hướng dẫn cách viết code Ruby cho nội bộ công ty chúng tôi (với sự tin tưởng từ các bạn). Trong một số thời điểm, tôi quyết định rằng công việc tôi đang làm hẳn là rất thú vị với các thành viên của cộng đồng Ruby nói chung và nó cũng cần thiết cho nội bộ các công ty khác. Và thế giới sẽ được hưởng lợi từ mô hình dựa vào cộng đồng và phản biện từ cộng đồng sẽ giúp tìm ra những bài học áp dụng, những thành ngữ và những quy định viết code Ruby.

Từ khi bắt đầu viết, tôi đã nhận được rất nhiều phản hồi, đặc biệt là các thành viên thuộc cộng đồng Ruby trên thế giới. Xin cảm ơn các bạn đã gợi ý và hỗ trợ cho tôi! Cùng nhau làm việc, chúng ta có viết ra cho một nguồn tư liệu mang lại nhiều lợi ích cho mỗi nhà phát triển code Ruby.

Bằng cách này, nếu bạn đang tìm hiểu Rails, bạn có thể muốn tham khảo thêm tài liệu Hướng dẫn viết code Ruby on Rails 3 & 4 / Ruby on Rails 3 & 4 Style Guide.

Hướng dẫn cách viết code Ruby (Ruby Style)

Bản hướng dẫn cách viết code Ruby giới thiệu những phương án thực hành tốt nhất mà các lập trình viên trong thế giới Ruby đã viết và được cập nhật, chỉnh sửa bởi các thành viên khác. Bản hướng dẫn vừa đưa ra phương án sử dụng thực tế tốt nhất, vừa đề cập cả những ý tưởng đã bị nhận xét là không tối ưu. Việc này sẽ giúp tránh được rủi ro cho tất cả mọi người - Việc này hẳn cũng tốt đấy chứ.

Bản hướng dẫn này được chia thành các phần bao gồm các quy tắc có liên quan đến nhau. Tôi đã cố gắng để thêm các quy tắc liên quan đằng sau (Tôi cho rằng nếu các bạn không nhất thiết phải xem, có thể bỏ qua những quy tắc đó).

Tôi không vô cớ mà đưa ra những quy tắc viết code này - chúng đều dựa trên những kinh nghiệm sâu sắc trong nghề của tôi khi làm kỹ sư phát triển phần mềm chuyên nghiệp. Các quy tắc cũng được tổng hợp từ những kiến nghị và gợi ý từ các thành viên khác, cũng như những nguồn tài liệu về lập trình Ruby được đánh giá cao, như là "Lập trình Ruby 1.9 / Programming Ruby 1.9""Ngôn ngữ lập trình Ruby / The Ruby Programming Language".

Bản hướng dẫn này vẫn đang tiếp tục được cập nhật - một số quy tắc đang thiếu các ví dụ mình hoạ, một số ví dụ minh họa thì không được rõ ý. Những vấn đề này sẽ được giải quyết trong thời gian tới - hãy tin là như vậy.

Bạn có thể xuất ra file PDF hay bản HTML nhờ sử dụng các công cụ Transmuter.

RuboCop là bộ phân tích mã code, dựa trên bản hướng dẫn này.

Các bản dịch ở các ngôn ngữ khác:

Mục lục

Bố cục trình bày khi viết code / Source code layout

Gần đây, mọi người quả quyết rằng phong cách code của tất cả mọi người đều rất tởm và không thể đọc được, ngoại trừ chính họ. Nếu bỏ đi cụm từ "ngoại trừ chính họ" thì có lẽ rằng họ đã gần đúng :D...
-- Jerry Coffin

  • Sử dụng chuẩn mã hóa UTF-8 khi code. [link]

  • Dùng hai (02) Khoảng trắng(spaces) cho mỗi tầng thụt đầu dòng (hay còn gọi là tab mềm (soft tabs)). Không sử dụng tab cứng (hard tabs). [link]

    # bad - four spaces
    def some_method
        do_something
    end
    
    # good
    def some_method
      do_something
    end
  • Sử dụng dấu cuối dòng theo Unix-style. (*Người dùng BSD/Solaris/Linux/OS X được kích hoạt sẵn, người dùng Windows cần phải để ý.) [link]

    • Nếu bạn đang dùng Git, bạn nên cấu hình như sau để chặn các dấu cuối dòng của Windows:

      $ git config --global core.autocrlf true
  • Không dùng dấu chấm phẩy (;) để ngăn cách các câu lệnh và biểu thức, mà hãy viết mỗi câu 1 dòng. [link]

    # bad
    puts 'foobar'; # Thừa dấu chấm phẩy
    
    puts 'foo'; puts 'bar' # hai câu lệnh trên cùng 1 dòng
    
    # good
    puts 'foobar'
    
    puts 'foo'
    puts 'bar'
    
    puts 'foo', 'bar' # câu lệnh này sẽ tự tách làm hai lệnh `puts`
  • Nếu lớp không có nội dung, nên viết trên một dòng. [link]

    # bad
    class FooError < StandardError
    end
    
    # okish
    class FooError < StandardError; end
    
    # good
    FooError = Class.new(StandardError)
  • Tránh kiểu viết gom phương thức vào một dòng. Mặc dù ở một vài nơi nó vẫn được sử dụng, có một vài điểm đặc thù khiến người ta cảm thấy khó chịu khi gặp nó. Dù sao vẫn không nền viết nhiều hơn một biểu thức, câu lệnh trên một hàng. [link]

    # bad
    def too_much; something; something_else; end
    
    # okish - dấu ; ngay sau tên hàm bắt buộc phải có
    def no_braces_method; body end
    
    # okish - dấu ; sau body thì không bắt buộc
    def no_braces_method; body; end
    
    # okish - đúng cú pháp, nhưng không có ; sau tên hàm khiến khó đọc hơn
    def some_method() body end
    
    # good
    def some_method
      body
    end

    Ngoại lệ: phương thức không có body thì nên viết trên một hàng

    # good
    def no_op; end
  • Đặt khoảng trắng trước và sau toán tử , sau dấu phẩy, dấu hai chấm và dấu chấm phẩy. Khoảng trắng có thể (hầu hết) là không bắt buộc, nhưng nó sẽ giúp code dễ đọc, dễ viết hơn. [link]

    sum = 1 + 2
    a, b = 1, 2
    class FooError < StandardError; end

    Ngoại lệ duy nhất, liên quan đến các toán tử, là toán tử mũ:

    # bad
    e = M * c ** 2
    
    # good
    e = M * c**2
  • KHÔNG DÙNG khoảng trắng sau (, [ hay trước ], ). DÙNG khoảng trắng quanh { và trước }. [link]

    # bad
    some( arg ).other
    [ 1, 2, 3 ].each{|e| puts e}
    
    # good
    some(arg).other
    [1, 2, 3].each { |e| puts e }

    {} cần được làm rõ một chút, nó được dùng cho cả block, hash và dùng để nhúng biến vào string.

    Với hash, có hai cách viết được sử dụng:

    • Cách thứ nhất dễ đọc hơn, và thường được sử dụng nhiều hơn trong cộng đồng Ruby.
    • Cách thứ hai có điểm mạnh là nó dùng để phân biệt giữa block và hash. Cho dù bạn chọn cách nào, thì cũng nên theo một cách thôi.
    # good - khoảng trắng trước { và sau }
    { one: 1, two: 2 }
    
    # good - không có khoảng trắng trước { và sau }
    {one: 1, two: 2}

    Khi dùng để nhúng vào string, không nên dùng khoảng trắng giữa cặp ngoặc nhọn.

    # bad
    "From: #{ user.first_name }, #{ user.last_name }"
    
    # good
    "From: #{user.first_name}, #{user.last_name}"
  • Không có khoảng trắng sau !. [link]

    # bad
    ! something
    
    # good
    !something
  • Không dùng khoảng trắng khi khai báo khoảng (range). [link]

    # bad
    1 .. 3
    'a' ... 'z'
    
    # good
    1..3
    'a'...'z'
  • whencase thụt đầu dòng cùng cấp. Style này được khuyến nghị trong cả "The Ruby Programming Language" và "Programming Ruby". [link]

    # bad
    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
    
    # good
    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
  • Khi gán kết quả của một biểu thức cho một biến, cặp case, when hay if,else cũng phải cùng cấp. [link]

    # bad - pretty convoluted
    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
    
    # good - it's apparent what's going on
    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
    
    # good (and a bit more width efficient)
    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
  • Thêm một dòng trống giữa các phương thức, và các nhóm xử lý logic. [link]

    def some_method
      data = initialize(options)
    
      data.manipulate!
    
      data.result
    end
    
    def some_method
      result
    end
  • KHÔNG DÙNG dấu phẩy sau tham số cuối cùng khi khai báo phương thức, đặc biệt khi những tham số không nằm trên những dòng riêng biệt. [link]

    # bad - cách làm này giúp dễ thêm/xóa/di chuyển tham số,
    # nhưng không khuyến khích dùng
    some_method(
                 size,
                 count,
                 color,
               )
    
    # bad
    some_method(size, count, color, )
    
    # good
    some_method(size, count, color)
  • Dùng khoảng trắng quanh toán tử = khi gán giá trị mặc định: [link]

    # bad
    def some_method(arg1=:default, arg2=nil, arg3=[])
      # do something...
    end
    
    # good
    def some_method(arg1 = :default, arg2 = nil, arg3 = [])
      # do something...
    end

    Trong khi một số sách về Ruby khuyên dùng cách một, nhưng cộng đồng lại thích dùng cách hai nhiều hơn.

  • Tránh dùng dấu ngắt dòng \ khi không bắt buộc. Thực tế, chỉ dùng nó khi cần ngắt dòng string thôi. [link]

    # bad
    result = 1 - \
             2
    
    # good (but still ugly as hell)
    result = 1 \
             - 2
    
    long_string = 'First part of the long string' \
                  ' and second part of the long string'
  • Với cách gọi phương thức nhiều lần liên tiếp, có hai kiểu thông dụng, kiểu nào cũng được cả: . ở đầu dòng mới (Phương án A) . ở cuối dòng cũ (Phương án B). Mặc dù dùng kiểu nào thì cũng nên dùng nhất quán một kiểu. [link]

    • (Phương án A)

      one.two.three
         .four
    • (Phương án B)

      one.two.three.
         four

    Xem thêm về cuộc thảo luận chọn cách nào: tại đây.

  • Khi gọi phương thức có nhiều đối số, nên xuống dòng, và các đối số này cần thụt đầu dòng bằng nhau và ở cùng cấp với đối số đầu tiên. Nếu đối số này dài thì có thể dùng cách thụt đầu dòng bình thường. [link]

    # mẫu (dòng quá dài)
    def send_mail(source)
      Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text)
    end
    
    # bad (thụt đầu dòng hai lần)
    def send_mail(source)
      Mailer.deliver(
          to: 'bob@example.com',
          from: 'us@example.com',
          subject: 'Important message',
          body: source.text)
    end
    
    # good
    def send_mail(source)
      Mailer.deliver(to: 'bob@example.com',
                     from: 'us@example.com',
                     subject: 'Important message',
                     body: source.text)
    end
    
    # good (thụt đầu dòng bình thường)
    def send_mail(source)
      Mailer.deliver(
        to: 'bob@example.com',
        from: 'us@example.com',
        subject: 'Important message',
        body: source.text
      )
    end
  • Nếu mảng có nhiều phần tử, nên đưa xuống dòng và thụt đầu dòng cùng cấp. [link]

    # bad
    menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
      'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
    
    # good
    menu_item = [
      'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
      'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'
    ]
    
    # good
    menu_item =
      ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
       'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
  • Với số lớn, thêm dấu gạch dưới _ cho dễ đọc. [link]

    # bad - có bao nhiêu số `0`?
    num = 1000000000
    
    # good - dễ đọc hơn hẳn đúng không :P
    num = 1_000_000_000
  • Sử dụng Rdoc và convention của nó để làm API documentation. KHÔNG đặt khoảng trắng giữa khối comment và từ khóa def. [link]

  • Giới hạn dòng ở 80 ký tự. [link]

  • Tránh dùng khoảng trắng thừa (thường gặp ở cuối dòng). [link]

  • Cuối file nên có thêm một dòng trống. [link]

  • KHÔNG DÙNG block comments. [link]

    # bad
    =begin
    comment line
    another comment line
    =end
    
    # good
    # comment line
    # another comment line

Cú pháp / Syntax

  • CHỈ sử dụng :: cho hằng số (của cả classes và modules) và các hàm khởi tạo (constructors) (vd: Array() hoặc Nokogiri::HTML()). KHÔNG sử dụng :: cho các lời gọi hàm thông thường. [link]

    # bad
    SomeClass::some_method
    some_object::some_method
    
    # good
    SomeClass.some_method
    some_object.some_method
    SomeModule::SomeClass::SOME_CONST
    SomeModule::SomeClass()
  • Chỉ dùng () khi khai báo phương thức có tham số. [link]

    # bad
    def some_method()
      # body omitted
    end
    
    # good
    def some_method
      # body omitted
    end
    
    # bad
    def some_method_with_parameters param1, param2
      # body omitted
    end
    
    # good
    def some_method_with_parameters(param1, param2)
      # body omitted
    end
  • Nếu phương thức có tham số mặc định, đặt nó ở cuối cùng. Nếu đặt trước một tham số khác thì hậu quả thật là khôn lường :3 Ruby nó hành xử vui lắm :P [link]

    # bad
    def some_method(a = 1, b = 2, c, d)
      puts "#{a}, #{b}, #{c}, #{d}"
    end
    
    some_method('w', 'x') # => '1, 2, w, x'
    some_method('w', 'x', 'y') # => 'w, 2, x, y'
    some_method('w', 'x', 'y', 'z') # => 'w, x, y, z'
    
    # good
    def some_method(c, d, a = 1, b = 2)
      puts "#{a}, #{b}, #{c}, #{d}"
    end
    
    some_method('w', 'x') # => '1, 2, w, x'
    some_method('w', 'x', 'y') # => 'y, 2, w, x'
    some_method('w', 'x', 'y', 'z') # => 'y, z, w, x'
  • TRÁNH khai báo biến song song, sẽ gây khó khăn khi đọc. Khai báo biến song song chỉ dùng khi trả kết quả phương thức, dùng với toán tử splat, hay dùng để hoán đổi giá trị hai biến. NÊN khai báo mỗi biến một hàng. [link]

    # bad
    a, b, c, d = 'foo', 'bar', 'baz', 'foobar'
    
    # good
    a = 'foo'
    b = 'bar'
    c = 'baz'
    d = 'foobar'
    
    # good - hoán đổi giá trị hai biến
    a = 'foo'
    b = 'bar'
    
    a, b = b, a
    puts a # => 'bar'
    puts b # => 'foo'
    
    # good - method return
    def multi_return
      [1, 2]
    end
    
    first, second = multi_return
    
    # good - dùng với splat
    first, *list = [1, 2, 3, 4] # first => 1, list => [2, 3, 4]
    
    hello_array = *'Hello' # => ["Hello"]
    
    a = *(1..3) # => [1, 2, 3]
  • Tránh dùng dấu gạch dưới thừa thãi trong khai báo song song. Nên dùng _ với tên biến cho rõ nghĩa. _ cần thiết khi có một biến splat, và biến này thì không cần _. [link]

    # bad
    foo = 'one,two,three,four,five'
    # Phép gán không cần thiết thì không cung cấp bất kỳ thông tin hữu ích nào
    first, second, _ = foo.split(',')
    first, _, _ = foo.split(',')
    first, *_ = foo.split(',')
    
    
    # good
    foo = 'one,two,three,four,five'
    # Dấu `_` được dùng khi ta cần lấy hết ra, trừ phần tử cuối cùng.
    *beginning, _ = foo.split(',')
    *beginning, something, _ = foo.split(',')
    
    a, = foo.split(',')
    a, b, = foo.split(',')
    # Việc gán cho biến không sử dụng là không cần thiết,
    # nhưng nó cung cấp thông tin hữu ích cho ta.
    first, _second = foo.split(',')
    first, _second, = foo.split(',')
    first, *_ending = foo.split(',')
  • Đừng dùng for, trừ khi có lý do chính đáng. Thay vào đó hãy dùng vòng lặp. for là một dạng của each, nhưng for không hỗ trợ scope (each thì có) và biến trong for thì có thể truy cập từ bên ngoài block. [link]

    arr = [1, 2, 3]
    
    # bad
    for elem in arr do
      puts elem
    end
    
    # ra khỏi vòng `for` ta vẫn truy cập biến `elem` được
    elem # => 3
    
    # good
    arr.each { |elem| puts elem }
    
    elem # => NameError: undefined local variable or method `elem'
  • KHÔNG dùng then trong if/unless nhiều cấp. [link]

    # bad
    if some_condition then
      # body omitted
    end
    
    # good
    if some_condition
      # body omitted
    end
  • LUÔN đặt điều kiện cùng cấp với if/unless. [link]

    # bad
    if
      some_condition
      do_something
      do_something_else
    end
    
    # good
    if some_condition
      do_something
      do_something_else
    end
  • NÊN dùng toán tử ba ngôi (?:) hơn là cấu trúc if/then/else/end. Nó thông dụng hơn và giúp code ngắn gọn súc tích hơn. [link]

    # bad
    result = if some_condition then something else something_else end
    
    # good
    result = some_condition ? something : something_else
  • KHÔNG nên dùng toán tử ba ngôi lồng nhau. Trong trường hợp này thì nên dùng if/else. [link]

    # bad
    some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
    
    # good
    if some_condition
      nested_condition ? nested_something : nested_something_else
    else
      something_else
    end
  • KHÔNG dùng if x; .... Trong trường hợp này thì dùng toán tử ba ngôi. [link]

    # bad
    result = if some_condition; something else something_else end
    
    # good
    result = some_condition ? something : something_else
  • Để ý rằng if case case có trả về kết quả. [link]

    # bad
    if condition
      result = x
    else
      result = y
    end
    
    # good
    result =
      if condition
        x
      else
        y
      end
  • DÙNG when x then ... cho một-dòng cases. Cú pháp when x: ... bị remove từ Ruby 1.9. [link]

  • KHÔNG dùng when x; .... Xem mục ở trên. [link]

  • DÙNG ! thay cho not. [link]

    # bad - parentheses are required because of op precedence
    x = (not something)
    
    # good
    x = !something
  • Tránh dùng !!. [link]

    # bad
    x = 'test'
    # obscure nil check
    if !!x
      # body omitted
    end
    
    x = false
    # double negation is useless on booleans
    !!x # => false
    
    # good
    x = 'test'
    unless x.nil?
      # body omitted
    end
  • KHÔNG dùng từ khóa andor. Thay vào đó hãy dùng &&||. [link]

    # bad
    # boolean expression
    if some_condition and some_other_condition
      do_something
    end
    
    # control flow
    document.saved? or document.save!
    
    # good
    # boolean expression
    if some_condition && some_other_condition
      do_something
    end
    
    # control flow
    document.saved? || document.save!
  • Tránh dùng ?: nhiều dòng; thay vào đó hãy dùng if/unless. [link]

  • Dùng if/unless cho single-line body. [link]

    # bad
    if some_condition
      do_something
    end
    
    # good
    do_something if some_condition
    
    # another good option
    some_condition && do_something
  • Tránh dùng if/unless ở cuối block khi không cần thiết. [link]

    # bad
    10.times do
      # multi-line body omitted
    end if some_condition
    
    # good
    if some_condition
      10.times do
        # multi-line body omitted
      end
    end
  • Tránh dùng if/unless/while/until lồng nhau. Nên dùng chung &&/|| nếu thích hợp. [link]

    # bad
    do_something if other_condition if some_condition
    
    # good
    do_something if some_condition && other_condition
  • Nếu điều kiện là false, dùng unless thay vì if not. Hoặc dùng || cũng được. [link]

    # bad
    do_something if !some_condition
    
    # bad
    do_something if not some_condition
    
    # good
    do_something unless some_condition
    
    # another good option
    some_condition || do_something
  • KHÔNG dùng unless cùng với else. Thay vào đó hãy viết với biểu thức true. [link]

    # bad
    unless success?
      puts 'failure'
    else
      puts 'success'
    end
    
    # good
    if success?
      puts 'success'
    else
      puts 'failure'
    end
  • KHÔNG dùng cặp ngoặc tròn () với if/unless/while/until. [link]

    # bad
    if (x > 10)
      # body omitted
    end
    
    # good
    if x > 10
      # body omitted
    end

Tuy nhiên, có một ngoại lệ, có tên là safe assignment in condition.

  • KHÔNG dùng while/until condition do cho while/until nhiều dòng. [link]

    # bad
    while x > 5 do
      # body omitted
    end
    
    until x > 5 do
      # body omitted
    end
    
    # good
    while x > 5
      # body omitted
    end
    
    until x > 5
      # body omitted
    end
  • Đặt while/until ở sau trong trường hợp body một dòng. [link]

    # bad
    while some_condition
      do_something
    end
    
    # good
    do_something while some_condition
  • Ưu tiên until hơn while cho biểu thức false. [link]

    # bad
    do_something while !some_condition
    
    # good
    do_something until some_condition
  • Dùng Kernel#loop thay vì while/until khi cần lặp vô hạn. [link]

    # bad
    while true
      do_something
    end
    
    until false
      do_something
    end
    
    # good
    loop do
      do_something
    end
  • Dùng Kernel#loop cùng với break thay vì begin/end/until hay begin/end/while kiểu lặp trước, kiểm tra sau. [link]

    # bad
    begin
      puts val
      val += 1
    end while val < 0
    
    # good
    loop do
      puts val
      val += 1
      break unless val < 0
    end
  • Với các phương thức mặc định của DSL (vd: Rake, Rails, RSpec), hay các phương thức có chứa "từ khóa" trong Ruby (vd: attr_reader, puts) và các phương thức truy cập thuộc tính thì không dùng (). Còn lại là dùng hết. [link]

    class Person
      # bad
      attr_reader(:name, :age)
      # good
      attr_reader :name, :age
    
      # body omitted
    end
    
    # bad
    temperance = Person.new 'Temperance', 30
    # good
    temperance = Person.new('Temperance', 30)
    
    # bad
    puts(temperance.age)
    # good
    puts temperance.age
    
    # bad
    x = Math.sin y
    # good
    x = Math.sin(y)
    
    # bad
    array.delete e
    # good
    array.delete(e)
    
    # bad
    expect(bowling.score).to eq 0
    # good
    expect(bowling.score).to eq(0)
  • Nếu hash đã tường minh rồi thì không cần cặp {} nữa. [link]

    # bad
    user.set({ name: 'John', age: 45, permissions: { read: true } })
    
    # good
    user.set(name: 'John', age: 45, permissions: { read: true })
  • Nếu phương thức hệ thống thì có thể bỏ luôn cả (){}. [link]

    class Person < ActiveRecord::Base
      # bad
      validates(:name, { presence: true, length: { within: 1..10 } })
    
      # good
      validates :name, presence: true, length: { within: 1..10 }
    end
  • Gọi phương thức không có tham số thì bỏ luôn (). [link]

    # bad
    Kernel.exit!()
    2.even?()
    fork()
    'test'.upcase()
    
    # good
    Kernel.exit!
    2.even?
    fork
    'test'.upcase
  • Nếu block chỉ làm một việc thì ưu tiên dùng shorthand. [link]

    # bad
    names.map { |name| name.upcase }
    
    # good
    names.map(&:upcase)
  • Ưu tiên {...} hơn do...end cho block một dòng. Tránh dùng {...} cho block nhiều dòng. Luôn dùng do...end cho khối điều khiển và khai báo phương thức. [link]

    names = %w(Bozhidar Steve Sarah)
    
    # bad
    names.each do |name|
      puts name
    end
    
    # good
    names.each { |name| puts name }
    
    # bad
    names.select do |name|
      name.start_with?('S')
    end.map { |name| name.upcase }
    
    # good
    names.select { |name| name.start_with?('S') }.map(&:upcase)
  • Cân nhắc sử dụng đối số cho block tường minh để tránh việc viết block mà chỉ truyền các đối số của nó cho một block.. [link]

    require 'tempfile'
    
    # bad
    def with_tmp_dir
      Dir.mktmpdir do |tmp_dir|
        Dir.chdir(tmp_dir) { |dir| yield dir }  # block just passes arguments
      end
    end
    
    # good
    def with_tmp_dir(&block)
      Dir.mktmpdir do |tmp_dir|
        Dir.chdir(tmp_dir, &block)
      end
    end
    
    with_tmp_dir do |dir|
      puts "dir is accessible as a parameter and pwd is set: #{dir}"
    end
  • Tránh dùng return, chỉ nên dùng trong luồng điều khiển if..else chẳng hạn. [link]

    # bad
    def some_method(some_arr)
      return some_arr.size
    end
    
    # good
    def some_method(some_arr)
      some_arr.size
    end
  • Tránh dùng self khi không bắt buộc. Chỉ dùng nó khi cần ghi giá trị vào biến. [link]

    # bad
    def ready?
      if self.last_reviewed_at > self.last_updated_at
        self.worker.update(self.content, self.options)
        self.status = :in_progress
      end
      self.status == :verified
    end
    
    # good
    def ready?
      if last_reviewed_at > last_updated_at
        worker.update(content, options)
        self.status = :in_progress
      end
      status == :verified
    end
  • As a corollary, avoid shadowing methods with local variables unless they are both equivalent. [link]

    class Foo
      attr_accessor :options
    
      # ok
      def initialize(options)
        self.options = options
        # both options and self.options are equivalent here
      end
    
      # bad
      def do_something(options = {})
        unless options[:when] == :later
          output(self.options[:message])
        end
      end
    
      # good
      def do_something(params = {})
        unless params[:when] == :later
          output(options[:message])
        end
      end
    end
  • Không dùng kết quả của phép gán = trong biểu thức điều kiện nếu như không có cặp ngoặc tròn. Xem thêm safe assignment in condition. [link]

    # bad (+ a warning)
    if v = array.grep(/foo/)
      do_something(v)
      # some code
    end
    
    # good (MRI would still complain, but RuboCop won't)
    if (v = array.grep(/foo/))
      do_something(v)
      # some code
    end
    
    # good
    v = array.grep(/foo/)
    if v
      do_something(v)
      # some code
    end
  • Dùng kiểu gán rút gọn khi có thể [link]

    # bad
    x = x + y
    x = x * y
    x = x**y
    x = x / y
    x = x || y
    x = x && y
    
    # good
    x += y
    x *= y
    x **= y
    x /= y
    x ||= y
    x &&= y
  • Dùng ||= để khởi tạo giá trị khi biến đó chưa được khởi tạo [link]

    # bad
    name = name ? name : 'Bozhidar'
    
    # bad
    name = 'Bozhidar' unless name
    
    # good - set name to 'Bozhidar', only if it's nil or false
    name ||= 'Bozhidar'
  • Không dùng ||= để khởi tạo biến boolean. (Xét trường hợp biến đó mang giá trị false sẵn rồi.) [link]

    # bad - would set enabled to true even if it was false
    enabled ||= true
    
    # good
    enabled = true if enabled.nil?
  • Dùng &&= để tiền xử lý biến khi nó có thể tồn tại hoặc không. Việc dùng &&= sẽ chỉ thay đổi giá trị của biến khi nó đã tồn tại. Không cần thiết phải kiểm tra xem biến tồn tại hay chưa. [link]

    # bad
    if something
      something = something.downcase
    end
    
    # bad
    something = something ? something.downcase : nil
    
    # ok
    something = something.downcase if something
    
    # good
    something = something && something.downcase
    
    # better
    something &&= something.downcase
  • Tránh việc ngầm định việc sử dụng của toán tử ===. [link]

    # bad
    Array === something
    (1..100) === 7
    /something/ === some_string
    
    # good
    something.is_a?(Array)
    (1..100).include?(7)
    some_string =~ /something/
  • Tránh dùng eql?, hãy dùng ==. [link]

    # bad - eql? is the same as == for strings
    'ruby'.eql? some_str
    
    # good
    'ruby' == some_str
    1.0.eql? x # eql? makes sense here if want to differentiate between Integer and Float 1
  • Tránh sử dụng các biến đặc biệt kiểu Perl (như $:, $;,.. ). Chúng khá là khó hiểu và cách sử dụng chúng trong bất cứ tình huống nào ngoài các đoạn code một dòng thì không được khuyến khích. Sử dụng các alias thân thiện với con người được cung cấp bởi thư viện tiếng Anh [link]

    # bad
    $:.unshift File.dirname(__FILE__)
    
    # good
    require 'English'
    $LOAD_PATH.unshift File.dirname(__FILE__)
  • Không dùng khoảng trắng giữa tên hàm và dấu ngoặc mở ( [link]

    # bad
    f (3 + 2) + 1
    
    # good
    f(3 + 2) + 1
  • Nếu đối số đầu tiên của phương thức có dùng ngoặc tròn thì lời gọi phương thức phải có ngoặc tròn. Vd: f((3 + 2) + 1). [link]

  • Luôn chạy trình biên dịch Ruby với tuy chọn -w, nó sẽ cảnh báo nếu ta quên một quy tắc nào ở trên. [link]

  • Không sử dụng các định nghĩa phương thức lồng nhau, thay vào đó hãy dùng lambda. Các định nghĩa phương thức lồng nhau thật ra đưa các phương thức trong cùng phạm vi (ví dụ lớp) như là một phương thức bên ngoài. Hơn nữa, các phương thức lồng sẽ bị định nghĩa lại mỗi lần phương thức chứa nó được gọi. [link]

    # bad
    def foo(x)
      def bar(y)
        # body omitted
      end
    
      bar(x)
    end
    
    # good - the same as the previous, but no bar redefinition on every foo call
    def bar(y)
      # body omitted
    end
    
    def foo(x)
      bar(x)
    end
    
    # also good
    def foo(x)
      bar = ->(y) { ... }
      bar.call(x)
    end
  • Dùng cú pháp lambda mới cho block có một dòng. Nếu nhiều dòng thì dùng từ khóa lambda. [link]

    # bad
    l = lambda { |a, b| a + b }
    l.call(1, 2)
    
    # correct, but looks extremely awkward
    l = ->(a, b) do
      tmp = a * 7
      tmp * b / 50
    end
    
    # good
    l = ->(a, b) { a + b }
    l.call(1, 2)
    
    l = lambda do |a, b|
      tmp = a * 7
      tmp * b / 50
    end
  • Khi dùng lambda với biến, nên đặt biến trong ngoặc tròn. [link]

    # bad
    l = ->x, y { something(x, y) }
    
    # good
    l = ->(x, y) { something(x, y) }
  • Lambda không có tham số thì bỏ cặp ngoặc tròn đi. [link]

    # bad
    l = ->() { something }
    
    # good
    l = -> { something }
  • Ưu tiên proc hơn Proc.new. [link]

    # bad
    p = Proc.new { |n| puts n }
    
    # good
    p = proc { |n| puts n }
  • Ưu tiên proc.call() hơn proc[] hay proc.() cho cả lambdas và procs. [link]

    # bad - looks similar to Enumeration access
    l = ->(v) { puts v }
    l[1]
    
    # also bad - uncommon syntax
    l = ->(v) { puts v }
    l.(1)
    
    # good
    l = ->(v) { puts v }
    l.call(1)
  • Dùng tiền tố _ cho biến không dùng trong block và biến cục bộ. Hoặc chỉ dùng _ cũng được. [link]

    # bad
    result = hash.map { |k, v| v + 1 }
    
    def something(x)
      unused_var, used_var = something_else(x)
      # some code
    end
    
    # good
    result = hash.map { |_k, v| v + 1 }
    
    def something(x)
      _unused_var, used_var = something_else(x)
      # some code
    end
    
    # good
    result = hash.map { |_, v| v + 1 }
    
    def something(x)
      _, used_var = something_else(x)
      # some code
    end
  • Dùng $stdout/$stderr/$stdin thay cho STDOUT/STDERR/STDIN. STDOUT/STDERR/STDIN là những hằng số, và ta có thẻ thay đổi mấy hằng số đó. [link]

  • Dùng warn thay cho $stderr.puts. Dùng warn sẽ rõ nghĩa hơn. Đồng thời cho phép ta bỏ qua warning nếu cần (bằng cách set warn level về 0 thông qua -W0). [link]

  • Ưu tiên dùng sprintf và bí danh format của nó hơn là phương thức String#%. [link]

    # bad
    '%d %d' % [20, 10]
    # => '20 10'
    
    # good
    sprintf('%d %d', 20, 10)
    # => '20 10'
    
    # good
    sprintf('%{first} %{second}', first: 20, second: 10)
    # => '20 10'
    
    format('%d %d', 20, 10)
    # => '20 10'
    
    # good
    format('%{first} %{second}', first: 20, second: 10)
    # => '20 10'
  • Ưu tiên dùng Array#join hơn là Array#* với tham số là string. [link]

    # bad
    %w(one two three) * ', '
    # => 'one, two, three'
    
    # good
    %w(one two three).join(', ')
    # => 'one, two, three'
  • Dùng Array() thay vì kiểm tra Array một cách tường minh hay [*var], [link]

    # bad
    paths = [paths] unless paths.is_a? Array
    paths.each { |path| do_something(path) }
    
    # bad (always creates a new Array instance)
    [*paths].each { |path| do_something(path) }
    
    # good (and a bit more readable)
    Array(paths).each { |path| do_something(path) }
  • Khi xử lý logic dải mà phức tạp, dùng dải (range) hoặc Comparable#between? khi có thể. [link]

    # bad
    do_something if x >= 1000 && x <= 2000
    
    # good
    do_something if (1000..2000).include?(x)
    
    # good
    do_something if x.between?(1000, 2000)
  • Ưu tiên dùng các phương thức có sẵn hơn là so sánh tường minh với ==. So sánh với số cũng chấp nhận được. [link]

    # bad
    if x % 2 == 0
    end
    
    if x % 2 == 1
    end
    
    if x == nil
    end
    
    # good
    if x.even?
    end
    
    if x.odd?
    end
    
    if x.nil?
    end
    
    if x.zero?
    end
    
    if x == 0
    end
  • Khi kiểm tra điều kiện thì không cần kiểm tra với nil trừ khi làm với boolean. [link]

    # bad
    do_something if !something.nil?
    do_something if something != nil
    
    # good
    do_something if something
    
    # good - dealing with a boolean
    def value_set?
      !@some_boolean.nil?
    end
  • Avoid the use of BEGIN blocks. [link]

  • Do not use END blocks. Use Kernel#at_exit instead. [link]

    # bad
    END { puts 'Goodbye!' }
    
    # good
    at_exit { puts 'Goodbye!' }
  • Tránh dùng flip-flops. [link]

  • Tránh dùng điều kiện lồng. Thay vào đó hãy kiểm tra dữ liệu trước. [link]

    # bad
    def compute_thing(thing)
      if thing[:foo]
        update_with_bar(thing)
        if thing[:foo][:bar]
          partial_compute(thing)
        else
          re_compute(thing)
        end
      end
    end
    
    # good
    def compute_thing(thing)
      return unless thing[:foo]
      update_with_bar(thing[:foo])
      return re_compute(thing) unless thing[:foo][:bar]
      partial_compute(thing)
    end

    Ưu tiên dùng next trong vòng lặp thay vì khối điều kiện.

    # bad
    [0, 1, 2, 3].each do |item|
      if item > 1
        puts item
      end
    end
    
    # good
    [0, 1, 2, 3].each do |item|
      next unless item > 1
      puts item
    end
  • Ưu tiên dùng map hơn collect, find hơn detect, select hơn find_all, reduce hơn injectsize hơn length. [link]

  • Đừng dùng count để thay cho size. Với đối tượng Enumerable hay là Array, nó sẽ duyệt qua từng phần tử để đếm số lượng phần tử, sẽ rất tốn thời gian. [link]

    # bad
    some_hash.count
    
    # good
    some_hash.size
  • Dùng flat_map thay cho map + flatten. Cái này không áp dụng cho mảng sâu hơn 2 cấp, vd: nếu users.first.songs == ['a', ['b','c']], thì nên dùng map + flatten hơn là flat_map. flat_map làm giảm đi 1 cấp, trong khi flatten làm phẳng hết về còn 1 cấp thôi. [link]

    # bad
    all_songs = users.map(&:songs).flatten.uniq
    
    # good
    all_songs = users.flat_map(&:songs).uniq
  • Ưu tiên dùng reverse_each hơn reverse.each vì một số lớp mà include Enumerable sẽ hoạt động hiệu quả hơn. [link]

    # bad
    array.reverse.each { ... }
    
    # good
    array.reverse_each { ... }

Cách đặt tên / Naming

Khó khăn duy nhất trong lập trình là việc lưu giữ thông tin không hợp lệ và việc đặt tên.
-- Phil Karlton

  • Dùng tiếng Anh để đặt tên. [link]

    # bad - Dùng các ký tự không thuộc bảng mã ascii
    заплата = 1_000
    
    # bad - tên này là tiếng Bulgarian
    zaplata = 1_000
    
    # good
    salary = 1_000
  • Dùng snake_case cho tên phương thức, biến và nhãn (symbol). [link]

    # bad
    :'some symbol'
    :SomeSymbol
    :someSymbol
    
    someVar = 5
    
    def someMethod
      # some code
    end
    
    def SomeMethod
      # some code
    end
    
    # good
    :some_symbol
    
    def some_method
      # some code
    end
  • Dùng CamelCase cho tên lớp và module. (Giữ những từ viết tắt như HTTP, RFC, XML viết hoa) [link]

    # bad
    class Someclass
      # some code
    end
    
    class Some_Class
      # some code
    end
    
    class SomeXml
      # some code
    end
    
    class XmlSomething
      # some code
    end
    
    # good
    class SomeClass
      # some code
    end
    
    class SomeXML
      # some code
    end
    
    class XMLSomething
      # some code
    end
  • Dùng snake_case cho tên file, vd: hello_world.rb. [link]

  • Dùng snake_case cho tên thư mục, vd: lib/hello_world/hello_world.rb. [link]

  • Mỗi file chỉ nên có một lớp/module. Tên của file chính là tên của class/module nhưng thay PascalCase thành snake_case. [link]

  • Dùng SCREAMING_SNAKE_CASE cho hằng số. [link]

    # bad
    SomeConst = 5
    
    # good
    SOME_CONST = 5
  • Phương thức trả về boolean thì nên có thêm ? đằng sau (vd: Array#empty?). Phương thức không trả về boolean thì không dùng. [link]

  • Tránh đặt tiền tố cho tên phương thức với các động từ bổ trợ như is, does, hay can. Những từ này không cần thiết và nghĩa quá chung chung, không đồng nhất với các phương thức boolean của ngôn ngữ: empty? hay include?. [link]

    # bad
    class Person
      def is_tall?
        true
      end
    
      def can_play_basketball?
        false
      end
    
      def does_like_candy?
        true
      end
    end
    
    # good
    class Person
      def tall?
        true
      end
    
      def basketball_player?
        false
      end
    
      def likes_candy?
        true
      end
    end
  • Tên của những phương thức có thể là nguy hiểm (vd: phương thức mà chỉnh sửa self hay các thuộc tính, hay phương thức exit! (không thực hiện việc finalize như exit), vv) cần kết thúc bằng một dấu chấm than ! nếu như đã có một phiên bản an toàn của nó rồi (safe version). [link]

    # bad - there is no matching 'safe' method
    class Person
      def update!
      end
    end
    
    # good
    class Person
      def update
      end
    end
    
    # good
    class Person
      def update!
      end
    
      def update
      end
    end
  • Nếu có phương thức nguy hiểm thì nên có thêm phương thức an toàn. [link]

    class Array
      def flatten_once!
        res = []
    
        each do |e|
          [*e].each { |f| res << f }
        end
    
        replace(res)
      end
    
      def flatten_once
        dup.flatten_once!
      end
    end
  • Khi dùng reduce với block ngắn, đặt tên các tham số là |a, e| (accumulator, element). [link]

  • Khi định nghĩa binary operators, đặt tên tham số là other (ngoại trừ hai toán tử <<[] là ngoại lệ, bởi nó mang ý nghĩa khác). [link]

    def +(other)
      # body omitted
    end

Viết chú thích / Comments

Good code is its own best documentation. As you're about to add a comment, ask yourself, "How can I improve the code so that this comment isn't needed?" Improve the code and then document it to make it even clearer.
-- Steve McConnell

  • Write self-documenting code and ignore the rest of this section. Seriously! [link]

  • Write comments in English. [link]

  • Use one space between the leading # character of the comment and the text of the comment. [link]

  • Comments longer than a word are capitalized and use punctuation. Use one space after periods. [link]

  • Avoid superfluous comments. [link]

    # bad
    counter += 1 # Increments counter by one.
  • Keep existing comments up-to-date. An outdated comment is worse than no comment at all. [link]

Good code is like a good joke - it needs no explanation.
-- Russ Olsen

  • Avoid writing comments to explain bad code. Refactor the code to make it self-explanatory. (Do or do not - there is no try. --Yoda) [link]

Comment Annotations

  • Annotations should usually be written on the line immediately above the relevant code. [link]

  • The annotation keyword is followed by a colon and a space, then a note describing the problem. [link]

  • If multiple lines are required to describe the problem, subsequent lines should be indented three spaces after the # (one general plus two for indentation purpose). [link]

    def bar
      # FIXME: This has crashed occasionally since v3.2.1. It may
      #   be related to the BarBazUtil upgrade.
      baz(:quux)
    end
  • In cases where the problem is so obvious that any documentation would be redundant, annotations may be left at the end of the offending line with no note. This usage should be the exception and not the rule. [link]

    def bar
      sleep 100 # OPTIMIZE
    end
  • Use TODO to note missing features or functionality that should be added at a later date. [link]

  • Use FIXME to note broken code that needs to be fixed. [link]

  • Use OPTIMIZE to note slow or inefficient code that may cause performance problems. [link]

  • Use HACK to note code smells where questionable coding practices were used and should be refactored away. [link]

  • Use REVIEW to note anything that should be looked at to confirm it is working as intended. For example: REVIEW: Are we sure this is how the client does X currently? [link]

  • Use other custom annotation keywords if it feels appropriate, but be sure to document them in your project's README or similar. [link]

Lớp đối tượng và Module / Classes & Modules

  • Use a consistent structure in your class definitions. [link]

    class Person
      # extend and include go first
      extend SomeModule
      include AnotherModule
    
      # inner classes
      CustomErrorKlass = 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
  • Don't nest multi-line classes within classes. Try to have such nested classes each in their own file in a folder named like the containing class. [link]

    # bad
    
    # foo.rb
    class Foo
      class Bar
        # 30 methods inside
      end
    
      class Car
        # 20 methods inside
      end
    
      # 30 methods inside
    end
    
    # good
    
    # 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
  • Prefer modules to classes with only class methods. Classes should be used only when it makes sense to create instances out of them. [link]

    # bad
    class SomeClass
      def self.some_method
        # body omitted
      end
    
      def self.some_other_method
        # body omitted
      end
    end
    
    # good
    module SomeModule
      module_function
    
      def some_method
        # body omitted
      end
    
      def some_other_method
        # body omitted
      end
    end
  • Favor the use of module_function over extend self when you want to turn a module's instance methods into class methods. [link]

    # bad
    module Utilities
      extend self
    
      def parse_something(string)
        # do stuff here
      end
    
      def other_utility_method(number, string)
        # do some more stuff
      end
    end
    
    # 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
  • When designing class hierarchies make sure that they conform to the Liskov Substitution Principle. [link]

  • Try to make your classes as SOLID as possible. [link]

  • Always supply a proper to_s method for classes that represent domain objects. [link]

    class Person
      attr_reader :first_name, :last_name
    
      def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
      end
    
      def to_s
        "#{@first_name} #{@last_name}"
      end
    end
  • Use the attr family of functions to define trivial accessors or mutators. [link]

    # bad
    class Person
      def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
      end
    
      def first_name
        @first_name
      end
    
      def last_name
        @last_name
      end
    end
    
    # good
    class Person
      attr_reader :first_name, :last_name
    
      def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
      end
    end
  • Avoid the use of attr. Use attr_reader and attr_accessor instead. [link]

    # bad - creates a single attribute accessor (deprecated in Ruby 1.9)
    attr :something, true
    attr :one, :two, :three # behaves as attr_reader
    
    # good
    attr_accessor :something
    attr_reader :one, :two, :three
  • Consider using Struct.new, which defines the trivial accessors, constructor and comparison operators for you. [link]

    # good
    class Person
      attr_accessor :first_name, :last_name
    
      def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
      end
    end
    
    # better
    Person = Struct.new(:first_name, :last_name) do
    end
  • Don't extend an instance initialized by Struct.new. Extending it introduces a superfluous class level and may also introduce weird errors if the file is required multiple times. [link]

    # bad
    class Person < Struct.new(:first_name, :last_name)
    end
    
    # good
    Person = Struct.new(:first_name, :last_name)
  • Consider adding factory methods to provide additional sensible ways to create instances of a particular class. [link]

    class Person
      def self.create(options_hash)
        # body omitted
      end
    end
  • Prefer duck-typing over inheritance. [link]

    # bad
    class Animal
      # abstract method
      def speak
      end
    end
    
    # extend superclass
    class Duck < Animal
      def speak
        puts 'Quack! Quack'
      end
    end
    
    # extend superclass
    class Dog < Animal
      def speak
        puts 'Bau! Bau!'
      end
    end
    
    # good
    class Duck
      def speak
        puts 'Quack! Quack'
      end
    end
    
    class Dog
      def speak
        puts 'Bau! Bau!'
      end
    end
  • Avoid the usage of class (@@) variables due to their "nasty" behavior in inheritance. [link]

    class Parent
      @@class_var = 'parent'
    
      def self.print_class_var
        puts @@class_var
      end
    end
    
    class Child < Parent
      @@class_var = 'child'
    end
    
    Parent.print_class_var # => will print 'child'

    As you can see all the classes in a class hierarchy actually share one class variable. Class instance variables should usually be preferred over class variables.

  • Assign proper visibility levels to methods (private, protected) in accordance with their intended usage. Don't go off leaving everything public (which is the default). After all we're coding in Ruby now, not in Python. [link]

  • Indent the public, protected, and private methods as much as the method definitions they apply to. Leave one blank line above the visibility modifier and one blank line below in order to emphasize that it applies to all methods below it. [link]

    class SomeClass
      def public_method
        # some code
      end
    
      private
    
      def private_method
        # some code
      end
    
      def another_private_method
        # some code
      end
    end
  • Use def self.method to define class methods. This makes the code easier to refactor since the class name is not repeated. [link]

    class TestClass
      # bad
      def TestClass.some_method
        # body omitted
      end
    
      # good
      def self.some_other_method
        # body omitted
      end
    
      # Also possible and convenient when you
      # have to define many class methods.
      class << self
        def first_method
          # body omitted
        end
    
        def second_method_etc
          # body omitted
        end
      end
    end
  • Prefer alias when aliasing methods in lexical class scope as the resolution of self in this context is also lexical, and it communicates clearly to the user that the indirection of your alias will not be altered at runtime or by any subclass unless made explicit. [link]

    class Westerner
      def first_name
        @names.first
      end
    
      alias given_name first_name
    end

    Since alias, like def, is a keyword, prefer bareword arguments over symbols or strings. In other words, do alias foo bar, not alias :foo :bar.

    Also be aware of how Ruby handles aliases and inheritance: an alias references the method that was resolved at the time the alias was defined; it is not dispatched dynamically.

    class Fugitive < Westerner
      def first_name
        'Nobody'
      end
    end

    In this example, Fugitive#given_name would still call the original Westerner#first_name method, not Fugitive#first_name. To override the behavior of Fugitive#given_name as well, you'd have to redefine it in the derived class.

    class Fugitive < Westerner
      def first_name
        'Nobody'
      end
    
      alias given_name first_name
    end
  • Always use alias_method when aliasing methods of modules, classes, or singleton classes at runtime, as the lexical scope of alias leads to unpredictability in these cases. [link]

    module Mononymous
      def self.included(other)
        other.class_eval { alias_method :full_name, :given_name }
      end
    end
    
    class Sting < Westerner
      include Mononymous
    end

Exceptions

  • Ưu tiên dùng raise hơn fail. [link]

    # bad
    fail SomeException, 'message'
    
    # good
    raise SomeException, 'message'
  • Không cần phải chỉ rõ RuntimeError khi raise mà có hai đối số. [link]

    # bad
    raise RuntimeError, 'message'
    
    # good - RuntimeError được raise mặc định
    raise 'message'
  • Khi ném exception, ưu tiên dùng phương án hai tham số: một class và một message hơn là một claas được khởi tạo với message. [link]

    # bad
    raise SomeException.new('message')
    # Lưu ý rằng lệnh sau `raise SomeException.new('message'), backtrace` là sai.
    
    # good
    raise SomeException, 'message'
    # Tương đương với `raise SomeException, 'message', backtrace`.
  • KHÔNG return từ ensure block. Nếu làm vậy, dù có exception được ném ra nhưng phương thức vẫn trả kết quả về, nên đôi khi ta sẽ bỏ qua exception. [link]

    # bad
    def foo
      raise
    ensure
      return 'very bad idea'
    end
  • Dùng từ khóa begin một cách ngầm định khi có thể. [link]

    # bad
    def foo
      begin
        # main logic goes here
      rescue
        # failure handling goes here
      end
    end
    
    # good
    def foo
      # main logic goes here
    rescue
      # failure handling goes here
    end
  • Mitigate the proliferation of begin blocks by using contingency methods (a term coined by Avdi Grimm). [link]

    # bad
    begin
      something_that_might_fail
    rescue IOError
      # handle IOError
    end
    
    begin
      something_else_that_might_fail
    rescue IOError
      # handle IOError
    end
    
    # good
    def with_io_error_handling
       yield
    rescue IOError
      # handle IOError
    end
    
    with_io_error_handling { something_that_might_fail }
    
    with_io_error_handling { something_else_that_might_fail }
  • Đừng bỏ qua exceptions. [link]

    # bad
    begin
      # an exception occurs here
    rescue SomeError
      # the rescue clause does absolutely nothing
    end
    
    # bad
    do_something rescue nil
  • Bắt đúng lỗi được ném ra. [link]

    # bad - this catches exceptions of StandardError class and its descendant classes
    read_file rescue handle_error($!)
    
    # good - this catches only the exceptions of Errno::ENOENT class and its descendant classes
    def foo
      read_file
    rescue Errno::ENOENT => ex
      handle_error(ex)
    end
  • Không dùng exceptions trong luông điều khiển. Thay vào đó hãy kiểm tra hợ lệ trước. [link]

    # bad
    begin
      n / d
    rescue ZeroDivisionError
      puts 'Cannot divide by 0!'
    end
    
    # good
    if d.zero?
      puts 'Cannot divide by 0!'
    else
      n / d
    end
  • Tránh việc bắt lớp Exception. Việc này sẽ gọi đến hàm exit, có thể bạn sẽ phải tắt cứng process bằng lệnh kill -9. [link]

    # bad
    begin
      # calls to exit and kill signals will be caught (except kill -9)
      exit
    rescue Exception
      puts "you didn't really want to exit, right?"
      # exception handling
    end
    
    # good
    begin
      # a blind rescue rescues from StandardError, not Exception as many
      # programmers assume.
    rescue => e
      # exception handling
    end
    
    # also good
    begin
      # an exception occurs here
    rescue StandardError => e
      # exception handling
    end
  • Bắt exception theo thứ tự từ thấp đến cao, bắt thằng con trước, rồi bắt cha sau. [link]

    # bad
    begin
      # some code
    rescue StandardError => e
      # some handling
    rescue IOError => e
      # some handling that will never be executed
    end
    
    # good
    begin
      # some code
    rescue IOError => e
      # some handling
    rescue StandardError => e
      # some handling
    end
  • Trong ensure, nên giải phóng tài nguyên cho hệ thống. [link]

    f = File.open('testfile')
    begin
      # .. process
    rescue
      # .. handle error
    ensure
      f.close if f
    end
  • Nếu như tài nguyên được giải phóng tự động thì không cần phải làm thủ công. [link]

    # bad - bạn muốn đóng file một cách tường minh qua lệnh `close`
    f = File.open('testfile')
    # some action on the file
    f.close
    
    # good - file descriptor được đóng tự động khi dùng xong
    File.open('testfile') do |f|
      # some action on the file
    end
  • Ưu tiên dùng những exception mặc định của hệ thống hơn là định nghĩa là lớp mới. [link]

Collections

  • Ưu tiên khai báo mảng hay hash bằng cặp ngoặc hơn là tạo thể hiện, bởi vì đôi khi ta phải truyền đối số. [link]

    # bad
    arr = Array.new
    hash = Hash.new
    
    # good
    arr = []
    hash = {}
  • Ưu tiên dùng %W để tạo mảng. Lưu ý chỉ dùng khi mảng có vài phần tử. [link]

    # bad
    STATES = ['draft', 'open', 'closed']
    
    # good
    STATES = %w(draft open closed)
  • Ưu tiên dùng %i nếu muốn tạo mảng các nhãn (symbol). [link]

    # bad
    STATES = [:draft, :open, :closed]
    
    # good
    STATES = %i(draft open closed)
  • Tránh thêm dấu phẩy sau phẩn tử cuối cùng của Array hay Hash, đặc biệt là khi các phần tử đó không nằm trên các dòng khác nhau. [link]

    # bad - easier to move/add/remove items, but still not preferred
    VALUES = [
               1001,
               2020,
               3333,
             ]
    
    # bad
    VALUES = [1001, 2020, 3333, ]
    
    # good
    VALUES = [1001, 2020, 3333]
  • Tránh việc tạo mảng nhiều phần tử quá mà không dùng hết. [link]

    arr = []
    arr[100] = 1 # now you have an array with lots of nils
  • Khi truy cập phần tử đầu tiên/cuối cùng trong mảng, ưu tiên dùng first hay last hơn [0], [-1]. [link]

  • Dùng Set thay vì Array khi làm việc với mảng các phần tử độc nhất. Set là tập hợp các phần tử không có thứ tự và không lặp lại. Đây là một biến thể của Array, nó là sự kết hợp các tính năng của Array và khả năng tìm kiếm nhanh của Hash. [link]

  • Khi làm việc với hash, ưu tiên dùng nhãn hơn string khi đặt key. [link]

    # bad
    hash = { 'one' => 1, 'two' => 2, 'three' => 3 }
    
    # good
    hash = { one: 1, two: 2, three: 3 }
  • Tránh việc sử dụng đối tượng không bất biến cho hash key. [link]

  • Dùng cú pháp của Ruby 1.9 để tạo hash. [link]

    # bad
    hash = { :one => 1, :two => 2, :three => 3 }
    
    # good
    hash = { one: 1, two: 2, three: 3 }
  • Không trộn cú pháp của Ruby 1.9 với kiểu cũ. [link]

    # bad
    { a: 1, 'b' => 2 }
    
    # good
    { :a => 1, 'b' => 2 }
  • Dùng Hash#key? thay cho Hash#has_key?, Hash#value? thay cho Hash#has_value?. Xem thêm: here [link]

    # bad
    hash.has_key?(:test)
    hash.has_value?(value)
    
    # good
    hash.key?(:test)
    hash.value?(value)
  • Dùng Hash#each_key thay cho Hash#keys.each, và Hash#each_value thay cho Hash#values.each. [link]

    # 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 }
  • Dùng Hash#fetch khi biết chắc rằng hash có key đó hay không. [link]

    heroes = { batman: 'Bruce Wayne', superman: 'Clark Kent' }
    # bad - if we make a mistake we might not spot it right away
    heroes[:batman] # => 'Bruce Wayne'
    heroes[:supermann] # => nil
    
    # good - fetch raises a KeyError making the problem obvious
    heroes.fetch(:supermann)
  • Khi dùng Hash#fetch, có thể thêm giá trị mặc định vào thay vì dùng logic. [link]

    batman = { name: 'Bruce Wayne', is_evil: false }
    
    # bad - if we just use || operator with falsy value we won't get the expected result
    batman[:is_evil] || true # => true
    
    # good - fetch work correctly with falsy values
    batman.fetch(:is_evil, true) # => false
  • Ưu tiên dùng block thay vì giá trị mặc định trong Hash#fetch, vì có thể sẽ làm chậm chương trình đi, hoặc có những tác dụng phụ. [link]

    batman = { name: 'Bruce Wayne' }
    
    # bad - if we use the default value, we eager evaluate it
    # so it can slow the program down if done multiple times
    batman.fetch(:powers, obtain_batman_powers) # obtain_batman_powers is an expensive call
    
    # good - blocks are lazy evaluated, so only triggered in case of KeyError exception
    batman.fetch(:powers) { obtain_batman_powers }
  • Dùng Hash#values_at khi muốn lấy nhiều giá trị liên tiếp trong hash ra. [link]

    # bad
    email = data['email']
    username = data['nickname']
    
    # good
    email, username = data.values_at('email', 'nickname')
  • Lưu ý rằng hash trừ Ruby 1.9 là có thứ tự. [link]

  • KHÔNG chỉnh sửa một collection khi đang duyệt qua các phần tử của nó. [link]

  • Khi truy cập vào các phần tử của collection, tránh việc truy cập trực tiếp thông qua [n], hãy dùng các phương án thay thế nếu có thể. Việc này sẽ giúp tránh được lỗi gọi [] on nil [link]

    # bad
    Regexp.last_match[1]
    
    # good
    Regexp.last_match(1)
  • Khi cấp quyền truy cập vào một collection, nên cung cấp một phương án giúp users khỏi phải check nil sau khi nhận kết quả trả về. [link]

    # bad
    def awesome_things
      @awesome_things
    end
    
    # good
    def awesome_things(index = nil)
      if index && @awesome_things
        @awesome_things[index]
      else
        @awesome_things
      end
    end

Numbers

  • Dùng Integer để kiểm tra kiểu của một số. Fixnum thì phụ thuộc vào nền tảng (hệ điều hành chẳng hạn), với hệ thống 32-bit64-bit thì kết quả nó sẽ khác nhau. [link]

    timestamp = Time.now.to_i
    
    # bad
    timestamp.is_a? Fixnum
    timestamp.is_a? Bignum
    
    # good
    timestamp.is_a? Integer

Strings

  • Ưu tiên dùng kiểu nhúng vào String hoặc là format hơn là nối chuỗi. [link]

    # bad
    email_with_name = user.name + ' <' + user.email + '>'
    
    # good
    email_with_name = "#{user.name} <#{user.email}>"
    
    # good
    email_with_name = format('%s <%s>', user.name, user.email)
  • Với String tuyệt đối, hiện có hai kiểu dùng: dấu nháy đơn (Option A) và dấu nháy kép (Option B). [link]

    • (Option A)

      # bad
      name = "Bozhidar"
      
      # good
      name = 'Bozhidar'
    • (Option B)

      # bad
      name = 'Bozhidar'
      
      # good
      name = "Bozhidar"

    Tài liệu này sử dụng kiểu thứ nhất.

  • Không dùng cú pháp ?x nữa, Ruby 1.9 đã bỏ rồi. [link]

    # bad
    char = ?c
    
    # good
    char = 'c'
  • Khi nhúng biến toàn cục vào String, không nên bỏ cặp ngoặc nhọn đi. [link]

    class Person
      attr_reader :first_name, :last_name
    
      def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
      end
    
      # bad - hợp lệ, nhưng khó đọc
      def to_s
        "#@first_name #@last_name"
      end
    
      # good
      def to_s
        "#{@first_name} #{@last_name}"
      end
    end
    
    $global = 0
    # bad
    puts "$global = #$global"
    
    # good
    puts "$global = #{$global}"
  • Không cần phải dùng Object#to_s khi in đối tượng ra, mặc định nó có rồi. [link]

    # bad
    message = "This is the #{result.to_s}."
    
    # good
    message = "This is the #{result}."
  • Tránh sử dụng String#+ khi muốn tạo string với nhiều dữ liệu. Thay vào đó, hãy dùng String#<<. String#+ sẽ tạo một đối tượng mới, do đó sẽ chậm hơn, String#<< ngược lại chỉ thêm vào. [link]

    # bad
    html = ''
    html += '<h1>Page title</h1>'
    
    paragraphs.each do |paragraph|
      html += "<p>#{paragraph}</p>"
    end
    
    # good and also fast
    html = ''
    html << '<h1>Page title</h1>'
    
    paragraphs.each do |paragraph|
      html << "<p>#{paragraph}</p>"
    end
  • Trong một số trường hợp cụ thể cần tốc độ, không dùng String#gsub. [link]

    url = 'http://example.com'
    str = 'lisp-case-rules'
    
    # bad
    url.gsub('http://', 'https://')
    str.gsub('-', '_')
    
    # good
    url.sub('http://', 'https://')
    str.tr('-', '_')
  • Khi sử dụng heredocs cho string nhiều dòng, để ý rằng nó sẽ bỏ qua mấy khoảng trắng, cho nên tốt nhất nên làm một ký tự | ở đầu dòng để làm mốc. [link]

    code = <<-END.gsub(/^\s+\|/, '')
      |def test
      |  some_method
      |  other_method
      |end
    END
    # => "def test\n  some_method\n  other_method\nend\n"
  • Sử dụng cú pháp ~ của Ruby 2.3 để hiển thị tốt hơn. [link]

    # bad - using Powerpack String#strip_margin
    code = <<-END.strip_margin('|')
      |def test
      |  some_method
      |  other_method
      |end
    END
    
    # also bad
    code = <<-END
    def test
      some_method
      other_method
    end
    END
    
    # good
    code = <<~END
      def test
        some_method
        other_method
      end
    END

Regular Expressions

Có một số người, khi gặp một vấn đề, họ sẽ nghĩ "OK, tôi sẽ dùng biểu thức chính quy (regular expressions)" Và giờ họ phải đổi mặt với hai vấn đề.
-- Jamie Zawinski

  • Đùng dùng regex nếu bạn đơn thuần chỉ cần tìm kiếm một đoạn text trong string: string['text'] [link]

  • For simple constructions you can use regexp directly through string index. [link]

    match = string[/regexp/]             # get content of matched regexp
    first_group = string[/text(grp)/, 1] # get content of captured group
    string[/text (grp)/, 1] = 'replace'  # string => 'text replace'
  • Use non-capturing groups when you don't use the captured result. [link]

    # bad
    /(first|second)/
    
    # good
    /(?:first|second)/
  • Don't use the cryptic Perl-legacy variables denoting last regexp group matches ($1, $2, etc). Use Regexp.last_match(n) instead. [link]

    /(regexp)/ =~ string
    ...
    
    # bad
    process $1
    
    # good
    process Regexp.last_match(1)
  • Avoid using numbered groups as it can be hard to track what they contain. Named groups can be used instead. [link]

    # bad
    /(regexp)/ =~ string
    # some code
    process Regexp.last_match(1)
    
    # good
    /(?<meaningful_var>regexp)/ =~ string
    # some code
    process meaningful_var
  • Character classes have only a few special characters you should care about: ^, -, \, ], so don't escape . or brackets in []. [link]

  • Be careful with ^ and $ as they match start/end of line, not string endings. If you want to match the whole string use: \A and \z (not to be confused with \Z which is the equivalent of /\n?\z/). [link]

    string = "some injection\nusername"
    string[/^username$/]   # matches
    string[/\Ausername\z/] # doesn't match
  • Use x modifier for complex regexps. This makes them more readable and you can add some useful comments. Just be careful as spaces are ignored. [link]

    regexp = /
      start         # some text
      \s            # white space char
      (group)       # first group
      (?:alt1|alt2) # some alternation
      end
    /x
  • For complex replacements sub/gsub can be used with a block or a hash. [link]

    words = 'foo bar'
    words.sub(/f/, 'f' => 'F') # => 'Foo bar'
    words.gsub(/\w+/) { |word| word.capitalize } # => 'Foo Bar'

Percent Literals

  • Dùng %() (hoặc %Q) cho string một-dòng với yêu cầu là nó phải vừa in ra được mà vừa nhúng được. Với string nhiều dòng, ưu tiên dùng heredocs. [link]

    # bad (no interpolation needed)
    %(<div class="text">Some text</div>)
    # should be '<div class="text">Some text</div>'
    
    # bad (no double-quotes)
    %(This is #{quality} style)
    # should be "This is #{quality} style"
    
    # bad (multiple lines)
    %(<div>\n<span class="big">#{exclamation}</span>\n</div>)
    # should be a heredoc.
    
    # good (requires interpolation, has quotes, single line)
    %(<tr><td class="name">#{name}</td>)
  • Tránh dùng %q trừ khi bạn có string mà chứa cả '". String bình thường vẫn dễ đọc hơn, trừ khi nó có chứa nhiều ký tự đặc biệt quá. [link]

    # bad
    name = %q(Bruce Wayne)
    time = %q(8 o'clock)
    question = %q("What did you say?")
    
    # good
    name = 'Bruce Wayne'
    time = "8 o'clock"
    question = '"What did you say?"'
    quote = %q(<p class='quote'>"What did you say?"</p>)
  • Chỉ dùng %r khi regex khớp ít nhất một ký tự / [link]

    # bad
    %r{\s+}
    
    # good
    %r{^/(.*)$}
    %r{^/blog/2016/(.*)$}
  • Tránh dùng %x trừ khi bạn muốn thực thi một command với dấu nháy ngược trong lệnh, mà thường hiếm khi gặp trường hợp này lắm. [link]

    # bad
    date = %x(date)
    
    # good
    date = `date`
    echo = %x(echo `date`)
  • Tránh dùng %s. Cộng đồng đã chọn :"some string" là cách để tạo symbol mà có khoảng trắng rồi. [link]

  • Ưu tiên () khi dùng với %, ngoại trừ %r. [link]

    # bad
    %w[one two three]
    %q{"Test's king!", John said.}
    
    # good
    %w(one two three)
    %q("Test's king!", John said.)

Metaprogramming

  • Tránh những metaprogramming không cần thiết. [link]

  • Không viết thêm phương thức cho lớp của hệ thống khi viết thư viện (monkey-patch) [link]

  • The block form of class_eval is preferable to the string-interpolated form. [link]

    • when you use the string-interpolated form, always supply __FILE__ and __LINE__, so that your backtraces make sense:

      class_eval 'def use_relative_model_naming?; true; end', __FILE__, __LINE__
    • define_method is preferable to class_eval{ def ... }

  • When using class_eval (or other eval) with string interpolation, add a comment block showing its appearance if interpolated (a practice used in Rails code): [link]

    # from activesupport/lib/active_support/core_ext/string/output_safety.rb
    UNSAFE_STRING_METHODS.each do |unsafe_method|
      if 'String'.respond_to?(unsafe_method)
        class_eval <<-EOT, __FILE__, __LINE__ + 1
          def #{unsafe_method}(*params, &block)       # def capitalize(*params, &block)
            to_str.#{unsafe_method}(*params, &block)  #   to_str.capitalize(*params, &block)
          end                                       # end
    
          def #{unsafe_method}!(*params)              # def capitalize!(*params)
            @dirty = true                           #   @dirty = true
            super                                   #   super
          end                                       # end
        EOT
      end
    end
  • Avoid using method_missing for metaprogramming because backtraces become messy, the behavior is not listed in #methods, and misspelled method calls might silently work, vd: nukes.launch_state = false. Consider using delegation, proxy, or define_method instead. If you must use method_missing: [link]

    • Be sure to also define respond_to_missing?

    • Only catch methods with a well-defined prefix, such as find_by_* -- make your code as assertive as possible.

    • Call super at the end of your statement

    • Delegate to assertive, non-magical methods:

      # bad
      def method_missing?(meth, *params, &block)
        if /^find_by_(?<prop>.*)/ =~ meth
          # ... lots of code to do a find_by
        else
          super
        end
      end
      
      # good
      def method_missing?(meth, *params, &block)
        if /^find_by_(?<prop>.*)/ =~ meth
          find_by(prop, *params, &block)
        else
          super
        end
      end
      
      # best of all, though, would to define_method as each findable attribute is declared
  • Prefer public_send over send so as not to circumvent private/protected visibility. [link]

    # We have  an ActiveModel Organization that includes concern Activatable
    module Activatable
      extend ActiveSupport::Concern
    
      included do
        before_create :create_token
      end
    
      private
    
      def reset_token
        # some code
      end
    
      def create_token
        # some code
      end
    
      def activate!
        # some code
      end
    end
    
    class Organization < ActiveRecord::Base
      include Activatable
    end
    
    linux_organization = Organization.find(...)
    # BAD - violates privacy
    linux_organization.send(:reset_token)
    # GOOD - should throw an exception
    linux_organization.public_send(:reset_token)
  • Prefer __send__ over send, as send may overlap with existing methods. [link]

    require 'socket'
    
    u1 = UDPSocket.new
    u1.bind('127.0.0.1', 4913)
    u2 = UDPSocket.new
    u2.connect('127.0.0.1', 4913)
    # Won't send a message to the receiver obj.
    # Instead it will send a message via UDP socket.
    u2.send :sleep, 0
    # Will actually send a message to the receiver obj.
    u2.__send__ ...

Misc

  • Write ruby -w safe code. [link]

  • Avoid hashes as optional parameters. Does the method do too much? (Object initializers are exceptions for this rule). [link]

  • Avoid methods longer than 10 LOC (lines of code). Ideally, most methods will be shorter than 5 LOC. Empty lines do not contribute to the relevant LOC. [link]

  • Avoid parameter lists longer than three or four parameters. [link]

  • If you really need "global" methods, add them to Kernel and make them private. [link]

  • Use module instance variables instead of global variables. [link]

    # bad
    $foo_bar = 1
    
    # good
    module Foo
      class << self
        attr_accessor :bar
      end
    end
    
    Foo.bar = 1
  • Use OptionParser for parsing complex command line options and ruby -s for trivial command line options. [link]

  • Prefer Time.now over Time.new when retrieving the current system time. [link]

  • Code in a functional way, avoiding mutation when that makes sense. [link]

  • Do not mutate parameters unless that is the purpose of the method. [link]

  • Avoid more than three levels of block nesting. [link]

  • Be consistent. In an ideal world, be consistent with these guidelines. [link]

  • Use common sense. [link]

Các công cụ / Tools

Here are some tools to help you automatically check Ruby code against this guide.

RuboCop

RuboCop is a Ruby code style checker based on this style guide. RuboCop already covers a significant portion of the Guide, supports both MRI 1.9 and MRI 2.0 and has good Emacs integration.

RubyMine

RubyMine's code inspections are partially based on this guide.

Contributing

The guide is still a work in progress - some rules are lacking examples, some rules don't have examples that illustrate them clearly enough. Improving such rules is a great (and simple way) to help the Ruby community!

In due time these issues will (hopefully) be addressed - just keep them in mind for now.

Nothing written in this guide is set in stone. It's my desire to work together with everyone interested in Ruby coding style, so that we could ultimately create a resource that will be beneficial to the entire Ruby community.

Feel free to open tickets or send pull requests with improvements. Thanks in advance for your help!

You can also support the project (and RuboCop) with financial contributions via Gratipay.

Support via Gratipay

How to Contribute?

It's easy, just follow the contribution guidelines.

License

Creative Commons License This work is licensed under a Creative Commons Attribution 3.0 Unported License

Spread the Word

A community-driven style guide is of little use to a community that doesn't know about its existence. Tweet about the guide, share it with your friends and colleagues. Every comment, suggestion or opinion we get makes the guide just a little bit better. And we want to have the best possible guide, don't we?

Cheers,
Bozhidar