Futzing around with Ruby, other languages, and further occasional idiosyncrasies.

Sunday, September 27, 2009

Argument defaults for partials in Rails

It has bugged me, ever since I started using Rails, that when using partials with arguments, it is not easy to define default values for arguments. Matthew Moore sets out an example and has a good solution.

His solution is to check the parameters thusly:
title = nil if local_assigns[:title].nil? 
This can be improved by checking instead whether the value key is there, to handle the case in which you actually want to set the value to nil:
title = :defaultvalue unless local_assigns.has_key? :title
Since this use of local_assigns is a bit of an undocumented hack (and so subject to change), and also a bit (actually documented, thanks Benjamin) long to remember and type, we could do better by hiding this behind a helper. However, local_assigns is a local variable itself, so how to access it from inside the helper?

Ah, but there is a back door! eval allows code to be executed in another context (and hence access to its local variables) provided we have a block to extract a binding from. So, we can make our helper interface be used like this:
title = opt(:title) { :defaultvalue }
Then we implement our helper thusly:
module OptHelper
  def opt(name, &block)
    if eval("local_assigns.has_key? :#{name}", block.binding)
      eval name.to_s, block.binding
    else
      yield
    end
  end
end
The first eval checks to see whether the variable was assigned, second eval is so that we can return that value. The yield executes the passed-in block to get the default value. This implementation also has the advantage that the default value can be an expensive operation that only gets executed if it's needed.

It turns out however that eval is a rather slow call (the string must be parsed and compiled), so a few benchmarks show that it is better to preemptively lump the two evals together:
module OptHelper
  def opt(name, &block)
    was_assigned, value = eval(
      "[ local_assigns.has_key?(:#{name}), local_assigns[:#{name}] ]", 
      block.binding)
    if was_assigned
      value
    else
      yield
    end
  end
end

Friday, September 25, 2009

begin

Why? because some of my colleagues believe code they find on blogs more than the some of the stuff I write, so the solution: publish my own blog. But seriously, also to give some back for the myriad times I've gotten unstuck thanks to some helpful blogger.

Some logistics: I'll expect to be having source code, so some easy way to publish code is needed. I found this: SyntaxHighlighter. Does it work?
class DoesItWork
  def yay!
  end
end

The documentation is a bit unclear, but to get it working using the author's hosted css and js files, you add this to the bottom of the page:
<link href='http://alexgorbatchev.com/pub/sh/2.0.320/styles/shCore.css' rel='stylesheet' type='text/css'>
<link href='http://alexgorbatchev.com/pub/sh/2.0.320/styles/shThemeDefault.css' rel='stylesheet' type='text/css'>
<script language='javascript' src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shCore.js'>
<script language='javascript' src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shBrushRuby.js'>
<script language='javascript' src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shBrushJScript.js'>
<script language='javascript' src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shBrushXml.js'>
<script language='javascript'>
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.defaults.gutter = false;
SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/2.0.320/scripts/clipboard.swf';
SyntaxHighlighter.all();
</script>

I added this to the bottom of the Blogger template, and then simply surrounded the code with
<pre name="code" class="brush:ruby">
class DoesItWork
  def yay!
  end
end
</pre>

The .swf (Adobe/Macromedia Shockwave) is to enable the neat clipboard functionality.