methodhash 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +121 -0
- data/lib/methodhash.rb +93 -0
- data/methodhash.gemspec +24 -0
- data/samples/samples.rb +88 -0
- metadata +58 -0
data/README
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
NAME
|
2
|
+
|
3
|
+
methodhash
|
4
|
+
|
5
|
+
SYNOPSIS
|
6
|
+
|
7
|
+
A Hash subclass for automatic storage of values obtained from a method
|
8
|
+
defined by the user. Useful for lengthy calculations on large datasets.
|
9
|
+
|
10
|
+
URI
|
11
|
+
|
12
|
+
http://rubyforge.org/projects/methodhash
|
13
|
+
http://github.com/fredrikj/methodhash
|
14
|
+
|
15
|
+
INSTALL
|
16
|
+
|
17
|
+
gem install methodhash
|
18
|
+
|
19
|
+
DESCRIPTION
|
20
|
+
|
21
|
+
A Hash subclass that defines its values from a specified method.
|
22
|
+
Use it by creating a subclass of MethodHash, and define a method
|
23
|
+
with the name "mymethod" in it. Like this (same code in samples/samples.rb):
|
24
|
+
|
25
|
+
# 1. Simple use
|
26
|
+
class AddOne < MethodHash
|
27
|
+
def mymethod(a)
|
28
|
+
sleep 3
|
29
|
+
a + 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
a = AddOne.new
|
34
|
+
a # {}
|
35
|
+
a[1] # 2
|
36
|
+
a[7] # 8
|
37
|
+
a # {1=>2, 7=>8}
|
38
|
+
puts a.dump # --- !map:AddOne
|
39
|
+
# 1: 2
|
40
|
+
# 7: 8
|
41
|
+
|
42
|
+
|
43
|
+
# 2. With a file
|
44
|
+
b = AddOne.new '/tmp/one.yml'
|
45
|
+
b # {}
|
46
|
+
b[1] # 2
|
47
|
+
b.dump # '/tmp/one.yml'
|
48
|
+
c = AddOne.new '/tmp/one.yml'
|
49
|
+
puts c.inspect # {1=>2}
|
50
|
+
|
51
|
+
|
52
|
+
# 3. Some protection against data corruption.
|
53
|
+
class AddTwo < MethodHash
|
54
|
+
def mymethod(a)
|
55
|
+
a + 2
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
begin
|
60
|
+
d = AddTwo.new '/tmp/one.yml' # ArgumentError: Path holds class AddOne
|
61
|
+
rescue
|
62
|
+
puts $!
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# 4. Saving exceptions arising from mymethod.
|
67
|
+
class AddOneFaulty < MethodHash
|
68
|
+
def mymethod(a)
|
69
|
+
rand(2)==0 ? raise("Epic Fail!") : a+1
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
e = AddOneFaulty.new
|
74
|
+
e[1] # RuntimeError: Epic Fail! # If something bad happened
|
75
|
+
e # {1=>"ERROR: Epic Fail!"}
|
76
|
+
e.retry_errors # false
|
77
|
+
e[1] # RuntimeError: Epic Fail! # Still keeping the error
|
78
|
+
e.retry_errors=true # true
|
79
|
+
e[1] # 2 # If better luck this time
|
80
|
+
e # {1=>2}
|
81
|
+
|
82
|
+
|
83
|
+
# 5. A more complex setting
|
84
|
+
class AddThree < MethodHash
|
85
|
+
def initialize(path1=nil, path2=nil, mypath=nil)
|
86
|
+
@one = AddOne.new(path1)
|
87
|
+
@two = AddTwo.new(path2)
|
88
|
+
super(mypath)
|
89
|
+
end
|
90
|
+
|
91
|
+
def mymethod(a)
|
92
|
+
@one[a] + @two[a] - a
|
93
|
+
end
|
94
|
+
|
95
|
+
def dump
|
96
|
+
@one.dump
|
97
|
+
@two.dump
|
98
|
+
super
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
f = AddThree.new( '/tmp/one.yml', '/tmp/two.yml')
|
103
|
+
puts f[3]
|
104
|
+
f.dump
|
105
|
+
|
106
|
+
|
107
|
+
# 6. With two arguments
|
108
|
+
class Add < MethodHash
|
109
|
+
def mymethod(a,b)
|
110
|
+
a + b
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
HISTORY
|
118
|
+
0.5.0
|
119
|
+
|
120
|
+
Initial version
|
121
|
+
|
data/lib/methodhash.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
class MethodHash < Hash
|
4
|
+
|
5
|
+
attr_accessor :retry_errors, :force_calc
|
6
|
+
attr_reader :changed, :path
|
7
|
+
alias_method :originalassign, :[]=
|
8
|
+
|
9
|
+
# --- initialize ---
|
10
|
+
# A path is optional but necessary if you want to store the Hash to disk.
|
11
|
+
def initialize(path=nil)
|
12
|
+
unless self.respond_to? :mymethod
|
13
|
+
raise ArgumentError,
|
14
|
+
"No calculation method (with name 'mymethod') defined for #{self.class}"
|
15
|
+
end
|
16
|
+
if self.class.to_s == 'MethodHash'
|
17
|
+
raise ArgumentError, "Make a subclass. Don't use MethodHash directly."
|
18
|
+
end
|
19
|
+
@path = path
|
20
|
+
@changed = false
|
21
|
+
@retry_errors = false
|
22
|
+
@force_calc = false
|
23
|
+
if @path and File.exists? @path
|
24
|
+
hash = YAML.load open(@path)
|
25
|
+
raise ArgumentError, "Path holds class #{hash.class}" if hash.class != self.class
|
26
|
+
self.merge! hash
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# --- [] ---
|
31
|
+
# If not previously called with given arguments, calls 'mymethod'
|
32
|
+
# with the arguments and inserts the answer in the Hash.
|
33
|
+
# If force_calc is set to true, mymethod is always called.
|
34
|
+
# If retry_errors is set to true, mymethod is also called if value
|
35
|
+
# has previously been retrieved but raised an error at that time.
|
36
|
+
def [](*arg)
|
37
|
+
arg = arg.first if arg.size==1
|
38
|
+
if !force_calc and s=super(arg)
|
39
|
+
if s.is_a? String and s[0,6]=='ERROR:'
|
40
|
+
if @retry_errors
|
41
|
+
self.delete arg
|
42
|
+
self.[](*arg)
|
43
|
+
else
|
44
|
+
raise s[7..-1]
|
45
|
+
end
|
46
|
+
else
|
47
|
+
s
|
48
|
+
end
|
49
|
+
else
|
50
|
+
@changed = true
|
51
|
+
begin
|
52
|
+
originalassign(arg,self.mymethod(*arg))
|
53
|
+
rescue
|
54
|
+
originalassign(arg,"ERROR: #{$!}")
|
55
|
+
raise
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# --- []= ---
|
61
|
+
# This method cannot be used.
|
62
|
+
def []=(key, value)
|
63
|
+
raise "You can't just put values into a #{self.class}."
|
64
|
+
end
|
65
|
+
|
66
|
+
# --- dump ---
|
67
|
+
# If a path has been given, writes self to that file and returns the path.
|
68
|
+
# Otherwise, returns a string containing the dump.
|
69
|
+
def dump
|
70
|
+
if @path
|
71
|
+
if self.changed?
|
72
|
+
open(@path,'w'){|f| YAML.dump(self,f) }
|
73
|
+
@changed = false
|
74
|
+
end
|
75
|
+
@path
|
76
|
+
elsif !path
|
77
|
+
@changed = false
|
78
|
+
YAML.dump(self)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def delete(arg)
|
83
|
+
@changed = true
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
# --- changed? ---
|
88
|
+
# Has something changed since the last call to dump?
|
89
|
+
def changed?
|
90
|
+
@changed
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/methodhash.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
## methodhash.gemspec
|
2
|
+
#
|
3
|
+
|
4
|
+
Gem::Specification::new do |spec|
|
5
|
+
spec.name = "methodhash"
|
6
|
+
spec.description = 'A Hash for automatic storage of values obtained from a method defined by the user.'
|
7
|
+
spec.version = "0.5.0"
|
8
|
+
spec.platform = Gem::Platform::RUBY
|
9
|
+
spec.summary = 'methodhash'
|
10
|
+
|
11
|
+
spec.files = ["methodhash.gemspec", "lib", "lib/methodhash.rb", "README", "samples", "samples/samples.rb"]
|
12
|
+
spec.executables = []
|
13
|
+
|
14
|
+
spec.require_path = "lib"
|
15
|
+
|
16
|
+
spec.has_rdoc = true
|
17
|
+
|
18
|
+
spec.extensions.push(*[])
|
19
|
+
|
20
|
+
spec.rubyforge_project = "methodhash"
|
21
|
+
spec.author = "Fredrik Johansson"
|
22
|
+
spec.email = "fredjoha@gmail.com"
|
23
|
+
spec.homepage = "http://github.com/fredrikj/methodhash/tree/master"
|
24
|
+
end
|
data/samples/samples.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# 1. Simple use
|
2
|
+
class AddOne < MethodHash
|
3
|
+
def mymethod(a)
|
4
|
+
sleep 3
|
5
|
+
a + 1
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
a = AddOne.new
|
10
|
+
a # {}
|
11
|
+
a[1] # 2
|
12
|
+
a[7] # 8
|
13
|
+
a # {1=>2, 7=>8}
|
14
|
+
puts a.dump # --- !map:AddOne
|
15
|
+
# 1: 2
|
16
|
+
# 7: 8
|
17
|
+
|
18
|
+
|
19
|
+
# 2. With a file
|
20
|
+
b = AddOne.new '/tmp/one.yml'
|
21
|
+
b # {}
|
22
|
+
b[1] # 2
|
23
|
+
b.dump # '/tmp/one.yml'
|
24
|
+
c = AddOne.new '/tmp/one.yml'
|
25
|
+
puts c.inspect # {1=>2}
|
26
|
+
|
27
|
+
|
28
|
+
# 3. Some protection against data corruption.
|
29
|
+
class AddTwo < MethodHash
|
30
|
+
def mymethod(a)
|
31
|
+
a + 2
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
begin
|
36
|
+
d = AddTwo.new '/tmp/one.yml' # ArgumentError: Path holds class AddOne
|
37
|
+
rescue
|
38
|
+
puts $!
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# 4. Saving exceptions arising from mymethod.
|
43
|
+
class AddOneFaulty < MethodHash
|
44
|
+
def mymethod(a)
|
45
|
+
rand(2)==0 ? raise("Epic Fail!") : a+1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
e = AddOneFaulty.new
|
50
|
+
e[1] # RuntimeError: Epic Fail! # If something bad happened
|
51
|
+
e # {1=>"ERROR: Epic Fail!"}
|
52
|
+
e.retry_errors # false
|
53
|
+
e[1] # RuntimeError: Epic Fail! # Still keeping the error
|
54
|
+
e.retry_errors=true # true
|
55
|
+
e[1] # 2 # If better luck this time
|
56
|
+
e # {1=>2}
|
57
|
+
|
58
|
+
|
59
|
+
# 5. A more complex setting
|
60
|
+
class AddThree < MethodHash
|
61
|
+
def initialize(path1=nil, path2=nil, mypath=nil)
|
62
|
+
@one = AddOne.new(path1)
|
63
|
+
@two = AddTwo.new(path2)
|
64
|
+
super(mypath)
|
65
|
+
end
|
66
|
+
|
67
|
+
def mymethod(a)
|
68
|
+
@one[a] + @two[a] - a
|
69
|
+
end
|
70
|
+
|
71
|
+
def dump
|
72
|
+
@one.dump
|
73
|
+
@two.dump
|
74
|
+
super
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
f = AddThree.new( '/tmp/one.yml', '/tmp/two.yml')
|
79
|
+
puts f[3]
|
80
|
+
f.dump
|
81
|
+
|
82
|
+
|
83
|
+
# 6. With two arguments
|
84
|
+
class Add < MethodHash
|
85
|
+
def mymethod(a,b)
|
86
|
+
a + b
|
87
|
+
end
|
88
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: methodhash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Fredrik Johansson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-02 00:00:00 +09:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A Hash for automatic storage of values obtained from a method defined by the user.
|
17
|
+
email: fredjoha@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- methodhash.gemspec
|
26
|
+
- lib/methodhash.rb
|
27
|
+
- README
|
28
|
+
- samples/samples.rb
|
29
|
+
has_rdoc: true
|
30
|
+
homepage: http://github.com/fredrikj/methodhash/tree/master
|
31
|
+
licenses: []
|
32
|
+
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: "0"
|
43
|
+
version:
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
requirements: []
|
51
|
+
|
52
|
+
rubyforge_project: methodhash
|
53
|
+
rubygems_version: 1.3.5
|
54
|
+
signing_key:
|
55
|
+
specification_version: 3
|
56
|
+
summary: methodhash
|
57
|
+
test_files: []
|
58
|
+
|