How-To Make a Rails Partial With Optional Locals (Parameters)
Sorry, this article has moved!
http://hackd.thrivesmarthq.com/how-to-make-a-rails-partial-with-optional-locals-parameters
Sorry, this article has moved!
http://hackd.thrivesmarthq.com/how-to-make-a-rails-partial-with-optional-locals-parameters
September 15, 2007 at 9:39 am
[...] Making a partial use optional parameters – Basically, set the parameter to nil inside the partial (e.g. title = nil unless defined?(title)) [...]
September 15, 2007 at 3:38 pm
Hmm, “unless defined?” doesn’t work as I expect:
$ ruby script/console
Loading development environment.
>> show_closed = true unless defined?(show_closed)
=> nil
>> show_closed
=> nil
$ ruby script/console
Loading development environment.
>> unless defined?(show_closed)
>> show_closed = true
>> end
=> true
>> show_closed
=> true
September 15, 2007 at 3:51 pm
Doh! Actually, something is seriously wrong here. I’m using:
unless defined?(show_closed)
show_closed = true
end
Sometimes I explicitly pass false. Sometimes I don’t pass anything. On the first load of the page, the default is applied. However, if I go to a page that explicitly passes false, and then I go back to the page that doesn’t pass anything, show_closed ends up being nil. Sure enough, show_closed goes back to being nil, but “defined?(show_closed)” is “local-variable”. Rails must be resetting all of these local variables to nil, but they’re still defined!
September 19, 2007 at 10:50 pm
Actually that’s enough:
title ||= nil
September 19, 2007 at 10:56 pm
Hi All,
Thanks for the interest!
@Gregory:
Using ||= will only work if your value for the parameter can never be ‘false’… if you mean to send in false as a value for a parameter, using ||= will override it with the new value.
@Shannon:
Sorry you’re having trouble with it. It looks like script/console does something funky to values that aren’t defined. After playing around with it a bit myself, it doesn’t parse show_closed = true unless defined?(show_closed) as you’d expect at all.
However, in ruby outside of script/console, like in partials, it performs fine. I’ve been using it successfully all over my application (opting to use it over ||=) quite well. Shannon, perhaps you can elaborate on what you mean by “going back and forth” between your pages?
September 20, 2007 at 11:09 am
A quick warning: this technique will cause you sleepless nights if you use Markaby. At times, Markaby’s behind-the-scenes magic renders the locals hash inaccessible, so defined?(foo) will always return nil. However, Markaby stuffs the locals into its assigns hash, so you can access the locals as member variables. So…
foo = nil unless defined?(foo)
becomes
@foo = nil unless defined?(@foo)
So far this appears to work for us.
September 27, 2007 at 8:46 am
Here’s list of solutions i’ve found :
http://gmarik.blogspot.com/2007/09/rails-optional-locals-values-in.html
October 16, 2007 at 10:47 am
Hey… after much help in the comments, I’m switching to another method that seems to be used in core rails: param = default_value unless !local_assigns[:param].nil? — although it won’t work with HAML
October 27, 2007 at 3:33 am
2Matt:
i think it’s just enough
title = local_assigns[:title]
Isn’t it?
Or just use local_assigns[:title] instead title – as it’ wont raise any errors
November 1, 2007 at 10:25 pm
I was having this problem too…
It’s strange that there’s not an easy DRY solution for this in rails. I originally used your implementation, but then decided it was way too much writing for my taste (my fingers get tired/sore easily).
I hacked up my own little plugin that does something similar (here: http://geekninja.blogspot.com/2007/09/rails-plugin-optional-parameters-for.html ).
Simple, but works alright in my opinion.
December 19, 2007 at 1:09 pm
If you want this to work with HAML you should use _haml_local_assigns instead of local_assigns.
January 17, 2008 at 1:27 pm
works like a charm. Thanks!
March 7, 2008 at 5:47 am
An alternative approach to querying the local_assigns hash for each optional local would be to use a local hash called options, analogous to many of the methods in the Rails API. Then at the top of your partial just do:
options ||= {}
This approach of course makes setting the optional locals a little clumsier when invoking the partial. I like though that it makes it more clear when you are referencing an optional param in your partial. For clarity and to keep them separate from local variables, I would have prefered if Rails would have set all params passed to a partial in a hash called say partial.
March 10, 2008 at 12:30 pm
Thanks. That was very helpful!
I totally agree that partials are analogous to methods. I am starting to learn to refactor reused peices of views into partials.
September 5, 2008 at 9:04 am
Just wanted to say thanks. I google for this every time I need to remember the syntax/variable name!
November 10, 2008 at 12:40 am
[...] Making a partial use optional parameters – Basically, set the parameter to nil inside the partial (e.g. title = nil unless defined?(title)) [...]
November 17, 2008 at 4:26 pm
Nice writeup – I will have to try this out. Oddly, I have never had a problem with the defined? way that you started out with. I totally agreed with treating partials like methods – from day one on Rails I have been putting this kind of thing at the top of each partial:
name = nil unless defined?(name)
phone = nil unless defined?(phone)
In fact, I often use this to define default value for the locals that are passed in, while letting an explicitly passed-in value of nil mean that the default value should be used, such as:
name = ‘unknown’ unless (defined?(name) && name)
That was all using RHTML…recently I started converting an existing rhtml-based project to haml and ran into a specific problem with this technique…but only when using a boolean value for a local. Consider that I want passing the local specifically to true or false to be preserved, but if you don’t specify it or pass in nil, then it should use a default value of true. So…I did this:
show_name = true unless (defined?(show_name) && !show_name.nil?)
This works like a charm when the partial is rhtml, but with haml, it seems that if I explicitly pass false for show_name to the partial, that it gets converted to nil. Doh!
Not sure what path I am going to choose to address this in a large existing code base that relies on it…I might have to try out the _haml_local_assigns tactic mentioned above.
For a simple code example that reproduces this problem, see my writeup on it at: http://sleeplesscoding.blogspot.com/2008/11/bug-in-haml-partial-rendering.html