Having trouble calling a Proc within a Method within a Class

699 views Asked by At

I am struggling to debug my issue; I am trying to use a Proc within a method, all inside a class. When I don't type in the "class" details, my program compiles fine; when I specify the class name, however, I get a variety of different errors.

Note: I posted this question on CodeReview.StackExchange.com, but it was off-topic there; the mods (?) recommended I ask the question here.

Background. Here is the prompt that I was given, taken directly from pine.fm:

Grandfather Clock. Write a method which takes a block and calls it once for each hour that has passed today. That way, if I were to pass in the block do puts 'DING!' end, it would chime (sort of) like a grandfather clock. Test your method out with a few different blocks (including the one I just gave you). Hint: You can use Time.now.hour to get the current hour. However, this returns a number between 0 and 23, so you will have to alter those numbers in order to get ordinary clock-face numbers (1 to 12).

The code that works:

def hourlyRing ring
    time = Time.now.hour%12;
    hour_set =[12, 1, 2, 3, 4, 5, 6, 7, 8 , 9 , 10, 11];
    (hour_set[time].to_i).times do 
        ring.call
    end
end

ring = Proc.new do 
    puts 'DING!'
end 

hourlyRing ring

It produces the following (correct, since it's 4 p.m. here) output:

~$ ruby GrandfatherClock.rb
DING!
DING!
DING!
DING!

The code that doesn't work:

class GrandfatherClock  

    def hourlyRing ring
        time = Time.now.hour%12;
        hour_set =[12, 1, 2, 3, 4, 5, 6, 7, 8 , 9 , 10, 11];
        (hour_set[time].to_i).times do 
            ring.call
        end
    end

    ring = Proc.new do 
        puts 'DING!'
    end 
end

# Here's where I try to call it:
ringtime = GrandfatherClock.new
ringtime.hourlyRing ring

Can anyone see the error in my code? It is a short enough code, in my humble opinion, but I cannot understand why it won't compile. Perhaps I am fundamentally misunderstanding something about Procs, but I can't understand what exactly. Thank you in advance.

2

There are 2 answers

0
Mircea On BEST ANSWER

It's about the context.
In the 2nd example you are putting the ring proc inside the class, so when you actually call ringtime.hourlyRing ring, ring is not defined in that scope.

You could alter the code to:

class GrandfatherClock  

    def hourlyRing ring
        time = Time.now.hour%12;
        hour_set =[12, 1, 2, 3, 4, 5, 6, 7, 8 , 9 , 10, 11];
        (hour_set[time].to_i).times do 
            ring.call
        end
    end
end

ring = Proc.new do 
   puts 'DING!'
end 

# Here's where I try to call it:
ringtime = GrandfatherClock.new
ringtime.hourlyRing ring

Furthermore, the problem statements explicitly ask you about a block, so the hourlyRing should be something along the lines of:

def hourlyRing
    time = Time.now.hour%12;
    hour_set =[12, 1, 2, 3, 4, 5, 6, 7, 8 , 9 , 10, 11];
    (hour_set[time].to_i).times do 
        yield
    end
end

You would call it like this:

hourlyRing { puts 'RING'}

or

 hourlyRing do
    puts 'RING'
 end
0
neuronaut On

In the first example you've assigned the proc to a variable ring outside of the class so the variable ring is in scope (i.e. visible) when you reference it.

In the second example, although you are still assigning the proc to a variable called ring, the variable is now declared inside the GrandfatherClock class and as such is no longer visible outside that class.

Instead of assigning the proc to a variable you could create a function that returns the proc:

class GrandfatherClock
    def hourlyRing ring
        time = Time.now.hour % 12
        hour_set = [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
        (hour_set[time].to_i).times do
            ring.call
        end
    end

    def ring
        Proc.new do
            puts 'DING!'
        end
    end
end

# Here's where I try to call it:
ringtime = GrandfatherClock.new
ringtime.hourlyRing ringtime.ring

Notice that you must now reference ring via the ringtime instance of the class.

However, this isn't really what the problem description was asking for (I'm just using this to illustrate what's going on). To be more in alignment with the problem description you'd need to use yield in the hourlyRing function in place of ring.call and then pass a block in the method call:

ringtime.hourlyRing do
    puts 'DING!'
end