The 4 Rules of East-oriented Code: Rule 1

4 simple rules are pretty easy to remember, but a bit harder to understand and apply.

A key concept of East-oriented Code is to enforce the use of commands by returning the object receiving a message.

Here's a simple example of what that looks like:

def do_something
      # logic for doing something omitted...
      self
    end

It's incredibly simple to follow.

Here are the rules I set forth in my presentation at RubyConf:

  1. Always return self
  2. Objects may query themselves
  3. Factories are exempt
  4. Break the rules sparingly

The first three are hard rules. The fourth, obviously, is more lenient. We'll get to some guidance on breaking the rules in the future but for now let's look at applying this to your code.

Rule 1: Always return self

Although this rule is simple at first, it inevitably leads to the queston of getter methods.

What if your objects had no getters? What if an object's name attribute simply was inaccessible to an external object?

You can make your data private by either marking your attr_accessors as private:

attr_accessor :name
    private :name

Or you can use the private method to mark all of the following defined methods to be private:

private
    attr_accessor :name

How you choose to do it will depend upon your code, but this would help you remove any getter methods.

Now this leaves you with a conundrum. How do you use the information?

If you have a need for that name, what can you do?

The only answer is to create a command which will apply the data to the thing you need.

def apply_name_to(form)
      form.name = name
      self
    end

The restricitions we put in our code are often self-imposed.

We can make whatever we want, so what's to stop us from putting Rails model data manipulation in it's view template? Nothing concrete stops us from doing so.

The same goes for getter methods like name. If it is publicly accessible by external objects, then we can create whatever if and case statements we want. We can put logic wherever we want.

If we create our own restrictions, we can guide ourselves and other programmers to the direction we intend for our application's structure.

Creating restrictions

I've written about the Forwardable library in the past not only because of it's usefulness, but because we can copy the same pattern to create our own DSL.

Forwardable provides methods which create getter methods for related objects. But what if we created our own DSL for commands to related objects? What if we could pass the messages on, but allow the related object to handle the values?

Here's what that could look like:

class Person
      command :send_email => :emailer
    end
    person = Person.find(1) # get some record
    person.emailer = Emailer.get # get some object to handle the emailing
    person.send_email

That's a lot of pseudo-code but the parts we care about are sending the command to a related object. Commands return the receiving object, queries will return a value.

Here's what that code would look like without our (yet unimplemented) command DSL.

class Person
      def send_email
        emailer.send_email
        self
      end
    end

Any code which uses a Person will have to rely on the command to do its own thing. This prevents a programmer from leaking logic out of the person.

What should happen when the email is sent? With the structure above, this code, can't make decisions:

if person.send_email
      # do one thing
    else
      # this will never work now
    end

If you find that you often write code like the if statement above, you might wonder "where does that logic go now?" Now, you'll be forced to write this code:

person.send_email

And this means that your send_email now has the job of handling what to do:

class Person
      def send_email
        emailer.send_email
        # do some other things...
        self
      end
    end

That might provide you with better cohesion; the related behaviors remain together.

Getting back to that command DSL we used above...

This was the final point of my presentation at RubyConf: you can build guidlines like this for yourself.

I created a gem called direction to handle enforcing this East-oriented approach. I'll write more about that later, but it shows that I can create signals to other developers on my team. I can take a simple concept like a command and simplify my code to show other developers what's happening:

class Person
      command :send_email => :emailer
    end

Building a DSL can aid in communication. The language and terminology we use can compress ideas into easily digestible parts.

If you like this, check out my new book: Ruby DSL Handbook designed to be a guide to help you build your own compressions of concepts.

Get a FREE sample chapter

Get a sample chapter of Clean Ruby and you'll be added to my periodic newsletter of helpful Ruby tips.