ruby-on-railspartials

optional local variables in rails partial templates: how do I get out of the (defined? foo) mess?


I've been a bad kid and used the following syntax in my partial templates to set default values for local variables if a value wasn't explicitly defined in the :locals hash when rendering the partial --

<% foo = default_value unless (defined? foo) %>

This seemed to work fine until recently, when (for no reason I could discern) non-passed variables started behaving as if they had been defined to nil (rather than undefined).

As has been pointed by various helpful people on SO, http://api.rubyonrails.org/classes/ActionView/Base.html says not to use

defined? foo

and instead to use

local_assigns.has_key? :foo

I'm trying to amend my ways, but that means changing a lot of templates.

Can/should I just charge ahead and make this change in all the templates? Is there any trickiness I need to watch for? How diligently do I need to test each one?


Solution

  • Rails 7.1

    introduced a nice feature called strict_locals, it allows us to explicitly tell which locales are accepted in a partial.

    Put this magic comment on top of your file:

    <%# locals: (title: "Default title", subtitle: nil, other_required_param:) %>
    

    This way, Rails will raise an ArgumentError, missing local: :other_required_arg when not given the parameter.

    See the docs.

    Gotcha

    If you need to use the generated helpers in a partial that is rendered as a collection of items, remember to put it on top of the file.
    For instance:

    _product.html.erb

    <%# locals: (product_iteration:, product_counter:) %>
    
    Collection size: <%= product_iteration.size %>
    Item index: <%= product_counter %>