Ruby syntax question: instance.method(args) { more_stuff }

Hi, I’m having difficulty working out the following construct:
img.write(“myimage.jpg”) { self.quality = 50 }
In this example (source
RMagick 1.15.0: Common Tasks), img is a
RMagick::Image.

The API says “You may also specify optional arguments by setting
Image::Info http://www.simplesystems.org/RMagick/doc/info.html
attributes in an associated block.” Is this a standard syntax in Ruby or
is it specific to this write method? And how does it work, how can I
make my own method accept blocks too? any advantage over passing a array
of arguments?

Also, I’m trying to pass my own instance variable in order to replace
the sample value of 50. So I’ve tried:

img.write(“myimage.jpg”) { self.quality = @my_object_quality }

Where @my_object_quality is defined as an instance variable on my class
def. But the interpreters seams to look for a @my_object_quality
instance variable within the RMagick class, which vaguely kind of make
sense. So how can I pass this instance variable into this method? and
why does it say “self.quality = 50” in the example and not “@quality =
50”?

Thanks in adance for your help. I’m looking for a genuine explanation of
what’s happening at a Ruby syntax level, not just a code sample that
would work with this RMagick example.

Nic

On 5/1/07, Nic D. [email protected] wrote:

how can I
make my own method accept blocks too?

Here is one explanation.

http://www.rubycentral.com/book/tut_containers.html#UD

Harry

On Tue, May 01, 2007 at 07:14:43PM +0900, Nic D. wrote:

The API says “You may also specify optional arguments by setting
Image::Info http://www.simplesystems.org/RMagick/doc/info.html
attributes in an associated block.” Is this a standard syntax in Ruby or
is it specific to this write method? And how does it work, how can I
make my own method accept blocks too? any advantage over passing a array
of arguments?

It’s fairly standard. It has the advantage of only being run when
required,
e.g. zero times if no object ends up being created, or it could be run
multiple times. Compare:

a = Array.new(2) { [] }
a[0] << 'bar'
a[1] << 'bap'

Here the initialization block is run twice, so you get two different
empty
arrays. This is different to

a = Array.new(2, [])
a[0] << 'bar'
a[1] << 'bap'

Try it in irb, and look at ‘a’ afterwards in both cases.

For another example of this pattern, look at dependency injection, e.g.
http://wikis.onestepback.org/OSB/page/show/DependencyInjectionCode/DepInjRb
(site appears to be very slow at present)

In this case the advantage is that the initialization code is run at the
latest possible time; at the time the block is created not all the
values
are known.

How to do it yourself?

class Foo
attr_accessor :x
def initialize(&blk)
instance_eval(&blk) if blk
end
end
a = Foo.new { self.x = 99 }
puts a.x

Also, I’m trying to pass my own instance variable in order to replace
the sample value of 50. So I’ve tried:

img.write(“myimage.jpg”) { self.quality = @my_object_quality }

Where @my_object_quality is defined as an instance variable on my class
def. But the interpreters seams to look for a @my_object_quality
instance variable within the RMagick class, which vaguely kind of make
sense. So how can I pass this instance variable into this method?

Simplest just to use a local variable:

foo = @my_object_quality
… { self.quality = foo }

If there are lots of these, then pass an instance of yourself, as long
as
you have public accessor methods.

template = self
… { self.quality = template.quality }

and
why does it say “self.quality = 50” in the example and not “@quality =
50”?

I guess it’s just standard accessor syntax, where it’s considered
cleaner to
call a setter method (which might implement additional logic) rather
than
twiddle directly with instance variables in an object, which are usually
considered ‘private’

Brian.

Brian C. wrote:

On Tue, May 01, 2007 at 07:14:43PM +0900, Nic D. wrote:
(…)

why does it say “self.quality = 50” in the example and not “@quality =
50”?

I guess it’s just standard accessor syntax, where it’s considered
cleaner to
call a setter method (which might implement additional logic) rather
than
twiddle directly with instance variables in an object, which are usually
considered ‘private’

Brian.

Brian’s explanation is correct. I learned this technique from the 1st
edition of Hal F.'s “The Ruby Way.” I think I remember seeing it in
the 2nd edition, too, although I don’t have my copy with me to check.

I’m using this technique to support the dozen or so optional arguments
that Image#write can accept. Overall it works well, although since the
block is executed in the context of an RMagick object, all that object’s
instance variables are exposed to the caller. RMagick mitigates this
problem by running the block in the context of an Image::Info object,
which doesn’t have any private data or methods, just attribute
accessors.