Hey everyone,
For a long time I've been writing boilerplate classes that have basically the same structure of initialization:
class UserRegistrationForm
def initialize(access_code)
@access_code = access_code
end
private
attr_reader :access_code
endSince I'm generating a whole bunch of similar classes like this, I've gotten tired of writing the same boilerplate code over and over again.
A year or two ago I found about a library called attr_extras that at first I didn't want to use since I didn't feel the real benefit of using it, but recently I said to myself that I should probably look it up again, and boy was I right.
The gem provides a couple of cool boilerplate cutdowns:
The same class that I've wrote above can be written like this:
class UserRegistrationForm
pattr_initialize :user
endThe pattr_initialize method does two things:
- creates an initializer that takes in the argument you've passed it, in this case, the
user, and assigns it to an instance variable@user - creates a private reader for that argument.
Some of you know about Structs in Ruby, and that most of this can be implemented with Ruby's standard library:
Customer = Struct.new(:name, :address) do
def greeting
"Hello, my name is #{name.capitalize}!"
end
end
Customer.new('John', 'Lemon Ave')This is also a shorter way of creating an initializer and accessors, however I've got a couple of problems with the struct class:
- it creates public readers and writers, while I only want readers. I'm a firm believer that accessors are a bad thing in almost any case, especially command/service classes, since you shouldn't alter the inner variables after initialization.
- you can initialize the Customer class without any attributes -> this is valid:
Customer.new. This leads to unwanted behavior that propagates errors down the line when you call a method on that class. So when you callCustomer.new.greeting, it will throw an NoMethodError for capitalize only when it comes to greeting (a step too late)
I've also been writing most of my clases with one public method, named call, and sometimes I create a class method to easily stub it out in tests:
class UserRegistrationForm
def self.call(access_code)
new(access_code).call
end
def initialize(access_code)
@access_code = access_code
end
def call
...do stuf...
end
private
attr_reader :access_code
endI usually write the class methods for two reasons:
- it's shorter to call the class method and it's easier to stub out during tests and looks nicer.
- Stubbing out a instance method:
allow_any_instance_of(Widget).to receive(:name).and_return("Wibble") - Stubbing out a class method
allow(Widget).to receive(:name).and_return("Wibble")
- Stubbing out a instance method: