3

The date_validator in its examples has a comment:

Using Proc.new prevents production cache issues

Does it mean, that everywhere in my code, where I use current time related methods (Time.now, 1.day.since(Time.zone.now), etc.) I should surround them with Proc.new { }?

I don't completely understand this, since replacing

time_now = Time.now.utc

with

time_now = Proc.new { Time.now.utc }

just doesn't make sense to me (new type of object is returned).

So, the question is, when and how should I use Proc.new with time related methods? And does that still apply to the latest versions of Ruby (1.92) and Rails (3.1)?

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
krn
  • 6,715
  • 14
  • 59
  • 82

2 Answers2

5

No, it only references the given example:

validates :expiration_date,
  :date => {:after => Proc.new { Time.now },
  :before => Proc.new { Time.now + 1.year } }

If instead you'd written

validates :expiration_date,
  :date => {:after => Time.now,
  :before => Time.now + 1.year }

Time.now would be interpreted when the class is parsed and it would be validating against that value.

Using Proc.new in that validation means Time.new will be evaluated when the validation is actually run - not when it's initially being interpreted.

Jakob S
  • 19,575
  • 3
  • 40
  • 38
3

What Proc.new (and lambda) does is, save all your statements in their original form (in an anonymous function), and doesn't evaluate them.

Date Validator gem must have some kind of test to check if a Proc was passed, and it evaluates it when it's actually validating the stuff.

Edit: It does this here - https://github.com/codegram/date_validator/blob/master/lib/active_model/validations/date_validator.rb#L47

option_value = option_value.call(record) if option_value.is_a?(Proc)

A quick example :

pry(main)> time_now = Time.now
=> 2011-06-19 21:07:07 +0530
pry(main)> time_proc = Proc.new { Time.now }
=> #<Proc:0x9710cc4@(pry):1>
pry(main)> time_proc.call
=> 2011-06-19 21:07:28 +0530
pry(main)> time_proc.call
=> 2011-06-19 21:07:31 +0530
pry(main)> 

Note that this will only work with libraries that do implement this kind of check, and not every function accepting a Time.

Dogbert
  • 212,659
  • 41
  • 396
  • 397
  • Thanks. I found that Delayed job (https://github.com/collectiveidea/delayed_job) has implemented that as well. Unlike with date_validator, I create new jobs inside a separate method (which should be run each time). Does it mean, that in this case Proc.new {} is optional and wouldn't make any difference? – krn Jun 19 '11 at 15:45
  • @krn, the problem with using direct Time.now is that your Time.now will be evaluated _when_ the app is first loaded (as it's a parameter to a function call, present directly in the body of a class). In production, the compiled class might be cached for some time, depending on how what you're running your app on, causing wrong values to be passed into your validations. – Dogbert Jun 19 '11 at 15:48