At Custora we allow all the numbers on the screen to be lazy-loaded. We precache all of the main page views, but there are too many possible ways that a user can slice the data to pre-compute all of the values.

If you hit one of these pages, you see a loading bar that looks like this:

Writing code to deal with all the lazy loading can be a bit of a challenge. Basically, it means that whenever we get a number from our internal statistics api, we have to accept the possibility that it is not rendered correctly. And we use the statistic term loosely, it can be a float, an array, or some other ruby class. Anything that is computed from the raw transaction data goes through this API.

So to render the pages we make two passes. First to run through and figure out what statistics need to be calculated. We then send these all to delayed job, display a pending page that perodically polls to see if the jobs are done, and when the jobs are completed we display the rendered page.

We have an interesting technical challenge, how do we handle these uncomputed statistics. When we first started we ran into all kinds of errors. We would have an expression like:

number_with_precision(client.new_customer_value)

 

When client.new_customer_value was hit, the job would be enqueued, but then the page would fail to load.

To solve this we tried checking if a statistic was nil, but this made convoluted code

(client.new_customer_value ? number_with_precision(client.new_customer_value) : nil)

So to solve this we made a do nothing class with the goal to fail silently as much as possible. So we have a class that is designed to fail gracefully no matter what you do with it. It should fail silently if you call

stat * 100

or

stat[1]

or

1 * stat

or even

stat[1][0].first.to_s

 

We could have done something like:

class NilClass
 def method_missing(*)
     return nil
 end
end

But this would have changed how nil behaves all over our application. Instead we decided to make a new class for unevaluated model statistics.

Another solution would have been to use the .try method on all statistics. But this is cumbersome, and doesn’t work when the unevaluated statistic is passed to another function.

So we came up with the unevaluated model statistic class.

Code:

class UnevaluatedModelStatistic
  def ==(_other)
    false
  end

  def method_missing(_method,*_args)
    self
  end

  def to_f
    0.0
  end

  def to_str
    ""
  end

  #for cohorts statistics
  def number_of_display_columns(_arg)
    1
  end

  def *(_arg)
    0
  end

  def +(_arg)
    0
  end

  def -(_arg)
    0
  end

  def coerce(_other)
    [self,_other]
  end

  def /(_arg)
    1
  end
end

On the second pass we end up with the completely rendered page:

This is the way we are attacking the problem. How would you approach it?