简体   繁体   中英

Returning an iterator or opaque collection in Ruby

I'm often in a situation where I have a class that contains a collection. I'd like external code to be able to iterate over this collection, but not modify it.

I end up writing something like this:

def iter
  @internal_collection.each do |o|
     yield o
  end
end

This allows the external code to do:

object.iter do |o|
    do_something(o)
end

There must be a more elegant way of writing the "iter" method. Any ideas?

Before elegance, I would make sure I return an Enumerator if no block is given.

This way your users can do object.iter.with_index do |obj, i|

An easy way to do this is and shorten your code is:

def iter(&block)
  @internal_collection.each(&block)
end

In other circumstances, you might want to simply return a copy...

def collection
  @internal_collection.dup
end

As far as explicitly writing the method goes, that's about as simple as it gets. But I think what you're after is the Forwardable module. Your code would look like this:

require 'forwardable'

class YourClass
  extend Forwardable
  def_delegator :@internal_collection, :each, :iter
end

Or if you wanted, you could delegate the whole Enumerable protocol to your internal collection and get all the standard Enumerable behavior that your internal collection features:

class YourClass
  extend Forwardable
  def_delegators :@internal_collection, *Enumerable.instance_methods
end

I'd use dup and freeze on your internal collection, then expose it to the world:

def collection
  @internal_collection.dup.freeze
end

collection.map!(&:to_s) #=> raise RuntimeError: can't modify frozen Array

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM