VCR Program Manager (#101)

When I used the term VCR in my latest book, my editor complained that it
me.” (Ouch. I’m only 30!) I’m told we all use Tivo to record our
shows now.
I’m not on that bandwagon yet, so the rest of you will need to tell me
if we
need a Tivo Program Manager quiz or if this same code will work.

First, let’s examine a super straight forward solution to the task to
see what’s
involved. When we’ve done that, I’ll make note of a few variations that
my eye.

We will begin with some code by Peter Severin. Here’s a helper method
added to the Time class:

class Time
	def seconds
		(hour * 60 + min) * 60 + sec

This just calculates the number of seconds since midnight. That
measurement is
used by weekly programs in the quiz, to specify when to record on a
given day.

Let’s take our first steps into the Program objects now:

class Program
	attr_reader :channel

	def initialize(program_details)
		@program_start = program_details[:start]
		@program_end = program_details[:end]
		@channel = program_details[:channel]

I think Peter has a very clean and correct OO design in these Program

Here we see the base class handling only the initialization that applies
to all
subclasses. Also note that only channel() is exposed to the outside
since that’s all the ProgramManager really needs.

Here’s the subclass for one-shot recording:

class SpecificProgram < Program
	def record?(time)
		time.between?(@program_start, @program_end)

All Peter adds here is the ability for the Program to determine if it is
scheduled for the passed time. This keeps the Program logic in the
classes where it belongs.

The other subclass is for repeat programming:

class RepeatingProgram < Program
	WEEKDAYS = %w(mon tue wed thu fri sat sun)

	def initialize(program_details)
		@days = program_details[:days].map {|day| WEEKDAYS.index(day) + 1}

	def record?(time)
		@days.include?(time.wday) &&
		time.seconds.between?(@program_start, @program_end)

Here initialization is modified to handle the :days parameter that only
to this type of Program. From there, another record?() method is
created to
examine both the day and time.

I don’t see as much of this traditional OO design in solutions to the
but here I found it quite elegant. With each Program able to answer the
questions about itself, the ProgramManager is almost trivial to

class ProgramManager
	def initialize()
		@programs = []

	def add(program_details)
		case program_details[:start]
		when Numeric
			@programs <<
		when Time
			@programs[0, 0] =


	def record?(time)
		program = @programs.find {|program| program.record?(time)}
		program ? : nil

Peter begins by constructing an Array to hold the @programs in
Conflict management was pretty easy for this quiz in that the last
Program wins out. You can deal with that by keeping them all in one
list and
making sure you order them correctly at insertion time.

The insertions are handled by the add() method. It determines the type
Program object to create and adds it to our list of @programs. The
addition is the tricky one, if you’re not familiar with how Array.[]=()
Let’s see what that does in IRb:

>> programs =
=> []
>> programs << "repeating 1" << "repeating 2"
=> ["repeating 1", "repeating 2"]
>> programs[0, 0] = "specific 1"
=> "specific 1"
>> programs
=> ["specific 1", "repeating 1", "repeating 2"]

Put another way, the assignment to index zero, length zero adds the
element to
the front of the Array. It’s equivalent to the more common:


The final method of Peter’s ProgramManager, record?(), just forwards the
record?() calls to the Array of Program objects via the find() iterator.
first one to claim the time is selected and that Program’s channel() is

There was an interesting element in Dema’s Program class that’s probably
worth a
quick look:

class Program

  # ...

  def initialize(program)
    @start = program[:start]
    @end = program[:end]
    @channel = program[:channel]
    @days = program[:days]

    raise "Missing start or end" \
      if @start.nil? || @end.nil?
    raise "Wrong start or end types" \
      unless (@start.is_a?(Time)    && @end.is_a?(Time))    ||
             (@start.is_a?(Integer) && @end.is_a?(Integer))
    raise "Invalid program" \
      if weekly? && (@start.is_a?(Time) || @end.is_a?(Time))
    raise "End must come after Start" \
      if !weekly? && @start > @end
    raise "Missing channel" \
      if [email protected]_a?(Integer)
    raise "Invalid weekday" \
      if @days.is_a?(Array) && @days.any? { |day| WEEKDAYS[day] == nil 


  # ...


The only difference here is that Dema does a fair amount of error
checking when
Programs are constructed. Ordinarily, I’m not a big fan of this kind of
lock-down coding, but this seems like a good case where it might just be
the effort. Programs are going to come from users and of course users
are going
to make mistakes. Isolating these at Program construction time would
allow the
machine to respond to those errors intelligently at the time when it
instead of running into trouble down the road and recording at the wrong

The focus of this quiz was not to implement a complete VCR, of course,
and most
solvers just assumed they would receive correct input. I just thought
it worth
mentioning that the safeguards employed by Dema do need to be in place
in the system.

I won’t show the code here, but as a final point of interest I want to
everyone take a peek at Gordon T.'s solution. It uses a library
Runt to handle the majority of the scheduling. I wasn’t aware of this
and Gordon’s code got me to look into it.

Runt is Temporal Expression library designed with things like recurring
and scheduling in mind. The documentation is pretty good and the
project worth
a look:

My thanks to all the solvers who always manage to teach me things, even
when we
do these simple problems. I love that aspect of the quiz.

Tomorrow we will take a shot at bringing Literate Programming to our

On 11/16/06, Ruby Q. [email protected] wrote:

Tomorrow we will take a shot at bringing Literate Programming to our fair

Awesome. I’ve actually been working on something to do this since it
was mentioned during the last quiz. It will be interesting to see
what kind of solutions everyone comes up with. Afterwards if anybody
is interested I’d definitely be for starting a project for working on

I think ruby is really well suited for literate programming, but I’ll
hold off on some thoughts until after the quiz.

Looking forward to it.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs