views:

240

answers:

3

I'm finding myself writing this bit of code in my controllers a lot:

params[:task][:completed_at] = Time.parse(params[:task][:completed_at]) if params[:task][:completed_at]

Don't get hung up on what I'm doing here specifically, because the reasons change every time; but there are many circumstances where I need to check for a value in params and change it before handing it off to create or update_attributes.

Repeating params[:task][:completed_at] three times feels very bad. Is there a better way to do this?

+10  A: 

One way to shorten this slightly is:

if c = params[:task][:completed_at]
  params[:task][:completed_at] = Time.parse(c)
end

Or, you might prefer this:

params[:task][:completed_at] &&= Time.parse(params[:task][:completed_at])

In the second case, the assignment will only happen if the left side is "truthy".

Alex Reisner
theIV
I've never seen it either! It just occurred to me that it would work.
Alex Reisner
Mike Woodhouse
Adam Lassek
Alex Reisner
A: 

I suppose you could consider doing something like this.

Implement #to_time on String and NilClass, perhaps in a extensions.rb (as recommended in Ruby Best Practices, e.g.

require 'time'
class String
  def to_time
    Time.parse(self) # add error/exception handling to taste
  end
end

class NilClass
  def to_time
    nil
  end
end

Then you can just call params[:task][:created_at].to_time and the duplication is gone.

I'm not at all sure that this necessarily constitutes "best practice", but IMHO it meets the objective of the question...

Mike Woodhouse
A: 

I am not incredibly familiar with Ruby, but since it has Perl roots, there may be a construct that allows you to write it like this:

$_ = Time->parse($_) for params[:task][:completed_at] || ();

basically exploiting the for loop to create an alias to the variable, if it exists

maybe something like:

(params[:task][:completed_at] || ()).each { |i| i = Time.parse(i) }

edit:

I see that Ruby has an alias keyword. I am not familiar enough with it to give a Ruby example, but in Perl, the above could also be written:

local *_ = \$params[$task][$completed_at];

$_ = Time->parse($_) if defined;

which specifies that $_ will be an alias for $params[$task][$completed_at]

I tried playing around with it breifly in Ruby, but didn't see a way to alias an identifier, just global variables.

Eric Strom
This won't work, because you'll get `NoMethodError: undefined method `each' for nil:NilClass` when `:completed_at` is nil. Each is only available on an iterator class.
Adam Lassek
Replacing `()` with `{}` will eliminate the exception, but it doesn't overwrite the value of `:completed_at`.
Adam Lassek