Ruby Code Style
Use rubocop, reek and flay tools to detect issues in code. If for some cases default configuration does not make sense and whole team agrees, then local exception can be made.
Rubocop
- Use tweaked default rubocop configuration to be less strict on code complexity:
# Write longer lines because in many cases code looks better
Metrics/LineLength:
Max: 120
# Default settings are too strict for complex rails applications
#
# Allow more complex methods
Metrics/AbcSize:
Max: 20
# Allow longer methods
Metrics/MethodLength:
CountComments: false
Max: 15
# We prefer to write multi-line expect blocks with curly braces in specs
Style/BlockDelimiters:
Exclude:
- 'spec/**/*'
Rails:
Enabled: true
AllCops:
TargetRubyVersion: 2.3
Include:
- '**/Rakefile'
- '**/config.ru'
# Don't lint auto-generated files
Exclude:
- 'db/**/*'
- 'script/**/*'
- 'bin/**/*'
- !ruby/regexp /client\/node_modules/
Style/CollectionMethods:
PreferredMethods:
# Don't confuse with ActiveRecord#find
find: 'detect'
# Write full parameter name instead of few letters
Style/SingleLineBlockParams:
Enabled: false
# Performance gain is not worth the hassle
Performance/Casecmp:
Enabled: false
# Lots of gems and own code does not support this yet
Style/FrozenStringLiteralComment:
Enabled: false
# Allow passing classes to ActiveRecord scopes
Rails/ScopeArgs:
Enabled: false
Reek
- Default reek configuration adapted for rails:
# Nature of rails helpers and validators require feature envy exception
FeatureEnvy:
exclude:
- !ruby/regexp /Helper/
- !ruby/regexp /Validator/
# Nature of rails helpers, validators, background jobs and concerns require utility function exception
UtilityFunction:
exclude:
- !ruby/regexp /Helper/
- !ruby/regexp /Validator/
- !ruby/regexp /Job/
- !ruby/regexp /ClassMethods/
# Rails mailers tend to get quite big and have lots of instance variables
TooManyInstanceVariables:
exclude:
- !ruby/regexp /Mailer/
# Rails helpers tend to have lots of methods that might look similar
DataClump:
exclude:
- !ruby/regexp /Helper/
# Allowing one level of nesting is way too restrictive.
NestedIterators:
max_allowed_nesting: 2
# No need for repetitive comments to namespace modules
IrresponsibleModule:
enabled: false
"app/controllers":
TooManyStatements: # Rails actions tend to have more statements, particularly within respond_to blocks
max_statements: 7
Flay
- Don’t check views with flay - most of the reported issues are false positives (input fields, forms, links, image tags, etc).
- Don’t check controllers with flay - most of the reported issues are false positives (permitted params, respond_to blocks, etc).
Attribute accessors
- Use
attr_reader
to define attributes and expose them publicly only if it is needed - Do not expose
attr_writer
publicly - Do not expose
attr_accessor
publicly (only exception is for form objects; otherwise, form objects won’t work correctly) - Use defined
attr_reader
,attr_writer
andattr_accessor
attributes inside the class instead of instance variables - Use instance variables only when defining custom setters/getters, caching and inside mailers
A simple example:
- The class below uses attribute writers to save passed parameters
lang_doc
andlearn_doc
- It also exposes those attributes publicly (let’s say it is needed)
LanguageDocumentComposer
also setserrors
attribute through an attribute writer andcounter
attribute through an attribute accessorerrors
attribute is exposed publicly whereascounter
attribute is only used internally- Instance variables aren’t used at all
class LanguageDocumentComposer
attr_reader :lang_doc, :learn_doc, :errors
def initialize(lang_doc, learn_doc)
self.lang_doc = lang_doc
self.learn_doc = learn_doc
self.errors = []
self.counter = 0
end
def valid?
errors.blank?
end
def compose
if lang_doc.valid? && learn_doc.valid?
composed_doc
else
errors.concat(lang_doc.errors)
errors.concat(learn_doc.errors)
nil
end
end
private
def composed_doc
...
counter += 1;
end
attr_writer :lang_doc, :learn_doc, :errors
attr_accessor :counter
end