Forum: Ruby on Rails Testing time-dependent named_scopes

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Sven (Guest)
on 2008-10-22 19:52
(Received via mailing list)
Suppose I have the following model, which defines a named_scope that
filters out some instances based on the current time:

  class Post < ActiveRecord::Base
    named_scope :current, lambda {
      { :conditions => ['do_not_show_before < ?', Time.now] }
    }
  end

I've been trying to come up with a good strategy for testing this,
since the test depends on time of execution. If I were using a
conventional method, say:

  def self.current
    find :all, :conditions => ['do_not_show_before < ?', Time.now]
  end

I would simply rewrite it like this:

  def self.current(time = Time.now)
    find :all, :conditions => ['do_not_show_before < ?', time]
  end

and then only pass in a time parameter during testing. But I don't
know how to do with with named scopes because I cannot have optional
arguments to a block.

For right now I've compromised by making a required time parameter as
follows:

  class Post < ActiveRecord::Base
    named_scope :current, lambda { |time|
      { :conditions => ['do_not_show_before < ?', time] }
    }
  end

This means that every time I use the named_scope I have to call it
like this:

  Post.current(Time.now).

I really don't like this, but it does facilitate testing, which I
accomplish (using shoulda and factory_girl) thus:

  context 'An embargoed post' do
    setup do
      @now = Time.now
      @post = Factory(:post, :no_not_show_before => @now)
    end

    should 'be in the named_scope "current" after the
do_not_show_before datetime' do
      assert_contains Post.current(@now + 1.second), @post
    end

    should 'not be in the named_scope "current" before the
embargoed_until datetime' do
      assert_does_not_contain Post.current(@now - 1.second), @post
    end
  end

Is there a better way to accomplish this using named_scopes?

-Sven
Craig D. (Guest)
on 2008-10-22 20:00
(Received via mailing list)
On Wed, Oct 22, 2008 at 11:51 AM, Sven <removed_email_address@domain.invalid> 
wrote:

> ...
>
> Is there a better way to accomplish this using named_scopes?


My first thought is to define the named_scope as you have and also
define a
wrapper method. In the wrapper method, you can make the time parameter
optional with a default of Time.now. Then you'd just use the wrapper
method
instead of the named_scope directly. I don't know how well that would
play
with chaining of named_scopes.

Regards,
Craig
Sven (Guest)
on 2008-10-22 20:06
(Received via mailing list)
So something like this, perhaps:

  class Post < ActiveRecord::Base
    named_scope :current_as_of, lambda { |time|
      { :conditions => ['do_not_show_before < ?', time] }
    }

    def self.current(time = Time.now)
      self.current_as_of(time)
    end
  end

I'm also concerned about chaining, and in particular about the
behavior of things like will_paginate. Hmmm.

Thanks,

Sven
Craig D. (Guest)
on 2008-10-22 20:21
(Received via mailing list)
On Wed, Oct 22, 2008 at 12:05 PM, Sven <removed_email_address@domain.invalid> 
wrote:

>    end
>  end
>
> I'm also concerned about chaining, and in particular about the
> behavior of things like will_paginate. Hmmm.


Yeah, that's what I was thinking. Let us know how it works or if you do
something else.

Regards,
Craig
Craig D. (Guest)
on 2008-10-22 20:26
(Received via mailing list)
Actually, you could do something like this:

named_scope :current, lambda { |*args|  { :conditions =>
['do_not_show_before < ?', (args.first || Time.now) ] } }

I knew Ryan B.' screencast on named_scope covered defaults. See:
http://railscasts.com/episodes/108 .

Regards,
Craig
Sven (Guest)
on 2008-10-22 20:58
(Received via mailing list)
Yes he did. I hadn't seen that one and didn't think of checking it.
Ryan suggests exactly what you've proposed; I've tried it and it
works. Thank you! This is much cleaner, and I get to keep using the
named_scope instead of a method.

Now if I can just find a way around my other method-to-named_scope
workaround problem (http://groups.google.com/group/rubyonrails-talk/
browse_thread/thread/a12f471c54a26b20) my code will be consistent once
again :)

-Sven
Craig D. (Guest)
on 2008-10-22 21:03
(Received via mailing list)
On Wed, Oct 22, 2008 at 12:57 PM, Sven <removed_email_address@domain.invalid> 
wrote:

>
> Yes he did. I hadn't seen that one and didn't think of checking it.
> Ryan suggests exactly what you've proposed; I've tried it and it
> works. Thank you! This is much cleaner, and I get to keep using the
> named_scope instead of a method.


Just to be sure Ryan gets the credit, I checked his site and then
modified
your code according to his example. :-)

Craig
This topic is locked and can not be replied to.