I'm a Floridian web engineer who planted his roots in Paris. Some of my favorite past-times are analyzing and optimizing development processes, building solid APIs, mentoring junior devs, and long walks on the beach.

STI Models and a Misconception of DRY 02-25-2017

One of the biggest problems in programming is how to handle and simplify the logic that shows up in your application. Logic which shows up uniformly throughout a class tends to be a smell which indicates the need to represent multiple different concepts. Here is an example of how this can manifest:

Example:

class ThingController < ApplicationController
  def create
    if creation_params[:kind] == 'First'
      First.create(creation_params)
    else
      Second.create(creation_params)
    end
  end

  private

  def creation_params
    params.require(:thing).permit!
  end
end

What's happened here is that we have two separate classes, here First and Second. If you are familiar with STI then you probably have seen this sort of thing often. The problem is that the objects share a data schema, but they will have potentially different views, potentially different validations and they will do potentially different things, ie they have different domains.

Keep it DRY

DRY

People often wrongly cite that they are staying DRY by merging two domains or by combining two functions. But the concept is actually not referring to code. The principal is referring to having a single representation knowledge or a single source. So an example of keeping code dry is to not have a constant show up in all of your view code.

.header
  %p Default circle size is 300 mm.
  .blurb
    Space remaining:
    %span = circle.size - 300

Instead you would store the size as a constant or a method in a class.

class Circle
  def self.max_size
    300
  end
end

.header
  %p= "Default circle size is #{Circle.max_size} mm."
  .blurb
    Space remaining:
    %span= circle.size - Circle.max_size

Manifestation of Excess Logic with STI:

If we look back to our original controller example, we see that there are two separate domains that are being handled by one single controller and that while the total amount of code is less(potentially), we haven't actually simplified our lives. As we add more subclasses to the STI model we will see an unhappy pattern emerge. Potentially we will have a case-statement to select our object and to have them do different things. We will have to define functions like this outside of our models that will then manage the models.

The solution is to not be afraid of having more files, not thinking that less code is necessarily more manageable. Treating the two models as separate resources will make maintenance easier from our routes to our views.

The problem with putting two separate models in the same controller is that there is a branch that starts in the route, and goes all the way to the view. The only argument that I have seen is that you can save on the amount of code you write by putting them in the same controller and giving them the same route. Doing so however makes all of the code associated more complicated as there is now a branch for every reference to the model. If there really is no divergent domain code, then there is no reason for STI. STI is not a way for you to treat two separate things as the same, it is a way for you to treat two things with the same data as different.

Tags: dry STI Rails

Definition vs Function/Methods Style 10-15-2011

I was recently reading The Rails 3 way by Obie Fernandez when I came across a section of the book that was talking about how Ruby allows for a definition-style as opposed to a funtion-style. That is to say, using a function call without including the open and closing parentheses. Typically, I like using functions without parentheses because I feel that the function looks more clear.

I much prefer to see:


link_to "Some page", some_page_path

instead of:


link_to( "Some page", some_page_path )

Especially in a haml template, the first style looks nicer. However, "The Rails 3 way" mentions that one of the key benefits parenthesis-less function calls offer is macro-like feeling and should be a signifier that this call will affect other things and not act directly. Indeed when you look at the following code:


class Client < ActiveRecord::Base 
  has_many :billing_codes
  has_many :billable_weeks
  has_many :timesheets, :through => :billable_weeks
end

class Client < ActiveRecord::Base
  has_many(:billing_codes)
  has_many(:billable_weeks)
  has_many(:timesheets, :through => :billable_weeks)
end

You can see that the first one looks cleaner. It also looks more like a declaration. Indeed, the has_many function alters the object of the class that calls it giving all of it's instantiated objects a slew of other functions. This theme happens over and over again in the Rails framework. The filters that are added to controllers, such as before_filter and after_filter, will alter the functionality of actions that get called.

Ambiguity

The problem with the definition-style method is when there are nested functions which take parameters. For instance, if the function for generating the page for some_page requires parameters as well:


some_page_path( :user_id => current_user.id, :name => 'derp' ) # calls the path for some page

This will cause problems when the following call is made:


link_to "Some page", some_page_path :user_id => current_user.id, :name => 'derp'

In the link_to call, it can't be determined whether the :name parameter should be part of the params for link_to or part of the params for some_page_path.

Since consistency is always preferred, it is better to always use parentheses with function calls only leave them off only when the function is being used to alter the class in some way.

Tags: style ruby Rails

Rails 3 Routing: Use match in a Block 12-19-2010

I was trying to set up an easy route that would let me use the following url: "posts/tags/:tagname". As I was playing around I realized that you can use match inside of a block. More importantly you can also give it a path name so that it can be easily referred to. Here is what I did:


  resources :posts do
    collection do
      get 'display'
      match ":name", :to => "posts#tags", :as => 'by_tag_name'
    end
  end

This is nice because in my code I can now call:

=linkto name, bytagnameposts_path(:name => name)

Tags: routes Rails

Finding out if data has changed Rails uses changed? 12-14-2010

Unfortunately, tainted? doesn't seem to work for activerecord objects.

Tags: Rails

To Module or Not to Module? 07-03-2010

So, the other day I ran into a problem. I have a simple admin? function that I use to tell whether I can show admin links and it also restricts access to admin sections of the site. So I have a function which I need to use in both views and controllers. The problem is that I don't have a really good place to define this. Since if I put it in an application_controller, I won't be able to use it in the views and if I put it in the application_helper I won't be able to use it in the controllers.

The only thing I could think of was putting it in both. Which is dumb b/c when I have to come back and change it later, I'll forget to change it in both places.

I ended up making a module called Auth and requiring that in both my application_controller and my application_helper. The examples I saw used it as follows:

Auth.admin?

However I went in favor of:

Auth::admin?

Since that makes it a little more clear that this isn't a model.

Tags: Rails