Rake Does What?: A Debugging Story

The Mystery

While working on upgrading one of our apps to Rails 5 I noticed that suddenly migrations were failing with the following error:

StandardError: An error has occurred, all later migrations canceled:
wrong number of arguments (given 2, expected 1)

The migration it was failing on just had something like this:

class LeakyMigration < ActiveRecord::Migration[4.2]
  results = select_one <<~SQL
    SELECT 1 FROM table_name
  SQL

  if results
    raise "Panic!"
  end
end

Digging into the ActiveRecord 4.2 and 5.0 documentation it became pretty clear that there wasn't a change to the method signature for select_one, so what gives? Looking at the stack trace I noticed that the method call was going through one of our private gems - specifically through a rake task we have defined there. Huh?

The Investigation

I opened the rake task and found something like the following:

namespace :db do
  task :leaky_task do
    min_db_schema_version
    ...Other things
  end

  def select_one
    ActiveRecord::Base.connection.select_one(ActiveRecord::Base.send(:sanitize_sql, sql, "NONE"))
  end
  
  def min_db_schema_version
    select_one('SELECT min(version) AS version FROM schema_migrations')['version']
  end
end

A quick check found that the method signature for sanitize_sql_for_conditions had changed. An optional second parameter was no longer supported, so it was understandably upset when we tried to give it a second parameter.

But why is this select_one being called during the migration anyways? Do methods defined in a namespace get shared across the namespace? While perhaps a bit inconvenient, that wouldn't be completely unreasonable. A quick check eliminated that possibility. Even with a different namespace the exception was getting raised during the migration.

It couldn't possibly be defining the method on Object, could it?

The Experiment

Well, a bit of quick investigation showed me two things:

You can experiment with this by creating a new empty rake task, defining a method foo and then starting a Rails console. Try something like User.send(:foo) and you’ll get a NoMethodError - so far so good, but now load your application’s rake tasks (which aren’t loaded by default) by calling AppName::Application.load_tasks and call User.send(:foo) again.

It runs! Madness!

These two things alone are worth digging into more, but first I think we should answer the question - why is our migration calling our select_one rather than the one provided by ActiveRecord?

In order to find out I commented out the offending rake task and added a raise statement to the ActiveRecord definition of select one. Re-running the migration yielded a stack trace that highlighted something very interesting...

The Finale

All those nice little sql methods like select_one() and update() aren't defined in ActiveRecord::Migration or any of its parents, they are all defined on the connection object. ActiveRecord::Migration defines a custom method_missing implementation that forwards the call to the connection object.

And as you remember, Rake is defining all its tasks as methods on Ruby's main object, which defines them on Object, which defines them nearly everywhere.

Unfortunately these two things combined lead to trouble. When rake tasks are loaded (which they are when they are running) these methods defined on main get defined on every object. Then, when we call select_one in the migration it sends the call to itself which it now knows how to respond to since it inherits from Object.

You might reasonably ask, "how does defining a method on 'main' define it everywhere?" You can check that it does do that for yourself. But next week, we'll dig down into how that happens and why it works.

View Models, Form Objects, Presenters, and Helpers Oh My!

This is the beginning of a series of blog posts on the different entities we at AppFolio use to manage logic in our server-rendered views. Now, much like the rest of the Rails community we too are moving towards more client-rendered views and a more client-server architecture but the majority of pages are still rendered server-side, and that will be the case for many years going forward. In such a setting it therefore still makes sense to discuss how we want to organize the code that renders these server-side views.

In this series we will be doing a deep dive into each of the concepts individually, but to start out I thought it would be good to give an overview of each concept and what we at AppFolio mean by them. There doesn't seem to be a consensus in the Rails community about what terms like “Presenter” mean and where the boundaries of responsibility lie.

I will by no means claim that what we’ve done will work for everyone - but it works well for us, and who knows - it may do the trick for you as well!

 

Key Concepts And Where To Find Them

View Models

At AppFolio a view model is the single object of interaction for the view. It is responsible for implementing the interface (data requirements) of a particular view template or partial.

I personally find a few things appealing about this. As someone who is still relatively new at AppFolio, a well-named method encapsulating complex logic to, say, decide whether or not a particular message is shown really helps me get up to speed quickly on the reason why a message should or should not be shown.

<% if !Company.has_enabled_this_feature? && (this_condition || that_condition) && one_more_condition %>
    <p>I’m an important message that will help drive adoption but only in certain contexts</p>
<% end %>

Is far less helpful than

<% if view_model.display_feature_marketing_messaging? %> 
    <p>I’m an important message that will help drive adoption but only in certain contexts</p>
<% end %>

Particularly given that when first working with said feature I’m more likely to know whether my changes should or should not be part of the marketing messaging than I am to know exactly which conditions we’re currently relying on for displaying the messaging. And if I do need that deep dive that logic is now isolated from the html noise in a PORO (Plain Old Ruby Object) for my casual perusal.

Not to mention the fact that if there need to be changes made to that logic I get to write nice little unit tests instead of controller tests. In addition to unit tests being faster than controller tests, unit tests also enable me to understand things more quickly, since the expected setup and output aren’t masked behind making get requests and parsing response bodies.

Form Objects

Form objects serve much the same purpose as view models but they specifically back an HTML form, and again in much the same way they implement the interface (data requirements) of a particular HTML form.

Why, then, would I not include them in the view models section? Well, form objects have some added responsibilities since they handle things like validations after the form has been submitted. Because of these extra responsibilities you will end up instantiating form objects in, for an instance, the update action of a controller, and as long as everything is valid that object will no longer be used to render the subsequent view (unless you want to render the same edit view even upon a successful form submission)

They further distinguish themselves from standard view models in that they “quack” like an ActiveRecord object in the sense that they have validations on the form fields (they include ActiveModel::Validations and other ActiveModel modules.) Furthermore, depending on the complexity of saving the actual form it may extend the default save behavior of the related objects.

Of the concepts discussed here form objects are the ones I’ve worked with least, but their value is already apparent from the few times I’ve worked with them. Because few forms represent only one ActiveRecord object, this separates business logic from, writing HTML, and encourages slim controllers.

Presenters

Presenter is by far the term that is most prevalent, and it can often include some of the responsibilities we’ve given to the view models, but for us a presenter wraps a particular ActiveRecord model or business concept. It provides methods that transform and format data for consumption by a view or another consumer (such as an API.) Since this blog post series is all about views we’ll just be talking about the view case.

I think at this point, a small example is in order. Imagine if you will that we have in our app the idea of a PhoneNumber - I know, radical right? But everywhere that we show a phone number we want it to be a TelLink to also have a button for sending a text message.

The exact styling of the number may change depending on context, so a full partial/view model pairing isn’t needed. Instead we’ll create a phone number presenter that takes in a phone number object and exposes a set of methods that return the various components that we want to have, namely an instance of a TelLink and an instance of the SendTextButton.

The great things about this is that we can instantiate this presenter in any view models that help render a view containing phone numbers, allowing the behavior to be consistent across views regardless of the context.

Helpers

A helper is a “functional” method (meaning it relies only on its inputs and not on any internal state stored in the classes it is included in) that provides easy access to commonly used logic. Rails of course provides many of these (such as link_to) but there are some basic formatting methods that also make sense as helpers, such as a method that takes in a value and provides a default value if the value is falsey.

def value_with_default(value)
 if value
   value
 else
   ‘--’
 end
end

What we’ve found is that many of the things we had put in helpers would actually make sense in a presenter, and since they were in helpers they ended up being included in other helpers that were then included in other helpers, leading to a big ‘ol ball of spaghetti that makes cleanup a lot more difficult.

In general, I’ve found that biasing towards trying to put something in a presenter first and then seeing if that feels off yields the best results, and it certainly leads to more OO design. After all, there are few things that are so ubiquitous that they aren’t tied to some well-defined business object or concept.

 

Looking Forward

Hopefully this overview has helped you get a basic idea of how we at AppFolio talk about and use view models, form objects, presenters, and helpers, as well as the value that adopting this sort of structure offers a development team. Over the next few months I’ll be releasing in-depth dives for each of those concepts focusing on real use cases and highlighting the value that view models can have to you, the developer.

In the meantime if you have any questions or feedback, feel free to reach out to me at mischa.lewis-norelle@appfolio.com or leave a comment!