Matthew Johnston

Professional Code Janitor

A Ruby Yukata

Let me introduce you to my new library called Yukata. It is a light weight Ruby attribute library that is configurable and extendable.

Virtus has been a huge inspiration for this library. I enjoyed the DSL it offered, while allowing me to have a quick way to make data objects.

Here is an example on how to utilize Yukata:

class Person < Yukata::Base
  attribute :first_name, String
  attribute :last_name,  String
  attribute :born_on,    DateTime
  attribute :married,    Boolean, default: -> { false }
end

The #attribute method is straight forward with its meaning. It is dynamically creating both getter and setter methods for the object. It can be thought of as a fancy attr_accessor but with a few extra features. It provides a fast way to discover what data type can be expected for that attribute.

Example Usage

When using Yukata, the the initializer expects a hash to be provided or a class that behaves like a Hash.

john = Person.new({
  :first_name => 'John',
  'last_name' => 'Doe',
  :born_on => '1969-01-16T00:00:00+00:00'
})

Yukata will take the hash and assign the values to their respective attribute keys. If a setter method is defined, then a corresponding value can be passed as well.

class Foo < Yukata::Base
  attr_accessor :bar
  attribute :qux, String
  attribute :baz, String, writer: false

  def baz=(value)
    @baz = value.to_s
  end
end

foo = Foo.new({
  bar: 'woot',
  qux: 'herp',
  baz: 'derp'
})

foo.bar # => 'woot'
foo.qux # => 'herp'
foo.bas # => 'derp'
foo.attributes # => { bar: 'woot', qux: 'herp' }

If a :coerce => false is passed, then Yukata will not attempt to coerce that attribute and leave it as is. This can be handy if a custom coercion is desired for the specific model. Here is an example:

class Episode < Yukata::Base
  attribute :season, Integer
  attribute :number, Integer
  attribute :name,   String, coerce: false

  # @override overides the yukata definition
  def name=(value)
    @name = '%sx%s - %s' % [@season, @number, value]
  end
end

episode = Episode.new({ season: 1, number: 1 })
episode.name = 'Foo Bar'
episode.name # => '1x1 - Foo Bar'

Now, remember just because there is access directly to instance variables does not mean it is okay to abuse them. With great power comes great responsibility, this means I am not responsible for your mistakes.

Setting Attribute Defaults

Sometimes the objects need default values if it is not set. Defaults are lazily loaded. They will only be set once the getter method is called.

class Book < Yukata::Base
  attribute :name,       String
  attribute :created_at, DateTime, default: -> { DateTime.now }
end

Registering Custom Coercions

This library only comes with basic coercers. I tried to make as little assumptions about the data coming in as I could. I believe that the consumer of the library should be the one who defines the coercions.

If the value can not be coerced, it is simply passed through and left alone.

Yukata.coercer.register(String, Array) do |string, target|
  string.split(' ')
end

Optional Readers and Writers

When declaring an attribute, both the reader and writer can be skipped. There is a use case where this would be handy.

class Book < Yukata::Base
  attribute :title, String, writer: false, reader: false

  def title=(value)
    @title = value.to_s
  end

  def title
    @title
  end
end

This is a bit contrived, but it demonstrates the following:

  1. The expected return data type for #title is a String.
  2. Custom coercer is defined.
  3. The attribute will be included when #attributes is called on Book.

If :writer => false is provided, there would be no need to include :coerce => false since the coercion only takes place when the value is being set on the object.

Conclusion

I wrote this library becaues I wanted to see how Virtus accomplished this task and how I could go about doing it differently. This is a highly configurable library that can be used to put your fat models on a diet.

References

Responding With Errors in Rails

As I work on API projects I find myself having to track down status codes and ensuring a consistent response code for any given error. Keeping these status codes consistent throughout the application starts to become a hassle.

A solution that I stumbled upon happened to be something very simple.

# /lib/errors/unauthorized_access_error.rb
module Errors
  class UnauthorizedAccessError
    def status
      403
    end

    def message
      'unauthorized access'
    end

    def to_hash
      {
        meta: {
          code: status,
          message: message
        }
      }
    end

    def to_json(*)
      to_hash.to_json
    end
  end
end

In my controller I would do the following:

class API::V1::UsersController < API::V1::ApplicationController
  before_filter :authorize!

  def index
    render(json: account.users)
  rescue Errors::UnauthorizedAccessError => error
    render(json: error, status: error.status)
  end
end

Very simple, and very elegant. The status code travels with the UnauthorizedAccessError class and is very well self documenting. This is a very simple example however, the principle still remains that the errors themselves carry the burden of what the HTTP response codes should be and what the response should look like.

It could get a little tedious to keep rescuing from that one error every method, fortunately Rails comes with a rescue_from and you can use that to blanket your application.

class API::V1::ApplicationController
  rescue_from Errors::UnauthorizedAccessError, with: :render_error

  def render_error(error)
    render(json: error, status: error.status)
  end
end

# Your other controller would then look like this!
class API::V1::UsersController < API::V1::ApplicationController
  before_filter :authorize!

  def index
    render(json: account.users)
  end
end

You could even take this a step further and make all of your error classes decend from a common parent like Errors::Error and then do the following:

class API::V1::ApplicationController
  rescue_from Errors::Error, with: :render_error

  def render_error(error)
    render(json: error, status: error.status)
  end
end

The possibilities are endless, but the benefits are huge. Keep things simple and you'll enjoy the new found power.

Test Spies

Test spies are a wonderful tool to utilize in the RSpec testing environment. When used in moderation and with care. Test spies require that a called method be stubbed so that it can be checked to see how it was invoked.

I have put together a really simple class to demonstrate a test spy.

class NotifyUser
  attr_reader :user

  def initialize(user)
    @user = user
  end

  def execute(params={})
    mailer.notify({ message: params[:message] })
  end

  def mailer
    SomeMailer.new(user)
  end
end

Notice in the test below that a test spy follows a pattern. There is a mock up section, an excercise section, and a verification section. Each of these are important and I always put a space in between the sections. It's much easier to see what is happening in the test.

describe NotifyUser do
  let(:user) { double('User') }
  let(:command) { NotifyUser.new(user) }

  describe '#execute' do
    it 'sends the notification to the user' do
      # Mock up
      mailer = double('SomeMailer', notify: true)
      command.stub(mailer: mailer)

      # Excercise
      command.execute({message: 'Hello'})

      # Verification
      expect(mailer).to have_received(:notify).with({ message: 'Hello'})
    end
  end
end

Mocking up outside of the it block should be kept to a minimum. This is because it can get to be a little hectic trying to understand what the test is doing. I like tests that are readable and succinct.

If the length of a test file is forcing me to start mocking outside of it blocks, I like take a step back and ask myself, "Is this class really complex?" It is important to realize when tests are getting more and more difficult to maintain, that the code base is most likely really coupled.

When to use them

Test spies are not meant to be used everywhere. I typically use them when a class is communicating with an external object. I will stub the method that wraps the object and make it return a double. This is so if the code base does change, this test will fail quickly and force the developer to look at what it failed and possibly refactor tests.

Do not apply liberally, but do apply where necessary.

Command Objects

In any Unix based OS the command line reigns supreme. Commmands are predictable, and return zero for success and non-zero for failures.

Rules

The same principles can be applied to programming with some minor rule alterations.

Example

A basic command object will look like the following:

class CreateUser
  attr_reader :user

  # @param [Hash] params
  def initialize(params={})
    @user = User.new(params)
  end

  # @return [Boolean]
  def execute
    user.persisted? ? false : user.save
  end
end

Inside of the controller it would be applied like this:

class RegistrationsController < ApplicationController
  # ...

  def create
    command = CreateUser.new(user_params)

    if command.execute
      sign_in(command.user)
      redirect_to welcome_path
    else
      @user = command.user
      render action: 'new'
    end
  end

  # ...
end

These rules are fairly simple to follow. The command object is not supposed to be complex. It is supposed to break other complex tasks down into digestable, easy to maintain, and an easy to test format. If the command starts to become complex, consider extracting code into services or refactoring the command.

if all you have is a hammer, everything looks like a nail - Abraham Maslow

Remember not everything looks like a nail, so apply this design pattern with care and understand why others may opt for this in their application and why it may or may not be a good fit for your application.

Resources

State Machines

A state machine is an interesting design pattern.

The state_machine gem is a great library. It provides a nice structure to declare states and transitions. It also provides nice callback hooks that can be utilized to run specific actions before or after a transition happens.

It has hooks into ActiveRecord and saves the model when the transition from one state to another is successful. The gem also works just fine with plain old ruby objects as well. Here is a simple example of a state machine.

class Ticket < ActiveRecord::Base
  state_machine :state, :initial => :stopped do
    state :stopped
    state :started

    event :start do
      transition :stopped => :started
    end

    event :stop do
      transition :started => :stopped
    end
  end
end

To interact with the state machine, you can do the following:

ticket = Ticket.create(subject: 'New issue with post', content: 'Some text')
ticket.start #=> true
ticket.state #=> 'started'
ticket.start #=> false
ticket.state #=> 'started'
ticket.stop  #=> true
ticket.state #=> 'stopped'

Earlier in Dumb Data Objects I talked very briefly about how to integrate a state machine into a Dumb Data Object. What this gem brings to the table is another way back into "Callback Hell", and that is something everyone should avoid.

The transition callbacks are there to be used sparingly. If the need arises to apply a callback to the state machine, think about the implications this can have on the object through out its lifetime. Will this callback make interaction easier or will it complicate the model and make it difficult for someone else to pick up?

One callback that is typically rife with code smell is the after_transition callback. This callback will trigger an action after a transition from one state to another has taken place. Why not just execute that method after the event was triggered? It's very easy to do.

State machines are supposed to be simple. If transitions become complex, then the state machine flow becomes disruptive and difficult to ascertain. The ultimate goal is to move crazy logic out of the model and push that off into service objects and command objects like the following:

class StartTicket
  def initialize(ticket)
    @ticket = ticket
  end

  def execute
    if @ticket.start
      notify = NotifyTicketSubscribers.new(@ticket)
      notify.send_ticket_started_email
      true
    else
      false
    end
  end
end

If I were to mix in the Signals Gem and implement a command pattern, it would look like the following:

class StartTicket
  include Signals::Publisher

  def initialize(ticket)
    @ticket = ticket
  end

  def execute
    if @ticket.start
      broadcast(:start_ticket_successful, @ticket)
    else
      broadcast(:start_ticket_failed, @ticket)
    end
  end
end

Testing StartTicket becomes really easy at this point.

# Use 'rspec', >= '2.14.0.rc1' in order to
# utilize test spies
describe StartTicket do
  describe '#execute' do
    it 'should start the ticket' do
      # Mock
      ticket = double('Ticket', start: true)
      command = StartTicket.new(ticket)
      command.stub(broadcast: true)

      # Excercise
      command.execute

      # Verify
      command.should(
        have_received(:broadcast).
        with(:start_ticket_successful, ticket)
      ).once
      ticket.should have_received(:start).once
    end
  end
end

The after_transition callbacks will be pushed off onto the listeners and before_transition calls will be done before the event is ever triggered in the StartTicket#execute method.

class TicketListener
  include Signals::Subscriber

  listen_for :start_ticket_successful => :send_ticket_started_email
  listen_for :stop_ticket_successful => :send_ticket_stopped_email

  def send_ticket_started_email(ticket)
    # Send out emails
  end

  def send_ticket_stopped_email(ticket)
    # Send out emails
  end
end

Piecing it all together, results in this simple use case.

ticket  = Ticket.find(1)
command = StartTicket.new(ticket)
command.subscribe(TicketListener.new)
command.execute

Yes there is a lot of boiler-plate code, but trust me when I say the benefits greatly out weighs the drawbacks.

For the love that is programming and refactoring, stay away from callbacks!

Resources