-
Prefer method invocation over instance variables link
Example
## Bad class MarkRecipe def initialize(recipe) @recipe = recipe end def run @recipe.mark end end ## Good class MarkRecipe def initialize(recipe) @recipe = recipe end def run recipe.mark end private attr_reader :recipe end ## Also acceptable class Image def initialize(placeholder: false) @placeholder = placeholder end def render if placeholder? # ... end end private def placeholder? !!@placeholder end end ## Also acceptable class Pager def initialize @current_page = 1 end def run while continue? # ... go_to_next_page end end private attr_reader :current_page def go_to_next_page @current_page += 1 end end
-
Avoid using ternary operator link
Example
## Bad published? ? published_message : standard_message ## Good if published? published_message else standard_message end
-
Prefer short, focused methods (aim for 1-liners, longer than 5 is a red flag) link
-
Avoid method names ending in
!
("bang") without a corresponding bang-less, safe version of the method. linkExample
## Bad def method! # dangerous operation, without any safe version (prima-donna) end ## Good def method # dangerous operation, without any safe version end ## Good def method # safe version # It’s conventional to define the non-bang method in terms of the bang one, i.e. dup.method! end def method! # dangerous operation, with a safe version end
-
Prefer small, focused classes (100+ lines is a red flag) link
-
Prefer extracting private methods over setting variables inside methods link
Example
## Bad def method var_1 = # ... var_2 = # ... var_3 = # ... var_1 + var_2 + var_3 end ## Good def method var_1 + var_2 + var_3 end private def var_1 # ... end def var_2 # ... end def var_3 # ... end
-
Prefer
$stdin
,$stdout
,$stderr
overSTDIN
,STDOUT
,STDERR
link -
Avoid lines that end with conditionals (exception is guard clauses) link
Example
## Bad mark_related_items(:spam) if spam_detected? ## Good if spam_detected? mark_related_items(:spam) end ## OK for guard clauses, separate by space def approve return if approved? return if unapprovable? update(approved: true) end
-
Describe unexplained strings/numbers using constants link
Example
## Bad def params { gak: "UA-4235" } end ## Good def params { gak: GOOGLE_ANALYTICS_KEY } end ## OK - string explained by hash key def params { google_analytics_key: "UA-4235" } end
-
DRY multiple occurrences of values using constants link
Example
## Bad class Logger def log_params { google_analytics_key: "UA-4235" } end end class Tracker def config { google_analytics_key: "UA-4235" } end end ## Good class Logger def log_params { google_analytics_key: GoogleAnalytics::KEY } end end class Tracker def config { google_analytics_key: GoogleAnalytics::KEY } end end
-
Separate multiline fetch/query methods from the memoized method link
Example
## Bad def pizza_recipes @_pizza_recipes ||= Recipe. published. in_current_language. where("title LIKE ?", "%pizza%") end ## Good def pizza_recipes @_pizza_recipes ||= fetch_pizza_recipes end def fetch_pizza_recipes Recipe. published. in_current_language. where("title LIKE ?", "%pizza%") end
-
Avoid
unless
with boolean operator(s) in the conditional linkExample
## Bad unless client_id.present? && client_secret.present? raise "Missing credentials for OauthApplication #{name}." end ## Good - flip to if if client_id.blank? || client_secret.blank? raise "Missing credentials for OauthApplication #{name}." end ## Good - extract method if missing_credentials? raise "Missing credentials for OauthApplication #{name}." end def missing_credentials? client_id.blank? || client_secret.blank? end
-
Prefer a custom error class over a plain raise or raising NotImplementedError in abstract methods. link
Example
## Bad def feed_class raise NotImplementedError end def search_source raise "Not implemented yet" end ## Good def feed_class raise UnimplementedAbstractMethodError.new(self.class.name, __method__) end