mimic File.open pattern

S

Schüle Daniel

Hello all,

open is singleton method of File and can be used in two ways

# 1 way
f = File.open("data")
f.readline until f.lineno == 10
puts f.readline
f.close

# 2 way
File.open("data") do |f|
f.readline until f.lineno == 10
puts f.readline
end

I am trying to mimic this with my own classes

irb(main):001:0> class Resource
irb(main):002:1> @@cnt=0
irb(main):003:1> def initialize
irb(main):004:2> @id = @@cnt
irb(main):005:2> @@cnt += 1
irb(main):006:2> end
irb(main):007:1> def id;@id;end
irb(main):008:1> end
=> nil
irb(main):009:0> class Q
irb(main):010:1> def Q.get &block
irb(main):011:2> r = Resource.new
irb(main):012:2> if Kernel::block_given?
irb(main):013:3> block.call(r)
irb(main):014:3> else
irb(main):015:3* return r
irb(main):016:3> end
irb(main):017:2> end
irb(main):018:1> end
=> nil
irb(main):019:0> Q.get
=> #<Resource:0x40206fe0 @id=0>
irb(main):020:0> Q.get {|r| puts r.id}
1
=> nil
irb(main):021:0>


I am interested in further suggestions
is my implementation close to that of File.open?

Regards, Daniel
 
D

Daniel Schierbeck

Schüle Daniel said:
Hello all,

open is singleton method of File and can be used in two ways

# 1 way
f = File.open("data")
f.readline until f.lineno == 10
puts f.readline
f.close

# 2 way
File.open("data") do |f|
f.readline until f.lineno == 10
puts f.readline
end

I am trying to mimic this with my own classes

irb(main):001:0> class Resource
irb(main):002:1> @@cnt=0
irb(main):003:1> def initialize
irb(main):004:2> @id = @@cnt
irb(main):005:2> @@cnt += 1
irb(main):006:2> end
irb(main):007:1> def id;@id;end
irb(main):008:1> end
=> nil
irb(main):009:0> class Q
irb(main):010:1> def Q.get &block
irb(main):011:2> r = Resource.new
irb(main):012:2> if Kernel::block_given?
irb(main):013:3> block.call(r)
irb(main):014:3> else
irb(main):015:3* return r
irb(main):016:3> end
irb(main):017:2> end
irb(main):018:1> end
=> nil
irb(main):019:0> Q.get
=> #<Resource:0x40206fe0 @id=0>
irb(main):020:0> Q.get {|r| puts r.id}
1
=> nil
irb(main):021:0>

First off, you can make it more efficient if your use `yield' instead of
calling the block explicitly:

class Q
def self.get
if block_given?
yield Resource.new
else
Resource.new
end
end
end

The above is what I'd go with, but there a lots of ways to accomplish
what you want. Take this for example:

class Q
def self.get
yield resource = Resource.new
rescue LocalJumpError
resource
end
end

or even shorter (though it'll catch other exceptions as well, so you
have to be careful)

class Q
def self.get
yield(resource = Resource.new) rescue resource
end
end


Cheers,
Daniel
 
R

Robert Klemme

Schüle Daniel said:
Hello all,

open is singleton method of File and can be used in two ways

# 1 way
f = File.open("data")
f.readline until f.lineno == 10
puts f.readline
f.close

# 2 way
File.open("data") do |f|
f.readline until f.lineno == 10
puts f.readline
end

I am trying to mimic this with my own classes

irb(main):001:0> class Resource
irb(main):002:1> @@cnt=0
irb(main):003:1> def initialize
irb(main):004:2> @id = @@cnt
irb(main):005:2> @@cnt += 1
irb(main):006:2> end
irb(main):007:1> def id;@id;end
irb(main):008:1> end
=> nil
irb(main):009:0> class Q
irb(main):010:1> def Q.get &block
irb(main):011:2> r = Resource.new
irb(main):012:2> if Kernel::block_given?
irb(main):013:3> block.call(r)
irb(main):014:3> else
irb(main):015:3* return r
irb(main):016:3> end
irb(main):017:2> end
irb(main):018:1> end
=> nil
irb(main):019:0> Q.get
=> #<Resource:0x40206fe0 @id=0>
irb(main):020:0> Q.get {|r| puts r.id}
1
=> nil
irb(main):021:0>


I am interested in further suggestions
is my implementation close to that of File.open?

You're missing the cleanup part when called with a block. The crucial
bit is that the block form allows proper cleanup under all circumstances
by using begin - ensure.

Kind regards

robert
 
T

Timothy Hunter

Daniel said:
First off, you can make it more efficient if your use `yield' instead
of calling the block explicitly:

class Q
def self.get
if block_given?
yield Resource.new
else
Resource.new
end
end
end

The above is what I'd go with, but there a lots of ways to accomplish
what you want. Take this for example:

class Q
def self.get
yield resource = Resource.new
rescue LocalJumpError
resource
end
end

or even shorter (though it'll catch other exceptions as well, so you
have to be careful)

class Q
def self.get
yield(resource = Resource.new) rescue resource
end
end


Cheers,
Daniel
There's no need to golf this. There's a well-known idiom for coding
block-scoped resources. See the section entitled "Destroy this object
when it goes out of scope" at
http://wiki.rubygarden.org/Ruby/page/show/RubyIdioms. Here's the example
from that page:

def Resource.open( identifier ) # :yield: resource
resource = Resource.new( identifier )
if block_given?
begin
yield resource
ensure
resource.close
end
else
return resource
end
end


The benefit of using the standard idiom is that any Ruby programmer that
reads your code will instantly understand what it's doing. Coding it any
other way will likely cause a bit of confusion. And, of course, if you
don't use the idiom you might forget the "ensure" part.
 
D

Daniel Schierbeck

Timothy said:
There's no need to golf this. There's a well-known idiom for coding
block-scoped resources. See the section entitled "Destroy this object
when it goes out of scope" at
http://wiki.rubygarden.org/Ruby/page/show/RubyIdioms. Here's the example
from that page:

def Resource.open( identifier ) # :yield: resource
resource = Resource.new( identifier )
if block_given?
begin
yield resource
ensure
resource.close
end
else
return resource
end
end


The benefit of using the standard idiom is that any Ruby programmer that
reads your code will instantly understand what it's doing. Coding it any
other way will likely cause a bit of confusion. And, of course, if you
don't use the idiom you might forget the "ensure" part.

I don't think the original post mentioned that the resource needed to be
closed :) I thought he wanted an easy way to either yield or return. But
yes, for that wider problem, your code is superior. We were just
answering different questions.


Cheers,
Daniel
 

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
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top