2009-02-28

Gmail Notifier using IMAP

A simple program to check if there are any new messages in our Gmail inbox.

We will connect to Gmail using IMAP protocol and get a list of new (unread) messages from mail server. Here's more or less the code that does it:
require 'net/imap' 

class GNotifier

GMAIL_IMAP="imap.gmail.com"

LOGIN="login@gmail.com" # or just "login"
PASSWORD="password"

def initialize
@envs={}
end

def check
begin
unless @imap
@imap=Net::IMAP::new(GMAIL_IMAP,993,true,nil,false)
@imap.login(LOGIN,PASSWORD)
end
@imap.select("INBOX")
ids=@imap.search(["NOT","SEEN"])
uids=ids.empty?? [] : @imap.fetch(ids,"UID").map{|e| e.attr["UID"]}
@envs.reject!{|uid,env| not uids.include?(uid)}
new_uids=uids-@envs.keys
if new_uids.empty?
new_envs=[]
else
new_envs=@imap.uid_fetch(new_uids,"ENVELOPE").map{|e| e.attr["ENVELOPE"]}
new_uids.each_with_index{|uid,i| @envs[uid]=new_envs[i]}
end
new_mail(new_envs) unless new_envs.empty?
rescue ThreadError, Errno::ECONNABORTED, Timeout::Error, IOError => e
@imap=nil
retry
end
end

def new_mail(new_m)
# ...
end

end

The code is just a draft but shows the most important part. Let's explain it a bit.

First,
@imap
is the instance of the IMAP connector. It is created once (in the first call to
check
and if nothing goes wrong, all subsequent calls to
check
do not create a new connection and do not log into the mail system, but use the previously created one. It is deleted and renewed in case of an error, though (in the
rescue
clause.

The field
@envs
is a hash holding envelopes of each new message in the inbox associated with this message's UID (unique identifier). At the beginning, it is empty.

So now how we fetch new mail: first we call
@imap.search
to get IDs (not UIDs, don't mix up the two) of all messages that do not have the SEEN flag set. Then we fetch these messages' UIDs (the ternary operator is here because
fetch
fails with empty
ids
).

So now we have the UIDs of all new messages, and we can compare it with the list of messages that we have already fetched. First, we
@envs.reject!
all messages that were new but now are not (this means that they have been deleted or marked as read, it doesn't matter for us). Then we compute the list of
new_uids
- UIDs of new messages that are new for the first time (they were not on our list) and for those messages we get some more info - the ENVELOPE - into
new_envs
and then add them to
@envs
. Finally, we call
new_mail
and pass all new new mail that arrived. This method can be also left unimplemented if we just want to know what new messages lie on the server (this info is in
@envs
of course) and do not necessarily want a notification when a new new message arrives.

Some technical details
When creating the connector, we could have written just
Net::IMAP::new(GMAIL_IMAP,993,true)
but it will not work in Ruby 1.9, where the last parameter (authenticate) is true by default.

The line
@imap.select("INBOX")
could be called within the conditional above it, but then somehow not all new messages can be accessed by IMAP. It sort of refreshes the inbox.

The ENVELOPE attribute that we download from the server contains information that would be on the envelope of a regular letter: sender, receiver(s), date, also subject. All accessed simply by method calls. Helpful link: Envelope.

If you prefer to download the whole message and not just the envelope then use the property BODY instead. If you want something more specific, look into the documentation, for example here: Net::IMAP. Note that Gmail does not support some of the commands, like
sort
for instance.

2009-02-22

Enumerator

Do you remember the Fiber? If not, better have a look there before reading on. I will show another use of fibres, this time we won't see them, but they are there, in the guts of the
Enumerator
.

One can use enumerators in either of the two main ways:

Virtual array
If you have read the post about fibres, you are already familiar with the virtual arrays. An enumerator is an easier and a bit more automated way to create a virtual array.

Let's say we'd like to observe a HOTPO sequence starting at any chosen number. As we know, the sequence might be infinite, so it wouldn't be very wise to create an array holding all the elements. But we can create an enumerator to iterate over them, like this:
def hotpo(v)
Enumerator::new\
{ |y|
loop\
{
y<<v
break if v==1
v=(v&1>0) ? 3*v+1 : v/2
}
}
end

hotpo(27).each{|x| print x," "}

The function takes the first value of the sequence as an argument, and returns an enumerator. We create an enumerator of this type by passing it a block and putting the sequentially computed values in the block argument. So: the
y
is the virtual array itself, say good evening.

The loop is not infinite, at least not in any of the known cases, because no infinite HOTPO sequence has been found. But if we remove the break, we could see that the program doesn't hung, even though it has the infinite loop in it! Well, it does hung, but it still produces the output, so it's not more hung than an open word processor.

This behaviour is very similar to this presented in the post about fibres, and that's because the enumerator uses them. If you have understood the fibres well, you could try to implement this kind of enumerator yourself - just as a small exercise for the reader.

View of an enumerable
The other way of using enumerators (the more standard way, I'd say) is to create them from existing enumerable objects. The idea of an enumerator is to allow only very limited access to the underlying enumerable.

For example,
an_array.each
(without passing a block) is an enumerable which can be regarded as a safe read-only view of the array. It does not allow the user to call any other method of the array but
each
and its derivate methods. So you can call
an_array.each.each{...}
or
an_array.each.map{...}
, but the call to
an_array.each_map!{...}
, even though legal, will not modify the underlying object. But still
an_array.map!.each{...}
is able to do so.

The general idea of using chained calls of enumerating methods is:
- If the first method is
each
, then any non-modifying method can be used as the second call.
- If the first method is something else, the valid second methods are
each
which simply forwards the call to the first method, and
with_index
, which does the same but also passes the element index to the block.

So the following, even though perfectly legal, makes no sense:
an_array.select.map{...}
. One of the methods should be neutral, and the neutral methods are
each
and
each_with_index
(or just
with_index
). So apart from making a save view, the main advantage of using enumerators is the possibility to call
an_array.map.with_index{|e,i| ...}
or even
an_array.select.with_index{|e,i| ...}
.

Note that the methods
all?
and
any?
do not return useful when called with no block, so you cannot make these checks with index.

2009-02-21

ASCII Art

Ruby 1.9 introduces some nice ways to write less code and make it look more mysterious at the same time. It's enough to have a look at the following pieces of ASCII art, each line is a valid expression in Ruby 1.9:

->(){}[]
0-->(){0}[]<--0
{x: :x}
{:+@=>->{:-@}}

What they mean? First, there's a new syntax for defining lambdas:
->(args){body}
, and when defining a lambda with no args and no body, and then calling it by
[]
, you obtain the first line. There's also another way to call a lambda or a proc now:
some_lambda.(args)
. This allows us to write
->()[].()
, of someone finds this even more confusing than the first option.

The second line should not be a problem now, it says
(0 - ->{0}.call) < -(-0)
. Yes, in Ruby even
--------1
is a valid expression. It's as they say in the primary school: minus and minus gives plus.

There's also a new syntax for defining hashes that have symbols as keys:
{k:val}
is equivalent to
{:k=>val}
. In our example however one has to put a whitespace between the two colons or else the interpreter is confused, because two colons is another token - for calling a function or getting a constant.

And the last line is just some creative nothing. It uses the symbols
:+@
and
:-@
which are normally used as method names for unary plus and minus. See yourself:
5.-@()
gives
-5
.

There's a lot of articles (and blog entries on various Ruby blogs) that cover the differences between Ruby 1.8 and Ruby 1.9 so if you're interested, just look for them and you'll find easily. One that is worth reading if you'd like to know more than is usually contained in short presentations:

Ruby 1.8 vs Ruby 1.9

And a nice wrap up: Useful Ruby 1.9 links

2009-02-20

Fiber

I haven't been here for quite a while. That's simply because I'm not a blogging kind of guy.

Let's have a look at one of the new features in Ruby 1.9.

class Fiber
You can think of a fibre as of a separate thread, but not a thread that is running all the time in the background, but rather one that is responsible for some specialised tasks and is activated only to do some job and return some results.

Also, a fibre is not a thread.

Let's skip to some code:

FILE=__FILE__

require 'fiber'

reader=Fiber::new\
{
File::open(FILE){|f| f.each_line{|l| Fiber.yield(l) unless l.strip.empty?}}
}

puts "Let's get a line: #{reader.resume}"
puts "Let's get anothe line: #{reader.resume}"
puts "And the rest:"
puts reader.resume while reader.alive?


This produces the following output:

Let's get a line: FILE=__FILE__
Let's get anothe line: require 'fiber'
And the rest:
reader=Fiber::new\
{
File::open(FILE){|f| f.each_line{|l| Fiber.yield(l) unless l.strip.empty?}}
}
puts "Let's get a line: #{reader.resume}"
puts "Let's get anothe line: #{reader.resume}"
puts "And the rest:"
puts reader.resume while reader.alive?
#<File:0xb114bc>


So first: let's have a look at the fibre code (I use the word fibre and not fiber because I prefer the British English; the truth is each time I want to use the class
Fiber
I first spell it
Fibre
and must correct later; I'll make an alias one day). The fibre opens the file and calls
Fiber.yield
passing each consecutive nonempty line to it. Think of it like this: it creates a virtual array containing all the elements that you put there, and
Fiber.yield
is putting and element in it. So we have a virtual array containing all the nonempty lines of code from
__FILE__
.

Of course the array does not exist - it is only a way to imagine how the fibre works. This fact saves memory - you don't have to load all the lines to memory before accessing them. Think of how to write this simple program without a fibre (assume that the file you're going to display is like 1G and you cannot simply load it all to memory) - I'm quite sure that using a fibre is one of the best ways to do it.

Now, how do we read from this virtual array? To read an element we call
reader.resume
. Simple, isn't it? Analyse the output to see that it did what we had expected: first it printed the first line, then the second (nonempty) line, and then the rest.

There's only one mysterious thing at the end:
#<File:0xb114bc>
. The explanation is: the virtual array created by a fibre is filled by all calls to
Fiber.yield
, and when the fibre finishes, its final value (the value of the last operation within the fibre block) is also added to the array. In our case, the last (and only) operation is opening the file and
File::open
returns the created file stream, so it was also added to the array. One can like this feature or not, but one has to live with it. So if we didn't want this line of output, we can change the end of the code to this:
puts "And the rest:"
loop\
{
l=reader.resume
break unless reader.alive?
puts l
}

Now it works like it should. More lines of code but oh well.

fiber.resume(*args)
We've seen that if you pass an argument to
Fiber.yield
then it becomes the value of the
fiber.resume
call. This enables you to pass data from the body of the fibre to the outer world. Passing data is also possible in the opposite direction, and is by no means harder. As you might have already guessed: arguments passed to
fiber.resume
are the value of
Fiber.yield
. So if we wanted a writer instead of a reader:
FILE="test.txt"

require 'fiber'

writer=Fiber::new\
{
File::open(FILE,"w")\
{ |f|
loop\
{
l=Fiber.yield
break unless l
f.puts l
}
f.puts "---"
}
}

writer.resume "Line 1"
writer.resume "Line 2"
writer.resume "Line 3"
writer.resume

Why we have to create a
loop
inside the fibre and break from it? Simply because now it's the outer world that decides when to finish the fibre. It signals the fibre to close the file (and add the
"---"
just for our information that the file was closed properly). If we remove the last line of the code (the one that calls the writer with no argument), we'll see that the file won't have
---
added at the end. It will be properly closed due to the finaliser hidden inside
File::open
but it will be closed and released no sooner than the whole program ends so in general it is a good idea to force file close manually.

But wait, there's no line 1 in the file! Yes, it's not there and that's why: the first call to
writer.resume
did not correspond with a call to
Fiber.yield
from within the fibre because at the time of this call the fibre has not yet been started, so it was not waiting on
Fiber.yield
but at the beginning of its block. So the line 1 just activated the fibre, but did not save the line to the file.

What's the solution? First: to get the value of the first call to
writer.resume
you have to add arguments to the fibre block itself. So one of the solutions is like this:
writer=Fiber::new\
{ |l0|
File::open(FILE,"w")\
{ |f|
f.puts(l0)
loop\
{
l=Fiber.yield
break unless l
f.puts l
}
f.puts "---"
}
}


But it doesn't look to nice, nor it is. In our case a best solution might be to use the first call as a special case and pass the file name in it, like this:
require 'fiber'

writer=Fiber::new\
{ |file|
File::open(file,"w")\
{ |f|
loop\
{
l=Fiber.yield
break unless l
f.puts l
}
f.puts "---"
}
}

writer.resume FILE
writer.resume "Line 1"
writer.resume "Line 2"
writer.resume "Line 3"
writer.resume

For most cases I'd use this form.

Of course there are much more uses of
Fiber
, also such kinds that use passing values in both directions simultaneously, not just in one of them, like in the above examples.

Producer - Consumer
Here's one more way of looking at the whole fibre thing: it's sort of the producer - consumer pattern, with a queue of size limited to zero. In this way the element is produced no sooner than it is needed and most of the time there are zero elements waiting on the queue. Only as the fibre is not a thread, there are no synchronisation problems and so on and so on.

I hereby certify the Fiber class for everyday use.

2008-09-16

StringBuffer

Today a potentially useful code sample:
class StringBuffer
. The class is a buffer into which you can write strings using various printing methods, and from which you can read all the data in the order of arrival. This object can be useful in one-way communication between two threads (or two such objects in two-way comunication), it is thread-safe.

The class will use
StringIO
- a built-in object that has several printing methods (we will add two more) and which allows random access reading and writing. We will remember the reading and writing points in the buffer, and use them during read and write commands. The buffer will also be emptied once it gets too long, to prevent high (infinite) memory usage.

Here's the code, with comments inside this time.
require 'pp' # allows method pp (pretty print); test in irb
require 'thread'

require 'thread'

# Thread synchronizer. A thread calls +wait+ and is stopped
# until the +timeout+ passes or until the method +signal+
# of this object is called to release all waiting threads.
class Synchronizer

def initialize
@waiting=[]
@mutex=Mutex::new
end

attr_reader :mutex # in case somebody wants to use it

def wait(timeout=nil)
thr=Thread.current
begin
# be sure to add myself to the list of waiting threads
@mutex.synchronize{@waiting<<thr}
# sleep given time or forever (yes, it does it)
sleep(*[timeout].compact)
ensure
# be sure to remove myself
@mutex.synchronize{@waiting.delete(thr)}
end
end

def signal
# wake up all waiting threads
@mutex.synchronize{@waiting.each{|t| t.wakeup}}
end

# Check if the thread is currently waiting
def waiting_thread?(thr)
raise ArgumentError,"Argument must be Thread!"\
unless thr.is_a? Thread
@waiting.include?(thr)
end

end

# We add two methods to the +StringIO+.
class StringIO

# Inspect into the buffer (self).
def p(*args)
puts args.map{|a| a.inspect}
end

# Pretty print into buffer (self).
def pp(*args)
args.each{|a| PP.pp(a,self)}
nil
end

end

class StringBuffer

# List of writing methods.
WRITE_METHODS=[:write,:<<,:print,:puts,:putc,:printf,:p,:pp]

# List of reading methods.
READ_METHODS=[:read,:gets,:getc,:readchar,:readline]

# If this much data is in the buffer, empty it.
TRUNC_LENGTH=1000

# dynamically define all writing methods
WRITE_METHODS.each\
{ |wm|
class_eval(
<<-METHOD
def #{wm}(*args)
# safely (mutex)
@mutex.synchronize\
{
# move the +StringIO+ pointer to the end
@buff.pos=@buff.length
# call the same method on the internal buffer
ret=@buff.#{wm}(*args)
# signal the synchronizer in case
# some thread was waiting for data
@synchronizer.signal unless empty?
ret
}
end
METHOD
)
}

# dynamically define read methods
READ_METHODS.each\
{ |rm|
class_eval(
<<-METHOD
def #{rm}(*args)
@mutex.synchronize\
{
# move pointer to the saved position of last read
@buff.pos=@r
# perform the read
ret=@buff.#{rm}(*args)
# save the new pointer
@r=@buff.pos
# call +trunc+ if there is at least +TRUNC_LENGTH+
# bytes of unnecessary data
trunc if @r>=TRUNC_LENGTH
ret
}
end
METHOD
)
}

def initialize
@buff=StringIO::new
# read position
@r=0
@mutex=Mutex::new
# synchronizer for threads waiting for data
@synchronizer=Synchronizer::new
end

attr_reader :synchronizer

def length
@buff.length-@r
end

def eof?
length==0
end

alias empty? eof?

def wait_for_data(timeout=nil)
@synchronizer.wait(timeout) if empty?
self unless empty?
end

private

def trunc
@buff.string=@buff.string[@r..-1]
@r=0
end

end
A small test
irb(main):002:0> s=StringBuffer::new
=> #<StringBuffer:0x2bf4f44 @mutex=#<Mutex:0x2bf4ef4>, @r=0,
# @buff=#<StringIO:0x2bf4f1c>,
# @synchronizer=#<Synchronizer:0x2bf4ee0 @mutex=#<Mutex:0x2bf4e54>,
# @waiting=[]>>
irb(main):003:0> Thread::new{loop{sleep 1;s.print "X"}}
=> #<Thread:0x2bf0b60 sleep>
irb(main):004:0> loop{s.wait_for_data;puts s.read}
XXXXXXXXXX
X
X
X
X
X
X
X
The first line with lots of
X
'es is because this many of them had been accumulated in the buffer before I called the command in line
004
.

2008-09-06

class Array

A handful of methods that could be added to the class
Array
:
class Array

def sum
s=0
each{|e| s+=e}
s
end

def mul
m=1
each{|e| m*=e}
m
end

def mean
sum.to_f/length
end

def map_with_index
i=-1
map{|e| yield(e,i+=1)}
end

def map_with_index!
i=-1
map!{|e| yield(e,i+=1)}
end

def any_with_index?
each_with_index{|e,i| return true if yield(e,i)}
false
end

def all_with_index?
each_with_index{|e,i| return false unless yield(e,i)}
true
end

def find_index
each_with_index{|v,i| return i if yield(v)}
end

def find_indices
ret=[]
each_with_index{|v,i| ret<<i if yield(v)}
ret
end

def select_by_index(*indices)
ret=[]
indices.each{|ind| ret<<self[ind]}
ret
end

alias find_indexes find_indices

def to_hash
raise "Cannot convert to Hash!" unless all?\
{ |e|
e.respond_to? :length and e.length==2 and e.respond_to? :[]
}
h={}
each{|e| h[e[0]]=e[1]}
h
end

def keys_to_hash
h={}
each{|e| h[e]=yield(e)}
h
end

def keys_with_index_to_hash
h={}
each_with_index{|e,i| h[e]=yield(e,i)}
h
end

def with(a2)
ensure_same_length(a2)
map_with_index{|e,i| [e,a2[i]]}
end

def with_to_hash(a2)
ensure_same_length(a2)
h={}
each_with_index{|e,i| h[e]=a2[i]}
h
end

def count_all
h={}
each\
{ |e|
h[e]||=0
h[e]+=1
}
h
end

def group_by
h={}
each\
{ |e|
g=yield(e)
h[g]||=[]
h[g]<< e
}
h.map{|g,ee| ee}
end

alias contain? include?
alias has? include?

def rand
a=to_a
a[Kernel.rand(a.length)] unless a.empty?
end

private

def ensure_same_length(arg)
raise ArgumentError,"Argument must be of the same length!"\
unless arg.respond_to? :length and length==arg.length\
and arg.respond_to? :[]
end

end
They are not perfect, but I use them quite a lot.

Some of them (or similar methods) will be present in Ruby 1.9. For example there will be a method
inject
(or
reduce
) working like this:
[1,4,5].reduce(:*)           #=> 20 # 1*4*5
["a","b","dd"].reduce(:+) #=> "abdd"
As you see, they are better than my
sum
, because they work for any type for which the operation is defined. This reduce is not hard to implement, too, but it will probably work a bit faster when included in Ruby core.

Ruby 1.9 is also going to have
group_by
, working exactly like mine, as far as I know.

Move to Enumerable
One more enhancement that can be done in the above code is to move all the methods in the module
Enumerable
(just write
module Enumerable
instead of
class Array
at the top). It allows you to use these methods also with other enumerable types, like
Hash
. You'll have to test the methods, though, as not all of them make sense when used with structures where the elements are not ordered.

Add to load path
If you create some files that you'd like to be easily accessible in your Ruby programs, you can add the path to your files to Ruby load path, so that you will be able to
require
your files without giving the full path. Under Windows, just go to environment variables, and add
RUBYLIB = P:/ath/To/Your/Dir
The path will be automatically added to Ruby load path each time Ruby starts, which can be verified by typing
$:
(or
$LOAD_PATH
) in irb and looking for your path.

If you want some of your files to be loaded even without the need to
require
them, then you can add them to the environment variable RUBYOPT. This variable can already contain -rubygems. If you want the file P:/ath/To/Your/Dir/start.rb to be loaded at startup, change the variable to
-rubygems -rstart
Each word starting with -r makes ruby load a file named by the rest of the word. Ruby will find your file because you already added file path to Ruby load path. If you want to load more files at startup, it is best to
require
them from within your first file.

As you might have guessed, there is file named ubygems that the original content of the variable caused to load. The strange name is in fact chosen only to make the whole command sound reasonable. All it does is load rubygems.rb, which initialises the Gems engine, enabling programs to use additional libraries.

2008-09-01

{block}

Blocks. The most powerful out of the basic features of Ruby.

Block is a way to pass a bit of code into a function, to let the function execute it if it wants to, and as many times as it wants to. You know already some examples like
[1,2,5].each{|e| puts e}
, where the function
each
calls the block three times - once for each element of
self
.

Let's learn how to write a function that takes a block. I'd like to have a method of the class
Array
that converts the array to
Hash
, where the original array elements become keys, and the values are computed inside the block. Example of how it is supposed to work:
[1,5,3].keys_to_hash{|k| k**2}
#=> {1=>1,5=>25,3=>9}

["Ruby","Al2","O3","Cr"].keys_to_hash{|k| k.length}
#=> {"Al2"=>3,"O3"=>2,"Ruby"=>4,"Cr"=>2}
# remember that Hash does not maintain the order of elements
# so they might get reordered when written irb
So, our function definitely takes a block, and executes it once for each element, and collects the return values of the block as values in the hash. The code that does it is like this:
class Array
def keys_to_hash
raise LocalJumpError,"Block not given!" unless block_given?
h={}
each\
{ |e|
h[e]=yield(e)
}
h
end
end
First we raise an exception if the method was called without a block. This line is not obligatory, as the exception would be raised anyway at the moment when we try to execute the block, so I raise it here mostly to show you how to check if a block is given.

Then we create an empty hash, and then for each element of
self
(works like
self.each
) we write an element to the hash, using the current element
e
as the key, and
yield(e)
as the value. As you must have guessed by now, the keyword
yield
is a call to the passed block.

Finally we return the created
h
as the function result. You can check that the function works as expected.

Just one more example:
class Array
def each_consequent(n)
for i in (0..length-n)
yield(*self[i,n])
end
self
end
end

[2,3,5,7,11,13,17,19,23].each_consequent(3)\
{ |a,b,c|
puts "#{a} #{b} #{c}"
}

# output:
2 3 5
3 5 7
5 7 11
7 11 13
11 13 17
13 17 19
17 19 23
I'll explain just the most suspicious part here:
self[i,n]
is an array (subarray of
self
) and we call
yield
with
*
before the array to make the array splash into the three block arguments
|a,b,c|
. This splash operator is not always necessary, but it's nice to include it to make it clear that the arguments get splashed.

If block is an object...
There are in general two ways of passing a block to a next function. Let's define two functions that behave exactly like
each
:
class Array

def my_each1
each{|a| yield(a)}
end

def my_each2(&b)
each(&b)
end

end
The first one makes a trivial block itself - the block is created just to call the original block coming to
my_each1
with the argument. The second one uses the
&
operator to make the block be assigned into the variable
b
. Inside
my_each2
the variable
b
is a
Proc
object. You could call it by hand inside the function, using
b.call(arg)
or for short
b[arg]
, but in our example it is instead passed to
each
, and the operator
&
makes it sort-of-unsplash back into a block. Two other ways to do it (not very elegant, though):
p=Proc::new{|a| yield(a)}; each(&p)
, or another ugly way:
each{|a| b.call(a)}
. I give these example just to touch your brain and make you understand!

If the block is not passed into a function declared with a block parameter, like
my_each2
, the value of
b
is
nil
, and you don't have to call
block_given?
to check it.

...then we can store it
Now another useful trick. If we can receive a block as an object, or wrap it into a new
Proc
, then it's an object, and can be stored in a variable. Look:
class K

def store_block(&b)
@b=b
end

def call_block(*args)
@b.call(*args)
end

end

k=K::new
k.store_block{|a,b| puts "#{a}::#{b}"} # no output to the console
k.call_block("Al2O3","Cr") # output: Al2O3::Cr
k.call_block("Hi","there") # output: Hi::there
So, we saved the passed block, and called it later. Note one very useful trick: if we receive the arguments as
*args
and pass them on as
*args
as well, then any set of arguments, no matter how many of them you pass to
call_block
, will get forwarded to the block call. (Of course now calling
k.call_block(1,2,3)
will print just
"1::2"
because our block takes two arguments, which means it ignores the third one; but the argument gets lost in the block, and not in
call_block
).

This block saving is not useless. You can for example call a method that saves a block, and executes it later as a callback to an event that happens inside the object. This is a very useful behaviour.

Passing more blocks
Unfortunatelly, Ruby doesn't support passing more blocks to a function. You can have only one parameter with
&
, and there is only one
yield
too. But Ruby does allow passing multiple regular arguments, so what's the problem? Let's write a function that sort of takes two blocks, and calls one of them with the result returned by the call to the other with the argument 5, or opposite:
def random_caller(b1,b2)
raise ArgumentError,"Arguments must be Procs"\
unless b1.is_a? Proc and b2.is_a? Proc
if rand(2).zero?
b1.call(b2.call(5))
else
b2.call(b1.call(5))
end
end

q=lambda\
{
random_caller(lambda{|x| x+2},lambda{|x| x**2})
}
q[] #=> 27
q[] #=> 27
q[] #=> 49
q[] #=> 27
q[] #=> 27
First we check if what we really got are procs. Then we randomly call one of them with
5
and the other with the result of the first one, or the opposite, and return the result.

Now the call. The structure
lambda{|arg| exp}
is more or less the same as
Proc::new{|arg| exp}
and
proc{|arg| exp}
. So
random_caller(lambda{|x| x+2},lambda{|x| x**2})
is a call to our function, and we can expect the result of the call to be either
(5+2)**2
which is
49
, or
(5**2)+2
which is
27
.

Now we must call our function multiple times. We could do it like this:
5.times{random_caller(lambda{|x| x+2},lambda{|x| x**2})}
But, as a part of this tutorial, I made the call to the function into another proc, and stored it in
q
. As you see, you don't even have to pass a block to a function to store it somewhere. You can create a proc just like that, and store it in a local variable, and then call it using
q[]
or
q.call
.

Scope
The scope visible to a block is its declaration scope. What is very interesting, even when the scope is no longer accessible, because the control left the function, it still exists if a lambda was declared there and can use it. This example illustrates the complicated words I just said:
def create_blocks
x=nil
getter=lambda{x}
setter=lambda{|v| x=v}
[setter,getter]
end

s,g=*create_blocks
s[6] # or s.call(6)
g #=> 6
s[:R]
g #=> :R
The scope from inside
create_blocks
is not lost, even though the control left the method and will never return. The variable
x
is still accessible by the lambdas declared in the scope.

Other sources
Here are some link to learn more about gotchas in Ruby's blocks.
Ruby blocks gotchas
Proc vs lambda
Wikipedia - Closure (in many other languages the Ruby clock thing is called closure, or probably more like the closures are called blocks in Ruby)
Wikipedia Smalltalk (this blocks are pretty modern and fresh programming things, aren't they? well, they're not; have a look at Smalltalk (1980))