[rspec-users] More on collection proxies

David Chelimsky dchelimsky at gmail.com
Sun Jan 21 17:28:44 EST 2007


On 1/21/07, Jay Levitt <lists-rspec at shopwatch.org> wrote:
> I've got a question similar to Francois's.  I'm writing a to-do-like
> application, where each Task has a number of Events, consisting of
> event.date and event.status.  I want to see how long it's been since the
> Task was last completed.  So:
>
> context "A task completed once on 2001-01-02" do
>    setup do
>      @task = Task.new
>      mock_event = Struct.new(:date, :status)
>      @task.stub!(:events).and_return(
>        [mock_event.new(Date.parse("2001-01-02"), "completed")])
>    end
>
>    specify "was completed 0 days ago on 2001-01-02" do
>      @task.completed_days_ago(Date.parse("2001-01-02")).should_eql 0
>    end
> end
>
> task.rb looks like:
>
>    def cache_completions
>      @completions = {}
>      events.each do |event|
>        @completions[event.date] = event.status
>      end
>    end
>
>    def completed_days_ago(todays_date)
>      cache_completions unless @completions
>      date = todays_date
>      while date > todays_date - 7 do
>        return (todays_date - date).to_i if @completions[date] == "completed"
>        date = date - 1
>      end
>
>      "never"
>    end
>
>
> This smells a bit.  I could separate part of completed_days_ago into
> last_completed_on(date), but no matter how I slice it I still end up
> mocking Task.cells for some routine.  Suggestions?

Ah, how I love these questions. Thanks Jay!

So first of all, lets think about what we REALLY want the interface to
Task to be. What you have there is:

  @task.completed_days_ago(date)

In your description, you say "how long it's been since the Task was
last completed". That leads me to a method name like this:

  @task.days_since_completed(date)

The next question is, why are we passing in the date? We want to how
many days have passed since it was completed, not how many days
between completion and some arbitrary date. Now this gets tricky
because the method becomes more testable if you can supply the date,
but supplying the date doesn't "feel" right in the context of
"days_since_completed".

>From here, I see a couple of ways to go. One would be to stub Time.new
(or local, or whatever method you wish to use to generate current
time) and leave the method with no argument:

context "A task completed once on 2001-02-03" do
  setup do
    event_stub = Struct.new(:date,
:status).new(Time.parse("2001-02-03"), "completed")
    Time.stub!(:new).and_return(Time.parse("2001-02-03"))

    @task = Task.new
    @task.stub!(:events).and_return([event_stub])
  end

  specify "was completed 0 days ago on 2001-02-03" do
    @task.days_since_completed.should_eql 0
  end
end

Another would be to change the meaning of the method to something like:

  @task.days_from_completion_date_to(date)

No perfect answer here. Just food for thought.

Cheers,
David


>
> Jay
>
>
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>


More information about the rspec-users mailing list