Methodhash 0.5 released

I created a small “lab tool” that makes life easier for me. It’s
basically a Hash that you can’t put values into, the values can only
be obtained from a method that you define. I created it with the idea
that a calculation should only have to be done once. I paste the
README file below.
Please let me know what you think!

Best regards,
Fredrik J.

NAME

methodhash

SYNOPSIS

A Hash subclass for automatic storage of values obtained from a
method
defined by the user. Useful for lengthy calculations on large
datasets.

URI

GitHub - fredrikj/methodhash: A Ruby Hash defined by a method. Enables disk storage of values calculated by method.

INSTALL

gem install methodhash

DESCRIPTION

A Hash subclass that defines its values from a specified method.
Use it by creating a subclass of MethodHash, and define a method
with the name “mymethod” in it.
Like this (same code in samples/samples.rb):

1. Simple use

class AddOne < MethodHash
def mymethod(a)
sleep 3
a + 1
end
end

a = AddOne.new
a # {}
a[1] # 2
a[7] # 8
a # {1=>2, 7=>8}
puts a.dump # — !map:AddOne
# 1: 2
# 7: 8

2. With a file

b = AddOne.new ‘/tmp/one.yml’
b # {}
b[1] # 2
b.dump # ‘/tmp/one.yml’
c = AddOne.new ‘/tmp/one.yml’
puts c.inspect # {1=>2}

3. Some protection against data corruption.

class AddTwo < MethodHash
def mymethod(a)
a + 2
end
end

begin
d = AddTwo.new ‘/tmp/one.yml’ # ArgumentError: Path holds class
AddOne
rescue
puts $!
end

4. Saving exceptions arising from mymethod.

class AddOneFaulty < MethodHash
def mymethod(a)
rand(2)==0 ? raise(“Epic Fail!”) : a+1
end
end

e = AddOneFaulty.new
e[1] # RuntimeError: Epic Fail! # If something bad
happened
e # {1=>“ERROR: Epic Fail!”}
e.retry_errors # false
e[1] # RuntimeError: Epic Fail! # Still keeping
the error
e.retry_errors=true # true
e[1] # 2 # If better luck
this time
e # {1=>2}

5. A more complex setting

class AddThree < MethodHash
def initialize(path1=nil, path2=nil, mypath=nil)
@one = AddOne.new(path1)
@two = AddTwo.new(path2)
super(mypath)
end

def mymethod(a)
  @one[a] + @two[a] - a
end

def dump
  @one.dump
  @two.dump
  super
end

end

f = AddThree.new( ‘/tmp/one.yml’, ‘/tmp/two.yml’)
puts f[3]
f.dump

6. With two arguments

class Add < MethodHash
def mymethod(a,b)
a + b
end
end

HISTORY
0.5.0

Initial version

On 02/04/2010 12:46 PM, Fredrik J. wrote:

I created a small “lab tool” that makes life easier for me. It’s
basically a Hash that you can’t put values into, the values can only
be obtained from a method that you define. I created it with the idea
that a calculation should only have to be done once. I paste the
README file below.
Please let me know what you think!

http://raa.ruby-lang.org/project/memoize/

end

end

a = AddOne.new
a # {}
a[1] # 2
a[7] # 8
a # {1=>2, 7=>8}
puts a.dump # — !map:AddOne
# 1: 2
# 7: 8

You can do this with a Hash already:

irb(main):001:0> a = Hash.new {|h,k| printf(“calc %p\n”,k);h[k] = k + 2}

add two

=> {}
irb(main):002:0> a[1]
calc 1
=> 3
irb(main):003:0> a[1]
=> 3
irb(main):004:0> a[5]
calc 5
=> 7
irb(main):005:0> a[5]
=> 7
irb(main):006:0> a
=> {1=>3, 5=>7}
irb(main):007:0>

Granted, serialization to a file needs a few lines more.

Kind regards

robert

You can do this with a Hash already:

Sure (I learned that from Ruby Best Practices. great book!), but I
would like to argue that it’s a bit more tidy to define a class (a
subclass of MethodHash) in one place, which I can then reuse for
anything I need to. But I don’t claim to have revolutionized the world
with this. :slight_smile:

Granted, serialization to a file needs a few lines more.

The use I have of it is that some calculations I have made that took
some time to do, is easily available at any time later by just
remembering the class name I used for it. Maybe the name MethodHash is
misleading…

On Thu, Feb 4, 2010 at 8:05 AM, Fredrik J. [email protected]
wrote:

You can do this with a Hash already:

Sure (I learned that from Ruby Best Practices. great book!), but I
would like to argue that it’s a bit more tidy to define a class (a
subclass of MethodHash) in one place, which I can then reuse for
anything I need to. But I don’t claim to have revolutionized the world
with this. :slight_smile:

Doing so introduces an inheritance relationship, which is rarely a
good thing in Ruby.
You might want to check out the link Robert suggested, for memoize.

http://raa.ruby-lang.org/project/memoize/

It also handles file based caching and is one idiomatic way of doing
what you’re trying to do here.

-greg

On 04.02.2010 14:02, Fredrik J. wrote:

You can do this with a Hash already:

Sure (I learned that from Ruby Best Practices. great book!), but I
would like to argue that it’s a bit more tidy to define a class (a
subclass of MethodHash) in one place, which I can then reuse for
anything I need to.

Well, there are multiple ways to reuse code and maintain DRY. You do
not necessarily need a class for that.

def add_two
Hash.new {|h,k| h[k] = k + 2}
end

or, a more generic variant

def make_cache
Hash.new {|h,k| h[k] = yield(k)}
end

AddTwo = make_cache {|x| x + 2}

etc.

But I don’t claim to have revolutionized the world
with this. :slight_smile:

Well, if you put a library in public then you probably want it to be
used. For that it needs to provide value to others. If it doesn’t it’s
probably not used and your effort may be in vain. :slight_smile:

The use I have of it is that some calculations I have made that took
some time to do, is easily available at any time later by just
remembering the class name I used for it. Maybe the name MethodHash is
misleading…

But what you actually want is the instance that does the caching,
otherwise you’ll loose cached values. Or do you store results somewhere
in a class instance variable?

Kind regards

robert