Ruby praises itself as the language that “makes programmers happy”. It does its best to be expressive and encourage a declarative style of programming.

Oxygen480-actions-irc-close-channel

Guillaume B. inherited a module which was supposed to simply check that a hash contained four fields which represented a signer: first name, last name, mobile, and email. There are many easy ways to check this using various collection methods (something like required_fields.all {|x| input_hash.has_key? x} is the first that comes to mind).

Why on Earth should we do it the easy way? Ruby has powerful features, like mix-ins, list expansions, and message passing. Guillaume’s predecessor wasn’t going to let any of those features sit unused. Comments below are my own.

module SignService
  class Signer
    ATTRIBUTE_KEYS = :firstname, :lastname, :mobile, :email
    
    attr_accessor *ATTRIBUTE_KEYS
    
    def initialize signer
      @firstname  = signer[:firstname]
      @lastname   = signer[:lastname]
      @mobile     = signer[:mobile]
      @email      = signer[:email]

      ATTRIBUTE_KEYS.each do |needed_attributes|
        raise "missing attribute: #{needed_attributes} to instanciate a SignService::Signer" if send(needed_attributes).empty?
      end
    end
    def params
      {
        firstname: @firstname,
        lastname: @lastname,
        phoneNum: @mobile,
        emailAddress: @email
      }
    end
  end
end

There’s so much hideous beauty in this, we have to take a moment to appreciate it. First, this pair of lines:

ATTRIBUTE_KEYS = :firstname, :lastname, :mobile, :email    
attr_accessor *ATTRIBUTE_KEYS

First, we make a list of symbols. Then we pass that list (using the “*” operator to turn it into a series of arguments) to the attr_accessor function, which generates getter/setter methods for each of those symbols.

Most people would have just written: attr_accessor :firstname, :lastname, :mobile, :email, but the splat operator shows a much deeper understanding of the Ruby language.

The function params at the end of the class simply turns the instance level variables back into a hash, which is nice, but remember our goal is just to verify that the input hash contains the keys we require.

And that’s where the initialize method comes in. The parameter signer is our input hash, and the first few lines do their job neatly- they stuff the hash values into instance variables.

It’s the last line of the initialize method that holds our real WTF. This method commits the worst sin it possibly could: it gets “clever”.

ATTRIBUTE_KEYS.each do |needed_attributes|
        raise "missing attribute: #{needed_attributes} to instanciate a SignService::Signer" if send(needed_attributes).empty?

We start with a simple for-each loop, and then we do a “raise … if”. Raise an error if the following condition is met. And that condition?: send(needed_attributes).empty?. The “send” function exposes Ruby’s “message passing” approach to calling functions. This statement calls one of the functions generated in the attr_accessor line and then checks if the result is empty?.

From this code, we can conclude that the original developer knows quite a bit about the Ruby language. We can also conclude that they don’t know nearly enough about programming.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!