methodhash 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|