1. text

    Thick Pipe Example: Ruby Code Storage

    In my previous post about laying thick pipes, I talked about the importance of writing code that doesn’t do just one thing, but enables more possibilities of other pieces of code to do a bunch of stuff based on input.

    Laying a thick pipe is usually different than over-engineering in that it rarely requires an object oriented approach to do it. An example of over-engineering a project would be making a Person class, then a EducationPerson, then a Teacher and a Student. Its obvious that Students and Teachers would have some common base, maybe even Person - but more than likely (if its a software system) it would be “User,” which implies some actual benefit and functionality. You certainly wouldn’t create a LivingCreature class in this case.

    So the point of laying a thick pipe isn’t to increase the complexity of the code, but to keep the code terse (short) while providing opportunities for other pieces of code to work at a good level of abstraction.

    A recent example in my daily work was being able to know what code to execute from a record stored in a database. In my case, this Rails app could have easily stored a “code_type” column that was nothing more than a string used as a key for code destination. Maybe something like “user_foo_creator” or “shoot_badguys_with_gun” or something.

    What the code would probably do is take the value from the database, do an if-else or a switch on the key, and then grab the object necessary to execute.

    The problem with this is the only thick pipe is the storage mechanism - the string in the database. The minute I wanted to add a new execute point, I’d have to change the dispatching code. To make that approach even less feasible, I couldn’t know - at any point in time - what modules were in my application as it could be changed by the user during run time. The dispatching mechanism would need to know all cases, and I couldn’t do that.

    So the types of things I was storing was the fully resolved (namespaced) module name for the execution of a background task. Something like “CodeBucket::MyTask” or “CodeBucket::FooBarCreator::CreateFoo”. In the spirit of a “thick pipe,” I would need a very small piece of code that could take a string from the database, get the correct object, and call my execute method on it. It ended up looking something like this:

    ###########################################
    # get_module
    # Given a string, like "CodeBucket::SomeNamespace::SomeModule",
    # get that "SomeModule" so you can do something with it.
    ###########################################
    def get_module(database_string)
      namespaces = database_string.split('::')
      cursor = Object::const_get(namespaces.shift)
      namespaces.each do |item|
        cursor = cursor.const_get item
      end
      cursor
    end
    

    A Rails controller action can find the correct task by id and run it in a one liner now (code_path is the column where the string is stored):

    class ExecutionController < ApplicationController
      def execute_task_in_foreground
        get_module(ExecutionTask.find(params[:id]).code_path).execute_me params
      end
    end
    

    Noticed how I passed in params to the #execute_me method. This is another thick pipe. Now ExecutionController#execute_task_in_foreground can run any piece of code with any number of parameters.

    If I wanted to, I could allow myself to store strings like “CodeBucket::MyClass#my_method” and create another interface similar to get_module that understood the “#” notation and actually executed the method specified. In my current example, the controller expects that #execute_me exists on the module that was retrieved. This makes sense as the controller provides the context keeping get_module generic. However, if I found myself writing lines like that all over the place with different methods being called on the modules grabbed, I’d probably want to store my method in the database column and write a generic facility to execute it.

    I’ll leave that as an exercise for the reader.

    Go lay thick pipes!

About

My name is Scott H. Conner. I've been writing software for over a decade, and I've dug myself into plenty of holes. You can learn more about me here.

Search