Write your Ruby in Style

3,345 views

Published on

Slides of my talk at Rubyconf India 2013

Published in: Technology
  • Be the first to comment

Write your Ruby in Style

  1. 1. Write your- byBhavin Javia@bhavinjaviainStylewww.mavenhive.in
  2. 2. Who this talk isNOT for ?
  3. 3. I am the only one !
  4. 4. Who this talkIS for ?
  5. 5. Dad ! Who’s ‘Ruby’ ?
  6. 6. Teams
  7. 7. Open SourceContributors
  8. 8. Style Matters
  9. 9. Why bother ?
  10. 10. Story
  11. 11. Rubo
  12. 12. • 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 homeStory of Rubo
  13. 13. Ruby Rocks !
  14. 14. But,
  15. 15. As soon as hecommitted somecode ...
  16. 16. WTF ?OMG !#$%@ Caveman !
  17. 17. Oh, Where did Igo wrong ?!
  18. 18. What was wrong ?
  19. 19. He lacked Style
  20. 20. Masters of RubyStylehttp://www.flickr.com/photos/jakescruggs/4687409786/http://vimeo.com/steveklabnikhttps://si0.twimg.com/profile_images/2931712744/95043c0f42f0700bdbd713635e942ac0.jpeghttp://peepcode.com/blog/2011/my-avatar/circle1.jpg
  21. 21. • Java - Code Conventions for Java (by Oracle)• Python - Style Guide for Python Code (PEP 8)• PHP - Basic Coding Standard (PSR-1)Many languages haveCoding Standardshttp://www.oracle.com/technetwork/java/codeconv-138413.htmlhttp://www.python.org/dev/peps/pep-0008/http://www.php-fig.org/psr/1/
  22. 22. Why Ruby doesn’t ?
  23. 23. Many waysto do things ...
  24. 24. And all of them work !
  25. 25. Ruby Sucks !
  26. 26. We need a Style Guide
  27. 27. What is a Style Guide ?
  28. 28. 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
  29. 29. Helps you decide ...
  30. 30. http://flic.kr/p/5n8Rrdwhat your code should look like ?this ...
  31. 31. http://flic.kr/p/voGgzor this ?
  32. 32. I’ve got my own Style
  33. 33. Why Style Guide ?
  34. 34. Why Style Guide ?• Functional correctness not enough• Visual consistency matters• Reduce cognitive friction• Avoid religious wars• Increase team productivity• Long term maintainability
  35. 35. Let the team own it
  36. 36. Whenno oneowns the code,everyoneowns the code !
  37. 37. http://flic.kr/p/6mHZfTcode which lasts 1000s of years
  38. 38. Examples
  39. 39. Code Layout
  40. 40. “ Nearly everybody is convinced that every style but theirown is ugly and unreadable. Leave out the "but theirown" and theyre probably right...- Jerry Coffin (on indentation)
  41. 41. # encoding: utf-8https://help.github.com/articles/dealing-with-line-endings$ git config --global core.autocrlf input # Mac/Linux$ git config --global core.autocrlf true # WindowsUse UTF-8 as the source file encodingUse Unix-style line endings
  42. 42. # bad - four spacesdef some_methoddo_somethingend# gooddef some_methoddo_somethingend* No tabs pleaseTwo spaces per indent
  43. 43. # baddef too_much; something; something_else; end# okishdef no_braces_method; body; end# okishdef some_method() body end# gooddef some_methodbodyend* Not applicable to empty methods e.g.# gooddef no_op; endAvoid single-line methods
  44. 44. # around operators, after commas, colons and semicolons,around `{` and before `}`sum = 1 + 2a, b = 1, 21 > 2 ? true : false; puts Hi[1, 2, 3].each { |e| puts e }* Not with exponent operator# bade = M * c ** 2# goode = M * c**2Use spaces
  45. 45. Indent when as deep as casecasewhen song.name == Mistyputs Not again!when song.duration > 120puts Too long!when Time.now.hour > 21puts "Its too late"elsesong.playendkind = case yearwhen 1850..1889 then Blueswhen 1890..1909 then Ragtimewhen 1910..1929 then New Orleans Jazzwhen 1930..1939 then Swingwhen 1940..1950 then Bebopelse Jazzend
  46. 46. Avoid line continuation# badresult = 1 - 2# badresult = 1 - 2
  47. 47. # badone.two.three.four# goodone.two.three.fourChain methods with .
  48. 48. Align multi-line params# starting point (line is too long)def send_mail(source)Mailer.deliver(to: bob@example.com, from:us@example.com, subject:Important message, body: source.text)end# bad (normal indent)def send_mail(source)Mailer.deliver(to: bob@example.com,from: us@example.com,subject: Important message,body: source.text)end
  49. 49. Align multi-line params# bad (double indent)def send_mail(source)Mailer.deliver(to: bob@example.com,from: us@example.com,subject: Important message,body: source.text)end# gooddef send_mail(source)Mailer.deliver(to: bob@example.com,from: us@example.com,subject: Important message,body: source.text)end
  50. 50. num = 1000000num = 1_000_000Which one is correct ?
  51. 51. num = 1000000num = 1_000_000Both !Which one is better ?
  52. 52. Add underscores to largenumeric literals# bad - how many 0s are there?num = 1000000# good - much easier to parsenum = 1_000_000
  53. 53. 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_paramcollect { |e| e.to_param }.join /endendUse RDoc and its conventionsfor API docs
  54. 54. Syntax
  55. 55. Use Iteratorsarr = [1, 2, 3]# badfor elem in arr doputs elemend# goodarr.each { |elem| puts elem }
  56. 56. # badresult = if some_condition then something else something_else end# goodresult = some_condition ? something : something_else* for one line constructsFavor the ternary operator (?:)over if/then/else/end
  57. 57. # badsome_condition ? (nested_condition ? nested_something :nested_something_else) : something_else# goodif some_conditionnested_condition ? nested_something : nested_something_elseelsesomething_elseendUse one expression per branchin a ternary operator
  58. 58. # badifsome_conditiondo_somethingdo_something_elseend# goodif some_conditiondo_somethingdo_something_elseendAlways put the condition onthe same line
  59. 59. Favor unless over if fornegative conditions# baddo_something if !some_condition# baddo_something if not some_condition# gooddo_something unless some_condition# another good optionsome_condition || do_something
  60. 60. class Personattr_reader :name, :age# omittedendtemperance = Person.new(Temperance, 30)temperance.nameputs temperance.agex = Math.sin(y)array.delete(e)bowling.score.should == 0Omit parentheses around parametersfor methods that are part of internalDSL, keyword status, accessors
  61. 61. names = [Bozhidar, Steve, Sarah]# badnames.each do |name|puts nameend# goodnames.each { |name| puts name }* Avoid using {...} for multi-line blocksPrefer {...} over do...end forsingle-line blocks
  62. 62. # baddef some_method(some_arr)return some_arr.sizeend# gooddef some_method(some_arr)some_arr.sizeendAvoid return where notrequired for flow of control
  63. 63. # baddef ready?if self.last_reviewed_at > self.last_updated_atself.worker.update(self.content, self.options)self.status = :in_progressendself.status == :verifiedend# gooddef ready?if last_reviewed_at > last_updated_atworker.update(content, options)self.status = :in_progressendstatus == :verifiedend* only required when calling a self write accessorAvoid self where not required
  64. 64. Naming
  65. 65. “The only real difficulties in programming are cacheinvalidation and naming things.- Phil Karlton
  66. 66. Name identifiers in English# bad - variable name written in Bulgarian withlatin characterszaplata = 1_000# goodsalary = 1_000
  67. 67. Use CamelCase forclasses and modules# badclass Someclass...endclass Some_Class...endclass SomeXml...end# goodclass SomeClass...endclass SomeXML...end* Keep acronyms likeHTTP, RFC, XMLuppercase
  68. 68. Use snake_case for symbols,methods and variables# bad:some symbol:SomeSymbol:someSymbolsomeVar = 5def someMethod...enddef SomeMethod...end# good:some_symbolsome_var = 5def some_method...end
  69. 69. Use SCREAMING_SNAKE_CASEfor other constants.# badSomeConst = 5# goodSOME_CONST = 5
  70. 70. Predicate methods shouldshouldend in a question mark ‘?’# baddef availableappointments.empty?end# gooddef available?appointments.empty?end* e.g. Array#empty?
  71. 71. Comments
  72. 72. “ Good code is its own best documentation ...- Steve McConnell“ Good code is like a good joke - it needs no explanation.- Russ Olsen
  73. 73. Avoid superfluous comments# badcounter += 1 # increments counter by one
  74. 74. Avoid writingcomments to explainbad code
  75. 75. An outdated commentis worse thanno comment at all !
  76. 76. DON’T do it !
  77. 77. Use CommentAnnotations• Use TODO to note missing features• Use FIXME to note broken code• Use HACK to note code smells• Use OPTIMIZE to note inefficient code
  78. 78. One you’ve addedannotations
  79. 79. DO do the TODO !
  80. 80. Classes & Modules
  81. 81. Use a consistent structure inyour class definitionsclass Person# extend and includeextend SomeModuleinclude AnotherModule# constantsSOME_CONSTANT = 20# attribute macrosattr_reader :name# other macros (if any)validates :name# public class methods are next in linedef self.some_methodend# followed by public instance methodsdef some_methodend# protected/private methodsend
  82. 82. Implement to_s ondomain classesclass Personattr_reader :first_name, :last_namedef initialize(first_name, last_name)@first_name = first_name@last_name = last_nameenddef to_s"#{@first_name} #{@last_name}"endend
  83. 83. Use the attr family of functionsto define trivial accessors# badclass Persondef initialize(first_name, last_name)@first_name = first_name@last_name = last_nameenddef first_name@first_nameenddef last_name@last_nameendend# goodclass Personattr_reader :first_name, :last_namedef initialize(first_name, last_name)@first_name = first_name@last_name = last_nameendend
  84. 84. What if I don’t like these‘rules’ ?
  85. 85. Rules are meantto be broken !http://flic.kr/p/dKyXLP
  86. 86. http://flic.kr/p/7MdXLXThese are justguidelines
  87. 87. Follow Style Guideconsistently
  88. 88. But,
  89. 89. “ A Foolish Consistency is the Hobgoblin of Little Minds- Essays: First Series by RalphWaldo Emerson
  90. 90. Consistency at what level ?• consistency with style guide - important• consistency with project - more important• consistency with module - most important• readability matters
  91. 91. https://twitter.com/AgileBorat/status/307791971991289856
  92. 92. When to break the rule ?• Applying the rule makes it less readable• To be consistent with surrounding code• Opportunity to clean up the mess
  93. 93. How to enforcethese rules ?
  94. 94. • 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 inspectionsHow to enforce these rules ?
  95. 95. • 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 notesHow to enforce these rules ?
  96. 96. https://github.com/blog/622-inline-commit-notes
  97. 97. Tools
  98. 98. Rubocophttps://github.com/bbatsov/rubocop
  99. 99. Demo
  100. 100. Like a Boss !
  101. 101. References• https://github.com/bbatsov/ruby-style-guide• https://github.com/styleguide/ruby• https://github.com/bbatsov/rails-style-guideStyle Guides
  102. 102. References• https://github.com/bbatsov/rubocop• https://github.com/martinjandrews/roodi• https://github.com/troessner/reekTools
  103. 103. Give it a shot
  104. 104. YMMV
  105. 105. Contribute your Styleto the community
  106. 106. ThankYou
  107. 107. Questions ?@bhavinjaviabhavin@mavenhive.inwww.mavenhive.in@mavenhive

×