Add a "filter" on the ERB <%= ... %>

I have a site with a lot of view code like this:

<p>
  <%=@post.user.name%>
</p>

Unfortunately a lot of old posts have had their users deleted, so the
code above breaks the entire view. I am working on a larger cleanup on
the code, but in the meantime I would like to make a quickfix to get the
views working.

What I would like is to convert

<%=@post.user.name%>

to

<%=(@post.user.name) rescue ''%>

Is there any way to override <%= to do the above? Thus fixing all errors
on the spot.

Thanks

- Carsten

What I would like is to convert
<%=@post.user.name%>
to
<%=(@post.user.name) rescue ''%>

No it's not. You shouldn't use exceptions to handle non-exceptional
behaviour - and if you know that some posts have had their user
deleted, then it's not exceptional
Besides, this all needs to be done in the model or controller, as the
view should be blissfully ignorant about any data integrity problems
you might have.

Is there any way to override <%= to do the above? Thus fixing all errors
on the spot.

I use a method of substituting a mock object (I'd call it MissingUser
in this instance) which has accessors for each of the properties that
I need from the real AR object that's missing. I then instanciate one
of the mock objects if I request a child object that's missing, by
overloading the call to the associated object, and adding a check that
it exists.

  # missing_user.rb
  # just a normal Ruby model, but not AR-backed
  class MissingUser
    attr_accessor :name, :dob # ...etc (any fields you know you need)

    # as well as accessors you can set default values for parameters,
so every new instance gets populated with some values
    def name
      @name || "Missing User"
    end
  end

  # post.rb
  class Post < ActiveRecord::Base

    has_one :user

    alias_method :ar_user, :user # alias the ActiveRecord call
    def user
      @user || = (ar_user || MissingUser.new) # memoized to save
creating a new MissingUser for every call to the method
      # you could also pass in default values as parameters for the
"new" if you wanted - just overload MissingUser's initialize method
    end
  end

Does that make sense?

I apologise for any typos or mistakes in the code above, as I've just
written it in the email and haven't run it to test. But I hope you can
get the gist from what's there.

I have a site with a lot of view code like this:

<p>
<%=@post.user.name%>
</p>

Unfortunately a lot of old posts have had their users deleted, so the
code above breaks the entire view. I am working on a larger cleanup on
the code, but in the meantime I would like to make a quickfix to get the
views working.

What I would like is to convert

<%=@post.user.name%>

<%= @post.user.name if @post.user %>

Colin

Michael Pavling wrote:

Does that make sense?

It does make sense, and this is a process that I am currently working
through. My example was just a single extract from the application,
though, and there are many of these scattered all over the code.

So what I really wants is a quick-fix that makes the presentation-code
"work" until I've fixed the errors the Right Way(tm).

For now I've resorted to

class NilClass
  def method_missing(method_name, *args)
    ''
  end
end

That works - if not pretty, then at least it holds my view code together
while I fix it.

- Carsten

class NilClass
def method_missing(method_name, *args)
''
end
end

Overloading method_missing is probable to cause more problems than it fixes :slight_smile:

That works - if not pretty, then at least it holds my view code together
while I fix it.

You only need to fix the missing associations one at a time; get a
MissingUser in there and all the nil users will disappear. Then if you
discover another nil association, create the MissingModel for that.

Michael Pavling wrote:

Overloading method_missing is probable to cause more problems than it
fixes :slight_smile:

I know - it is a very VERY temporary solution to get peace to do real
fixing. :slight_smile:

You only need to fix the missing associations one at a time; get a
MissingUser in there and all the nil users will disappear. Then if you
discover another nil association, create the MissingModel for that.

Yup it is a very neat solution, that I will adapt to. Thanks

- Carsten

No worries. I got it from the "Refactoring: Ruby Edition" book - they
call it the "introduce null object" pattern.
Well worth being familiar with the book for loads of other very good
little patterns.

There is a much easier solution to all of this. Utilize the try()
method that comes with ActiveSupport:

<p>
<%= @post.try(:user).try(:name) %>
</p>

try() calls a method and catches exceptions, returning nil instead.
Try is added to the main Object class, so even nil objects have the
method, hence your ability to chain them together....

-casey

I don't see how that's easier than the guard condition that Colin
suggested - but either way it ignores that the OP wanted to deal with
all the occurrences of the problem in one place, rather than go
tweaking loads of views.