Ruby equivalent of C# "using keword" and IDispose

P

Peter Alvin

What is the most expedient way to do this C# construct in Ruby?

C#: using (myObj = new MyClass()) {
// misc code here
} //MyClass.Dispose automatically called here

I wish to put stuff in MyClass.Dispose() to release resources... it must
always be called.

Pete
 
R

Robert Klemme

2010/1/21 Peter Alvin said:
What is the most expedient way to do this C# construct in Ruby?

C#: using (myObj =3D new MyClass()) {
=A0 =A0// misc code here
} //MyClass.Dispose automatically called here

I wish to put stuff in MyClass.Dispose() to release resources... it must
always be called.

You want "ensure". See
http://blog.rubybestpractices.com/posts/rklemme/001-Using_blocks_for_Robust=
ness.html
http://blog.rubybestpractices.com/posts/rklemme/002_Writing_Block_Methods.h=
tml

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
J

Jörg W Mittag

Peter said:
What is the most expedient way to do this C# construct in Ruby?

C#: using (myObj = new MyClass()) {
// misc code here
} //MyClass.Dispose automatically called here

I wish to put stuff in MyClass.Dispose() to release resources... it must
always be called.

In a language with higher-order procedures like Ruby, adding a special
language construct for this is completely unnecessary. You just write
a method which accepts a piece of code as an argument, acquires the
resource, runs the piece of code and subsequently releases the
resource. This is the well-known "with-foo" pattern from Lisp,
basically. In Ruby, we generally use a factory-ish class method like
so:

class DatabaseConnection
def self.use(*args)
yield handle = new(*args)
ensure
handle.close
end
end

This is how usage would look like:

DatabaseConnection.use('/path/to/sqlite.db') do |conn|
conn.exec 'SELECT blah FROM blurb WHERE foo = 42;'
conn.exec 'INSERT something INTO somewhere;'
end

This pattern is for example used by Io_Open in the standard library:

File.open('/path/to/file', 'w+') do |f|
f.puts 'Hello, World'
end

The two main differences between the two methods are:

* in the Ruby version, the acquisition of the resource is hidden away
inside the method, whereas in C#, the user is responsible for
creating the resource. (You could, of course, write a generic
method that simply takes any resource as a parameter. That's just
not the normal style in Ruby.)
* in the C# version, there is a standardized agreement over what the
name of the method for releasing the resource is (the Dispose()
pattern) and there is even a type for it (the IDisposable
interface)

Here's what a more C# style implementation in Ruby would look like:

def using(handle)
yield handle
ensure
handle.dispose
end

using(MyClass.new) do |my_obj|
# misc code here
end # MyClass#dispose automatically called here

BTW: you can do that in C#, too, now. It's just that before lambda
expressions, local variable type inference and anonymous types were
introduced in C#, it would have been a lot of typing (in both senses
of the word). This is what the opposite would look like (i.e. Ruby
style implemented in C#):

class DatabaseConnection
{
static T use<T>(string connection, Func<DatabaseConnection, T> block)
{
var handle = new DatabaseConnection(connection);
try
{
return block(handle);
}
finally
{
handle.Dispose();
}
}
}

And this is how you use it:

DatabaseConnection.use("/path/to/db", (conn) =>
{
// misc code here
}); // MyClass.Dispose automatically called here

(If you don't want to return a value, lose the T and replace Func with
Action.)

jwm
 
A

Allen Lee

I finally came up with this toy:

def using(res, options =3D { release_with: :close })
yield res
ensure
if res.respond_to?(options[:release_with])
res.send(options[:release_with])
end
end

With using method, you can have C# style coding as follows:

using(db =3D SQLite3::Database.new('cart.db')) {
# ...
}

Or you can make use of the block parameter:

using(SQLite3::Database.new('cart.db')) { |db|
# ...
}

If, however, the resource you are about to use favers a different method to
release, you can also specify it to using:

using(SQLite3::Database.new('cart.db'), release_with: "close") { |db|
# ...
}

Note that you need to go back to :release_with =3D> "close" if you are
targeting version before 1.9.

Enjoy!

allen

2010/1/22 J=F6rg W Mittag
 
R

Robert Klemme

2010/1/22 Allen Lee said:
I finally came up with this toy:

This looks interesting. Although I have to say that the functionality
should be rather built into the classes at hand (like e.g. File has)
IMHO.
def using(res, options =3D { release_with: :close })

Since you have a single option only you can as well do

def using(res, close =3D :close)
=A0yield res
ensure
=A0if res.respond_to?(options[:release_with])
=A0 =A0res.send(options[:release_with])
=A0end

I'd rather do

res.send(options[:release_with]) rescue nil

Because the idea of respond_to? and what the class really understands
may differ.

irb(main):001:0> class X
irb(main):002:1> def method_missing(s,*a,&b)
irb(main):003:2> "I can #{s}!"
irb(main):004:2> end
irb(main):005:1> end
=3D> nil
irb(main):006:0> o =3D X.new
=3D> #<X:0x101596fc>
irb(main):007:0> o.respond_to? :dance
=3D> false
irb(main):008:0> o.dance
=3D> "I can dance!"
irb(main):009:0> o.dance!
=3D> "I can dance!!"
irb(main):010:0>
end

Note that you need to go back to :release_with =3D> "close" if you are
targeting version before 1.9.

This is not necessary if you just use a single additional parameter (see ab=
ove).

Kind regards

robert


--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
A

Allen Lee

[Note: parts of this message were removed to make it a legal post.]

1) I also considered

def using(res, close = :close)

over

def using(res, options = { release_with: :close })

however, I think

using(Res.new, release_with: "close")

would be nicer than

using(Res.new, "close")

so finally I chose what it is right now :)

2) The solution you provided as follows is definitely more robust due to
respond_to? may not work as expected in the situation you mentioned and
thanks!

res.send(options[:release_with]) rescue nil

allen

2010/1/22 Allen Lee said:
I finally came up with this toy:

This looks interesting. Although I have to say that the functionality
should be rather built into the classes at hand (like e.g. File has)
IMHO.
def using(res, options = { release_with: :close })

Since you have a single option only you can as well do

def using(res, close = :close)
yield res
ensure
if res.respond_to?(options[:release_with])
res.send(options[:release_with])
end

I'd rather do

res.send(options[:release_with]) rescue nil

Because the idea of respond_to? and what the class really understands
may differ.

irb(main):001:0> class X
irb(main):002:1> def method_missing(s,*a,&b)
irb(main):003:2> "I can #{s}!"
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> o = X.new
=> #<X:0x101596fc>
irb(main):007:0> o.respond_to? :dance
=> false
irb(main):008:0> o.dance
=> "I can dance!"
irb(main):009:0> o.dance!
=> "I can dance!!"
irb(main):010:0>
end

Note that you need to go back to :release_with => "close" if you are
targeting version before 1.9.

This is not necessary if you just use a single additional parameter (see
above).

Kind regards

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,160
Messages
2,570,890
Members
47,423
Latest member
henerygril

Latest Threads

Top