Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Write your Ruby in Style

Write your Ruby in Style

Slides of my talk at Rubyconf India 2013

Bhavin Javia

June 23, 2013
Tweet

More Decks by Bhavin Javia

Other Decks in Programming

Transcript

  1. • Fascinated by computers • Learnt programming in school/college •

    Knew a few languages - C, C++, Java • Loved Ruby • Joined a Startup as an Intern • Full of Ruby veterans • Felt right at home Story of Rubo
  2. • Java - Code Conventions for Java (by Oracle) •

    Python - Style Guide for Python Code (PEP 8) • PHP - Basic Coding Standard (PSR-1) Many languages have Coding Standards http://www.oracle.com/technetwork/java/codeconv-138413.html http://www.python.org/dev/peps/pep-0008/ http://www.php-fig.org/psr/1/
  3. What’s a Style Guide ? • Better way to write

    code • Better way to lay-out code • Right idioms to use and when • Simplicity and Elegance
  4. Why Style Guide ? • Functional correctness not enough •

    Visual consistency matters • Reduce cognitive friction • Avoid religious wars • Increase team productivity • Long term maintainability
  5. “ Nearly everybody is convinced that every style but their

    own is ugly and unreadable. Leave out the "but their own" and they're probably right... - Jerry Coffin (on indentation)
  6. # encoding: utf-8 https://help.github.com/articles/dealing-with-line-endings $ git config --global core.autocrlf input

    # Mac/Linux $ git config --global core.autocrlf true # Windows Use UTF-8 as the source file encoding Use Unix-style line endings
  7. # bad - four spaces def some_method do_something end #

    good def some_method do_something end * No tabs please Two spaces per indent
  8. # bad def too_much; something; something_else; end # okish def

    no_braces_method; body; end # okish def some_method() body end # good def some_method body end * Not applicable to empty methods e.g. # good def no_op; end Avoid single-line methods
  9. # around operators, after commas, colons and semicolons, around `{`

    and before `}` sum = 1 + 2 a, b = 1, 2 1 > 2 ? true : false; puts 'Hi' [1, 2, 3].each { |e| puts e } * Not with exponent operator # bad e = M * c ** 2 # good e = M * c**2 Use spaces
  10. Indent when as deep as case 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
  11. Align multi-line params # starting point (line is too long)

    def send_mail(source) Mailer.deliver(to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source. text) end # bad (normal indent) def send_mail(source) Mailer.deliver( to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text) end
  12. Align multi-line params # bad (double indent) def send_mail(source) Mailer.deliver(

    to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text) end # good def send_mail(source) Mailer.deliver(to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text) end
  13. Add underscores to large numeric literals # bad - how

    many 0s are there? num = 1000000 # good - much easier to parse num = 1_000_000
  14. class Array # Calls <tt>to_param</tt> on all its elements and

    joins the result with # slashes. This is used by <tt>url_for</tt> in Action Pack. def to_param collect { |e| e.to_param }.join '/' end end Use RDoc and its conventions for API docs
  15. Use Iterators arr = [1, 2, 3] # bad for

    elem in arr do puts elem end # good arr.each { |elem| puts elem }
  16. # bad result = if some_condition then something else something_else

    end # good result = some_condition ? something : something_else * for one line constructs Favor the ternary operator (?:) over if/then/else/end
  17. # 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 Use one expression per branch in a ternary operator
  18. # bad if some_condition do_something do_something_else end # good if

    some_condition do_something do_something_else end Always put the condition on the same line
  19. Favor unless over if for negative conditions # 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
  20. class Person attr_reader :name, :age # omitted end temperance =

    Person.new('Temperance', 30) temperance.name puts temperance.age x = Math.sin(y) array.delete(e) bowling.score.should == 0 Omit parentheses around parameters for methods that are part of internal DSL, keyword status, accessors
  21. names = ['Bozhidar', 'Steve', 'Sarah'] # bad names.each do |name|

    puts name end # good names.each { |name| puts name } * Avoid using {...} for multi-line blocks Prefer {...} over do...end for single-line blocks
  22. # bad def some_method(some_arr) return some_arr.size end # good def

    some_method(some_arr) some_arr.size end Avoid return where not required for flow of control
  23. # 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 * only required when calling a self write accessor Avoid self where not required
  24. Name identifiers in English # bad - variable name written

    in Bulgarian with latin characters zaplata = 1_000 # good salary = 1_000
  25. Use CamelCase for classes and modules # bad class Someclass

    ... end class Some_Class ... end class SomeXml ... end # good class SomeClass ... end class SomeXML ... end * Keep acronyms like HTTP, RFC, XML uppercase
  26. Use snake_case for symbols, methods and variables # bad :'some

    symbol' :SomeSymbol :someSymbol someVar = 5 def someMethod ... end def SomeMethod ... end # good :some_symbol some_var = 5 def some_method ... end
  27. Predicate methods shouldshould end in a question mark ‘?’ #

    bad def available appointments.empty? end # good def available? appointments.empty? end * e.g. Array#empty?
  28. “ Good code is its own best documentation ... -

    Steve McConnell “ Good code is like a good joke - it needs no explanation. - Russ Olsen
  29. Use Comment Annotations • Use TODO to note missing features

    • Use FIXME to note broken code • Use HACK to note code smells • Use OPTIMIZE to note inefficient code
  30. Use a consistent structure in your class definitions class Person

    # extend and include extend SomeModule include AnotherModule # constants SOME_CONSTANT = 20 # attribute macros attr_reader :name # other macros (if any) validates :name # public class methods are next in line def self.some_method end # followed by public instance methods def some_method end # protected/private methods end
  31. Implement to_s on domain classes 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
  32. Use the attr family of functions to define trivial accessors

    # 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
  33. “ A Foolish Consistency is the Hobgoblin of Little Minds

    - Essays: First Series by Ralph Waldo Emerson
  34. Consistency at what level ? • consistency with style guide

    - important • consistency with project - more important • consistency with module - most important • readability matters
  35. When to break the rule ? • Applying the rule

    makes it less readable • To be consistent with surrounding code • Opportunity to clean up the mess
  36. • Make it a standard team practice • Select/Draft a

    style guide for project • Make it a must read for everyone • Use IDE support e.g. RubyMine inspections How to enforce these rules ?
  37. • Use Style Checker tools • Integrate with CI •

    Use a live style guide - fork it, send PRs • Commit your IDE settings • Point out violations in context • Use Github inline commit notes How to enforce these rules ?