Why? Makes it simpler to locate and organize code
Why? This is where most Rubyists will expect it, and clearly delineates your app from lib extensions
Code for a class/module goes in the lower-cased, snake-cased named file, in a directory structure that matches the module namespaces. For example, the class Foo::Bar::Baz
should be in foo/bar/baz.rb
, relative to lib
or ext
Why? Makes it simpler to locate the code for a given class
Test::Unit
files should go in test
and be named class_test.rb
, e.g. to test the class Foo
, create test/foo_test.rb
.
Why? Test dir is then sorted by classname, making it easy to visually scan or auto-complete test names
Why? this is standard across almost all Ruby code
Do not bother aligning arrows, equals, or colons. If there are so many grouped together, consider restructuring your code
Why? Alignment requires extra work to maintain, and results in hard-to-understand diffs when alignment must change
Why? Single-line methods are harder to change when the method needs an additional line
Keep line lengths 80 characters or less unless doing so negatively impacts readability (e.g. for large string literals)
Why? Excessively long lines can be very difficult to read and understand; a low line length encourages keeping things simple
Why? This makes it easier to add elements and reorder elements without having to worry about missing commas
Why? This makes it easier to read and modify the options sent to the method
some_call(non_option1,non_option2,:foo => "bar",
:blah => "quux",
:bar => 42)
Why? This makes it easier to add new attributres without creating complex diffs. It also affords documenting what the attributes represent
# Wrong; hard to modify
attr_accessor :first_name, :last_name, :gender
# Right; we can easily modify and document
attr_accessor :first_name
attr_accessor :last_name
attr_accessor :gender
Why? Two-dimensional data is hard to read and modify.
Why? No reason, just my personal preference
class SomeClass
def public_method
end
protected
def protected_method
end
private
def private_method
end
end
Why? Visually sets them off
Why? Visual clearance
Why? I don't get much value from visual clearance for the end blocks that 'close' a class or module
Why? This reduces errors caused by moving methods around, and also doesn't require you to scroll up in the class to determine what type of method it is; you can see it instantly
Why? This makes it very clear that you are using if..else..end
as a complex expression and not as a control structure
# Wrong
result = if something?
:foo
else
:bar
end
# Right
result = if something?
:foo
else
:bar
end
Why? Abbreviations can clash, require explanation and generally disrupt the flow of things
Why? No reason not to be descriptive
For non 'primitive' types, use the name of the class, or the name of the class plural if a collection, unless there will be multiple instances in scope, then do not follow this convention
Why? When there's no particular specific name for something, using its type makes it easy to remember what the object is
# Just use the classname
def routine
customer = Customer.find(124)
customers = Customer.find_all_by_gender("M")
end
# Here we have two lists, so neither should just be "customers"
def other_routine
males = Customer.find_all_by_gender("M")
minors = Customer.where('age < ?',18)
end
# Here we have a very long method that, even though it should be refactored,
# isn't, so we use a more verbose variable name to keep things clear 50 lines in.
def other_routine
# tons of code
male_customers = Customer.find_all_by_gender("M")
minor_customers = Customer.where('age < ?',18)
# tons more code
end
Why? Procs and lambdas are more like methods and thus should be verbs, since they do something
# Wrong; the variable has been needlessly "nounified" for no real benefit
saver = lambda { |x| x.save! }
# Correct; the variable, being a verb, is instantly recognizable as an action
save = lambda { |x| x.save! }
Why? Since Ruby allows the '=' form, using 'get' or 'set' in the names just adds noise
Why? This allows methods that can used in expressions to clearly stand out and makes code more fluent
Why? This calls out methods whose use should be carefully understood
# Mutates the object; dangerous on an otherwise immutable object
def process!
@processed = true
end
# Has a side-effect that is not obvious and might not be idempotent
def render!(output)
queue_event(:some_event)
output.puts @content
end
# Raises an exception on failure, unlike its analog, save, which does not
def save!
raise ValidationException unless self.save
end
Why? Prevents naming clashes when your code is used with other libraries
Why? Ensures that classnames are understood everywhere used.
# Wrong; 'Base' is not an accurate classname
class ActiveRecord::Base
end
# Good; using the class without its namespaced module doesn't remove any clarity
class Gateway::BraintreeGateway
end
Why? Classes represent things, and things are nouns
Why? Modules used as mixins represent a trait or aspect that you want to use to enhance a class. These are naturally adjectives.
Why? Using modules just for namespacing is, again, specifying things, which are nouns
Why? This syntax only works if your hash keys are symbols; otherwise you have to use the old syntax. There's a very limited benefit to the new syntax, and the cost is two ways of doing things, increasing mental overhead when reading/writing code. Further, for libraries, 1.8 compatibility is nice to achieve
Why? Many shops are still on 1.8, and many systems have 1.8 as the default, so there's no reason not to keep 1.8 compatible for code that will be shared
Why? This visually sets them off, and makes for a cleaner chaning syntax
# Wrong; calling a method on 'end' is awkward looking
male_teens = Customers.all.select do |customer|
customer.gender == :male
end.reject do |man|
man.age > 19 || man.age < 13
end
# Right; the chaining and use of the comprehensions is clear
male_teens = Customers.all.select { |customer|
customer.gender == :male
}.reject { |man|
man.age > 19 || man.age < 13
}
Why? These blocks are more control-structures, so do..end
is more natural. It sets them off from blocks that produce a useful value
Always use parens when calling methods with arguments unless you are coding in a heavy 'DSL Style' bit of code
Why? Parens visually set off the parameters, and reduce confusion about how Ruby will parse the line, making the code easier to maintain
# Given this code
class Person
def self.create(name,status)
# ..
end
end
Person.create 'Dave', :single
# and we change it so that :status is based on a condition
# Wrong; what does this line do?
Person.create 'Dave', wife || :single
# If we used parens from the get go, it's a no brainer
Person.create('Dave',:single)
# Right; code is clear
Person.create('Dave',wife || :single)
Why? the expression becomes too difficult to unwind, just use an if
Do not use an unless
for any expression that requires a ||
, &&
, or !
. Either extract to a method or use if
Why? unless
is like putting a giant !()
around your expression, so it becomes harder and harder to understand what is being tested by adding this. It's not worth it
# Wrong; too hard to figure out
unless person.valid? && !person.from('US')
# doit
end
# Right; DeMorgan's law simplified this
if !person.valid? || person.from('US')
# doit
end
# Better; use a method
unless valid_and_foreign?(person)
# doit
end
Why? Code can become hard to read when conditions are trailing and complex. Early-exit conditions are generally simple and can benefit from this form
# Wrong; too complex
person.save unless person.from('US') || person.age > 18
# OK; an early exit
raise "person may not be nil" if person.nil?
Why? Exception
is the base of all exceptions, and you typically don't want to catch memory exceptions and the like. Catch StandardError
instead
When you must mutate an object before returning it, avoid creating intermediate objects and use tap
:
Why? Intermediate objects increase the mental requirements for understanding a routine. tap
also creates a nice scope in which the object is being mutated; you will not forget to return the object when you change the code later
# Wrong
def eligible_person(name)
person = Person.create(:name => name)
person.update_eligibility(true)
person.save!
person
end
# Right
def eligible_person(name)
Person.create(:name => name).tap { |person|
person.update_eligibility(true)
person.save!
}
end
Feel free to chain Enumerable
calls using simple blocks instead of one complex block that does everything.
Why? The braces syntax encourages clean chaning, and with simple blocks, you can easily follow step-by-step what is being done without having to have a bunch of private methods explaining things.
Avoid conditionals based on the truthiness or falsiness of a variable. Use .present?
or .nil?
to be clear as to what's being tested
Why? It's a lot clearer when you state exactly what you are testing; this makes it easier to change the code later, and avoids sticky issues like 0 and the empty string being truthy
# Wrong; intent is masked
if person.name
# do it
end
# Right; we can see just what we're testing
if person.name.present?
# do it
end
Why? Returning a 'truthy' value will lead to unintended consequences, and could lead to complex dependencies in your code. You don't need the hassle
Why? A class with one method, especially Doer.do, is just a function, so make it a lambda
Why? This gives you a single place to document which methods subclasses are expected to implement, and ensures that, at least at runtime, they are implemented
Avoid instantiating classes inside other classes. Prefer dependency injection and default parameter values.
Why? This makes it easier to test the classes, but doesn't require Herculean efforts to instantiate classes at runtime:
class PersonFormatter
def format(person)
# whatever
end
end
# Wrong; we are tightly coupled to an implementation and have to use
# crazy mocks to fake this out
class View
def render
# code
puts PersonFormatter.new.format(person)
# more code
end
# Correct; Option 1 - constructor injection
class View
def initialize(person_formatter=PersonFormatter.new)
@person_formatter = person_formatter
end
def render
# code
puts @person_formatter.format(person)
# more code
end
end
# Correct; Option 2 - setter injection with a sensible default
class View
attr_writer :person_formatter
def initialize(person_formatter)
@person_formatter = PersonFormatter.new
end
def render
# code
puts @person_formatter.format(person)
# more code
end
end
Why? Public is the way to formally document the contract of your class. Putting methods that shouldn't be called by others in the public interface is just lazy, and increases maintenance down the road.
protected
is likely not correct; only use it for the template method pattern and even then, would a lambda or proc work better?
Why? Protected is used to allow subclasses to call non-public methods. This implies a potentially complex type hierarchy, which is likely not correct for what you are doing.
Why? It's a convienient pattern to add macro-style methods to classes, and is used in Rails source, so will be easily recognizable by others
module Helper
def self.included(k)
k.extend ClassMethods
end
module ClassMethods
def strategy(strat)
@strategy = strat
end
end
end
class UsesHelper
include Helper
strategy :foo
def doit; puts self.strat; end
end
Why? Instance variables are a form of global data, and your routines' complexity increases when their input comes from multiple sources. If the instance variables control flow or logic, pass them in as parameters
private methods should be used to make public methods clear; they should avoid reliance on ivars if at-all possible
Why? Private methods that do not rely on instance variables can very easily be extracted to new classes when things get compledx
private methods calling more private methods might indicate a second class is hiding inside this class
Why? RDoc.info does not support TomDoc, and YARD is way too heavyweight
Why? RDoc will link to methods or classes in your project
Why? This makes it clear that you mean a method name or class, because RDoc cannot link outside of your codebase
Why? Don't restate what the code does, but DO explain why it works the way it does, especially if it does something in a suprising or weird way, from a business logic perspective
## Wrong; don't explain what the code does, we can read it
def minor?
# Check if they are under 19
self.age < 19
end
## Right; explain the odd logic so others know it is intentional, with
## a ref for more info as to why
def minor?
# For our purposes, an 18-year-old is still a minor. See
# ticket XYZ for a more detailed discussion
self.age < 19
end
Why? Because a README is a nice way to explain what your code is/does
Why? Summarizing things in one line is helpful
Why? Because not everyone knows what needs to be done, even if it's just gem install
Why? This, along with the description allows someone to understand your library/app in under a minute
Why? When rendered as RDoc, these classes link to where the user should start reading to get a deeper perspective
Why? If you want contributions, developers need to know how to work with your code
Why? There's no other way to tell what the types are and it's just not nice to hide this info
Why? Because it's jerky not to; there's no other way to know what they are
# Makes a request
#
# url:: url to request
# options:: options to control request:
# +:method+:: HTTP method to use as a String (default "GET")
# +:content_type+:: Mime type to include as a header, as a String (default "text/plain")
# +:if_modified_since+:: Date for if-modified-since header, default nil
def request(url,options={})
end
Why? Good method names are preferred, but if it's somewhat complex, add a bit more about what the method does
Why? Again, the parameter names should be meaninful, but if they don't full explain things, throws us a bone
Why? These valuers show up in rdoc, so restating them is just a maintenance issue
Why? No other way to know what the types are
Why? Naming is hard; documentation helps explain what a class is for
For non-namespaced modules, the Rubydoc should include the names and purpose of all methods that a class is expected to provide when mixing in
Why? Because we don't have types, the user of your module needs to know what methods to implement to make the module work
Why? This lets someone see, at a glance, what the construct is for
Why? We know what kind of thing it is; just state what it does
Why? This makes it clear what each section of the test is doing, which is crucial when tests get complex
def test_something
# Given
person = Person.new("Dave",38)
# When
minor = person.minor?
# Then
assert minor,"Expected #{person.inspect} to be a minor"
end
Why? It's important to established what the mock expectations are, and thinking of it as seperate from a given helps the test read better.
def test_something
# Given
person = Person.new("Dave",38)
# When the test runs, Then
AuditStats.expects(:query).returns(true)
# When
minor = person.minor?
# Then
assert minor,"Expected #{person.inspect} to be a minor"
end
Why? It's important to let others know your intent that the mock expectations ARE the test and that you just didn't forget an assert
def test_something
# Given
person = Person.new("Dave",38)
# When the test runs, Then
PersonDAO.expects(:save).with(person)
# When
person.save!
# Then mock expectations should've been met
end
Why? Again, this preserves the flow of the test
def test_something
# Given
name = 'Dave'
# When
code = lambda { Person.create(:name => name) }
# Then
assert_difference('Person',&code)
assert_equal name,Person.last.name
end
Avoid literals that aren't relevant to the test; abstract into a any
method or some
method, e.g. some_int
Why? Tests littered with literals can be very hard to follow; if the only literals in the test are those specific to this test, it becomes very clear what's being tested.
Why? Just like magic strings are bad in real code, they are in test code. Plus it codifies that the values are the same by design and not just by happenstance
Why? If you are running a different test under the exact same conditions as another test, extract that duplicative code into a helper. Similarly, if you are conducting a different test that should have the same outcome, extract those assertions to a helper.
Why? This sort of duplication is OK becauser they code is only the same by happenstance, and may diverge as the code matures. By leaving it seperate, it's easier to change
Why? Some RSpec constructs assert things about the class under test without calling its public API, e.g. person.should be_valid
. This goes against the spirit of TDD, and requires the reader to make a mental transaction between the testing DSL and the class' method, with no discernable benefit.
Why? raise
will mark your test as erroneous, not failed, as that is the case when the setup conditions for the test aren't correct. Better yet, don't use brittle globally-shared test fixtures or factories.
Why? As an app matures, the fixtures or factories become incredibly brittle and hard to modify or understand. It also places key elements of your test setup far away from the test itself, making it hard to understand any given test.
Why? if
statements usually imply business logic, which does not belong in controllers. The logic in the controller should be mainly concerned with send the correct response to the user.
Why? When the number of filters increases, it becomes harder and harder to know what code is executing and in what order. Further, when filters set instance variables, it becomes increasingly difficult to understand where those variables are being set, and the filters become very order-specific. Finally, conditional filters, or filters used on only one controller method increase complexity beyond the point of utility
Why? By lazily creating all routes for a resource, when you only need a few to be valid, you create misleading output for newcomers, and allow valid named route methods to be created that can only fail at runtime or in production.
# Wrong; our app only supports create and show
resources :transactions
# Right; rake routes reflects the reality of our app now
resources :transactions, :only => [:create, :show]
Why? When views navigate deep into object hierarchies, it becomes very difficult to understand what data the views really do require, and it makes refactoring anything in those object hierarchies incredibly difficult
# A view for a person's credit cards requires the person's name, and a list of last-4, type, and expiration date of cards
# Wrong; the view must navigate through the person to get his credit cards and has
# access to the entire person objects, which is not needed
def show
@person = Person.find(params[:person_id])
end
# Wrong; although the view can now access credit cards directly, it's still not clear what data
# is really needed by the view
def show
@person = Person.find(params[:person_id])
@credit_cards = @person.credit_cards
end
# Right; the ivars represent what the view needs AND contain only what the view needs.
# You may wish to use a more sophisticated "presenter" pattern instead of OpenStruct
def show
@person_name = Person.find(params[:person_id].full_name
@credit_cards = @person.credit_cards.map { |card|
OpenStruct.new(:last_four => card.last_four,
:card_type => card.card_type,
:expiration_date => [card.expiration_month,card.expiration_year].join('/'))
}
end
Why? Using instance variables to avoid passing parameters is lazy and creates complex and hard-to-understand code. In a controller, instance variables are special: they represent the data passed to the views, and that's all they should be used for.
Why? Hooks make your models very hard to use in different ways, and lock them to business rules that are likely not all that hard and fast. They also make testing very difficult, as it becomes harder and harder to set up the correct state using objects that have excessive hooks on them.
# Wrong; we've hidden business logic behind a simple CRUD operation
class Person < ActiveRecord::Base
after_save :update_facebook
private
def update_facebook
# send Facebook some status update
end
end
# Better; we have a method that says what it does
class Person < ActiveRecord::Base
def save_and_update_facebook
if save
# Send Facebook some status update
end
end
end
Why? Validations that are not always applicable make it very hard to modify objects and enhance them, because it becomes increasingly difficult tor understand what a valid objects really is. Further, it becomes very difficult to set up objects in a particular state for a given test if there are a lot of conditonal validations
Why? The database is the only place that can truly ensure various constraints, such as uniqueness. Constraints are incredibly useful for making sure that, regardless of bugs in your code, your data will be clean.
Why? It may be tempting to add business logic to your models. This instanvce violates the single responsiblity principal, but it also makes the classes harder and harder to understand, test, and modify. Treat your modesl as dumb structs with persistence, and put all other concerns on other classes. Do not just mix in a bunch of modules.