Saturday, January 9, 2010

Improving ImmutableProxy

My initial version of ImmutableProxy worked perfectly for methods that were specific to the object being wrapped, but it didn't work properly for the built-in Object methods.

Most of the built-in object methods aren't actually used in my code and probably aren't needed by the players, so I didn't want to waste time overriding them all.  But some of the basic ones were needed, like eql?, and is_a? (is_a? is used by the eql? methods defined by the classes being wrapped).



My initial plan for ImmutableProxy was that the code that's using one doesn't have to know or care that it's using one except that it needs to not call any of the mutating functions.  However, to avoid having to override every built-in Object method I think I'm going to change it so that the code does need to know if it wants to call one of the non-overriden Object methods.  I doubt I'll need it anywhere in my code, but it was easy enough to add the capabilities to ImmutableObject.

I overrode a few of the more used Object methods, and then added a send_to_target method that works just like send, but passed it directly to the target object (after making sure it's an allowed method).

Here's what I've got so far:

# Proxies any object but doesn't allow any mutating method (methods ending in
# !) from being called.
class ImmutableProxy
  def initialize(target)
    @target = target
  end

  # Use this to call any built-in object methods that are masked by
  # ImmutableProxy's built-in object methods.
  def send_to_target(name, *args, &block)
    raise "Mutable methods not allowed" unless allowed?(name)
    @target.__send__(name, *args, &block)
  end

  def method_missing(name, *args, &block)
    send_to_target(name, *args, &block)
  end

  def respond_to?(name, include_private=false)
    return false unless allowed?(name)
    @target.respond_to?(name, include_private)
  end

  def eql?(o)
    @target.eql?(o)
  end

  def ==(o)
    @target == o
  end

  def dup
    ImmutableProxy.new @target
  end

  def hash
    @target.hash
  end

  def is_a?(c)
    @target.is_a?(c)
  end

  def nil?
    @target.nil?
  end
private
  def allowed?(name)
    ! name.to_s.end_with?('!') || !@target.respond_to?(name)
  end
end



More work on ImmutableProxy to make it more flexible

No comments:

Post a Comment