equivalent of Python's "with" in Ruby equivalent of Python's "with" in Ruby ruby ruby

equivalent of Python's "with" in Ruby


Ruby has syntactically lightweight support for literal anonymous procedures (called blocks in Ruby). Therefore, it doesn't need a new language feature for this.

So, what you normally do, is to write a method which takes a block of code, allocates the resource, executes the block of code in the context of that resource and then closes the resource.

Something like this:

def with(klass, *args)  yield r = klass.open(*args)ensure  r.closeend

You could use it like this:

with File, 'temp.txt', 'w' do |f|  f.write 'hi'  raise 'spitespite'end

However, this is a very procedural way to do this. Ruby is an object-oriented language, which means that the responsibility of properly executing a block of code in the context of a File should belong to the File class:

File.open 'temp.txt', 'w' do |f|  f.write 'hi'  raise 'spitespite'end

This could be implemented something like this:

def File.open(*args)  f = new(*args)  return f unless block_given?  yield fensure  f.close if block_given?end

This is a general pattern that is implemented by lots of classes in the Ruby core library, standard libraries and third-party libraries.


A more close correspondence to the generic Python context manager protocol would be:

def with(ctx)  yield ctx.setupensure  ctx.teardownendclass File  def setup; self end  alias_method :teardown, :closeendwith File.open('temp.txt', 'w') do |f|  f.write 'hi'  raise 'spitespite'end

Note that this is virtually indistinguishable from the Python example, but it didn't require the addition of new syntax to the language.


The equivalent in Ruby would be to pass a block to the File.open method.

File.open(...) do |file|  #do stuff with fileend  #file is closed

This is the idiom that Ruby uses and one that you should get comfortable with.


You could use Block Arguments to do this in Ruby:

class Object      def with(obj)          obj.__enter__          yield          obj.__exit__      end  end

Now, you could add __enter__ and __exit__ methods to another class and use it like this:

with GetSomeObject("somefile.text") do |foo|      do_something_with(foo)end