A complex code usually is a very long method doing a lot of things. The first thing we do is to find the correspondent test for that method, make sure tests are passing, then using some of the following techniques to refactor and make sure tests still pass.
def price
order.quantity * order.itemPrice - [0, order.quantity - 500].max * order.itemPrice * 0.05 + [order.quantity * order.itemPrice * 0.1, 100].min
end
=>
def price
base_price = order.quantity * order.itemPrice
discount_by_quantity = [0, order.quantity - 500].max * order.itemPrice * 0.05
shipping_cost = [order.quantity * order.itemPrice * 0.1, 100].min
basePrice - quantityDiscount + shipping;
end
Once we got lots of variables in a long method, we can extract to a method.
Increase readability by naming what we are doing, foster reuse of methods.
def price
basePrice - quantityDiscount + shipping;
end
private
def base_price
order.quantity * order.itemPrice
end
def discount_by_quantity
[0, order.quantity - 500].max * order.itemPrice * 0.05
end
def shipping_cost
[order.quantity * order.itemPrice * 0.1, 100].min
end
A lot of times in Rails, we want to do something when user is not signed in. People may add logics in user model or in controller. But a better way is to introduce a Guest.
def current_user
super || Guest.new
end
class Guest
def do_something
end
end
def some_method_requires_signed_in
current_user.do_something
end
instead of
def some_method_requires_signed_in
if current_user.signed_in?
current_user.do_something
else
current_user.do_something_when_not_signed_in
end
end
Pass no more than four parameters into a method. Hash options are parameters.
You can wrap long list of parameters to a Parameter Object and use it accordingly.
def complex_method(a, b, c, d, e = {})
end
class Parameter
attr_reader :a, :b, :c, :d, :e
def initialize(a:, b:, c:, d:, e:)
@a = a
@b = b
@c = c
@d = d
@e = e
end
end
def complex_method(parameter)
# parameter.a, parameter.b, parameter.c ...
end
Replace conditionals!
if type == :some_condition
some_condition_plan.cost
elsif type == :another_condition
another_condition_plan.cost
else
default_plan.cost
end
plan = find_plan(type)
plan.cost