# VCR Program Manager (#101)

**URL:** <https://www.ruby-forum.com/t/vcr-program-manager-101/76189>\
**Category:** Ruby\
**Created:** [November 16, 2006, 4:30pm UTC](https://www.ruby-forum.com/t/vcr-program-manager-101/76189 "2006-11-16T16:30:25Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [November 16, 2006, 4:30pm UTC](https://www.ruby-forum.com/t/vcr-program-manager-101/76189/1 "2006-11-16T16:30:25Z")

</div>

When I used the term VCR in my latest book, my editor complained that it  
“dates  
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  
caught  
my eye.

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

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

```

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]
	end
end

```

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

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  
world,  
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)
	end
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  
Program  
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)
		super
		@days = program_details[:days].map {|day| WEEKDAYS.index(day) + 1}
	end

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

```

Here initialization is modified to handle the :days parameter that only  
applies  
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  
quizzes,  
but here I found it quite elegant. With each Program able to answer the  
right  
questions about itself, the ProgramManager is almost trivial to  
construct:

```
class ProgramManager
	def initialize()
		@programs = []
	end

	def add(program_details)
		case program_details[:start]
		when Numeric
			@programs << RepeatingProgram.new(program_details)
		when Time
			@programs[0, 0] = SpecificProgram.new(program_details)
		end

		self
	end

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

```

Peter begins by constructing an Array to hold the @programs in  
initialize().  
Conflict management was pretty easy for this quiz in that the last  
defined  
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  
of  
Program object to create and adds it to our list of @programs. The  
second  
addition is the tricky one, if you’re not familiar with how Array.[]=()  
works.  
Let’s see what that does in IRb:

```
>> programs = Array.new
=> []
>> 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:

```
      @programs.unshift(SpecificProgram.new(program_details))

```

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

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 !@channel.is_a?(Integer)
    raise "Invalid weekday" \
      if @days.is_a?(Array) && @days.any? { |day| WEEKDAYS[day] == nil 

```

}  
end

```
  # ...

end

```

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  
worth  
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  
matters,  
instead of running into trouble down the road and recording at the wrong  
times.

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  
somewhere  
in the system.

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

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

```
http://runt.rubyforge.org/

```

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  
fair  
Ruby…

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [November 16, 2006, 9:31pm UTC](https://www.ruby-forum.com/t/vcr-program-manager-101/76189/2 "2006-11-16T21:31:39Z")

</div>

On 11/16/06, Ruby Q. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

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

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  
this.

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.
