Mimic File.open pattern

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

Schüle Daniel wrote:

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

Schüle Daniel wrote:

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

Daniel Schierbeck wrote:

Schüle Daniel wrote:

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

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.

Timothy Hunter wrote:

Daniel Schierbeck wrote:

Schüle Daniel wrote:

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

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 :slight_smile: 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