Hi, I'm Nicholas Johnson!

software engineer / trainer / AI enthusiast

Shuffling up method params with parallel assignation

Today’s Rails is the button_to helper.

This little rockstar will make a whole RESTful form for you containing just one button, so you can make put, post and delete requests without relying on JavaScript.

In this section, we’re going to look at how parallel assignation gives us two clean and simple ways to call the helper.

Here’s the code:

def button_to(name = nil, options = nil, html_options = nil, &block)
  html_options, options = options, name if block_given?
  options      ||= {}
  html_options ||= {}

  html_options = html_options.stringify_keys
  convert_boolean_attributes!(html_options, %w(disabled))

  url    = options.is_a?(String) ? options : url_for(options)
  remote = html_options.delete('remote')

  method     = html_options.delete('method').to_s
  method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : ''.html_safe

  form_method  = method == 'get' ? 'get' : 'post'
  form_options = html_options.delete('form') || {}
  form_options[:class] ||= html_options.delete('form_class') || 'button_to'
  form_options.merge!(method: form_method, action: url)
  form_options.merge!("data-remote" => "true") if remote

  request_token_tag = form_method == 'post' ? token_tag : ''

  html_options = convert_options_to_data_attributes(options, html_options)
  html_options['type'] = 'submit'

  button = if block_given?
    content_tag('button', html_options, &block)
  else
    html_options['value'] = name || url
    tag('input', html_options)
  end

  inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
  content_tag('form', content_tag('div', inner_tags), form_options)
end

source

Parallel Assignation

On line 2 we see this happy little guy:

html_options, options = options, name if block_given?

Ruby is allowing us to assign multiple variables on a single line. Here’s an example:

a,b = 1,2

# a => 1
# b => 2

Two ways to call the helper

In the button_to helper there are two ways to set the content of the button, either by passing a string, or by passing a block, like so:

# method one
button_to "profile", profile_path

# method two
button_to profile_path do
  "profile"
end

But the method is defined like this:

def button_to(name = nil, options = nil, html_options = nil, &block)
  # ...
end

Passing nil as the first parameter looks lame, so if a block is given we want to shuffle up the parameters, putting the value of name into options, and the value of options into html_options.

We can achieve this nice and easily using parallel assignation, like so:

def fnc(a=nil,b=nil,c=nil, &block)
  if block_given?
    b,c = a,b
  end
end

fnc(1,2,3)
fnc(2,3) {1}