Search code examples
rubyhashmapruby-hashwriteonly

An "append-only" / "write-only" hash in Ruby


I'm looking for a kind of "append-only" hash where keys may only be set once.

For example:

capitals = AppendOnlyHash.new
capitals['france'] = 'paris'
capitals['japan'] = 'tokyo'
capitals['france'] = 'nice' # raises immutable exception

Any library recommendations or ideas how to achieve this?

(Use case is a logging type object which will be passed to numerouis loosely connected classes, and wanting to detect if any use the same key.)


Solution

  • There are 10 methods, directly mutating the hash:

    Hash.instance_methods.grep(/.+!\z/) << %i|[]= delete keep_if|
    #⇒ [:select!, :filter!, :reject!, :compact!, delete, keep_if,
    #   :transform_keys!, :transform_values!, :merge!, :[]=]
    

    Also, there is a possibility to mutate values themselves (capitals['france'] << ' and Lyon',) so we are to prevent this as well.

    class MyHash < Hash; end
    
    MyHash.prepend(
      Module.new do
        (Hash.instance_methods.grep(/.+!\z/) | %i|delete keep_if|).each do |method|
          define_method(method) do |*args|
            raise "Method #{method} is restricted since it is mutating"
          end
        end
        def []=(key, val)
          raise "This hash is immutable" if key?(key)
          super(key, val.freeze) # to prevent inplace mutations
        end
      end
    )
    

    One needs to derive from Hash because otherwise we are to break all the hashes.

    I did not test this code but it should work out of the box, (if not, the idea should be clear.)