hashery 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +15 -0
- data/LICENSE +23 -0
- data/README.rdoc +47 -0
- data/ROADMAP.rdoc +13 -0
- data/lib/hashery.rb +13 -0
- data/lib/hashery/castinghash.rb +171 -0
- data/lib/hashery/dictionary.rb +430 -0
- data/lib/hashery/lruhash.rb +274 -0
- data/lib/hashery/memoizer.rb +64 -0
- data/lib/hashery/opencascade.rb +82 -0
- data/lib/hashery/openhash.rb +65 -0
- data/lib/hashery/openobject.rb +279 -0
- data/lib/hashery/orderedhash.rb +417 -0
- data/lib/hashery/ostructable.rb +209 -0
- data/lib/hashery/queryhash.rb +35 -0
- data/lib/hashery/stash.rb +181 -0
- data/lib/hashery/statichash.rb +48 -0
- data/meta/authors +3 -0
- data/meta/created +1 -0
- data/meta/description +5 -0
- data/meta/homepage +1 -0
- data/meta/license +1 -0
- data/meta/name +1 -0
- data/meta/release +1 -0
- data/meta/repository +1 -0
- data/meta/suite +1 -0
- data/meta/summary +1 -0
- data/meta/version +1 -0
- data/test/case_dictionary.rb +142 -0
- data/test/case_opencascade.rb +68 -0
- data/test/case_openhash.rb +18 -0
- data/test/case_openobject.rb +130 -0
- data/test/case_stash.rb +131 -0
- metadata +102 -0
@@ -0,0 +1,209 @@
|
|
1
|
+
#--
|
2
|
+
# OpenStructable
|
3
|
+
#
|
4
|
+
# Copyright (c) 2005 Thomas Sawyer
|
5
|
+
#
|
6
|
+
# Ruby License
|
7
|
+
#
|
8
|
+
# This module is free software. You may use, modify, and/or redistribute this
|
9
|
+
# software under the same terms as Ruby.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
12
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
13
|
+
# FOR A PARTICULAR PURPOSE.
|
14
|
+
#
|
15
|
+
# ==========================================================================
|
16
|
+
# Revision History ::
|
17
|
+
# --------------------------------------------------------------------------
|
18
|
+
# 05.04.28 Trans
|
19
|
+
# - Minor modifications to documentation.
|
20
|
+
# ==========================================================================
|
21
|
+
#
|
22
|
+
# TODO
|
23
|
+
# Keep this uptodate with ostruct.rb.
|
24
|
+
#
|
25
|
+
# TODO Ask Matz
|
26
|
+
# See if Matz will accept it into core so we don't have to anymore.
|
27
|
+
#
|
28
|
+
# TODO Marshalling
|
29
|
+
# As with OpenStruct, marshalling is problematic at the moment.
|
30
|
+
#
|
31
|
+
#++
|
32
|
+
|
33
|
+
# OpensStructable is a mixin module which can provide OpenStruct behavior to
|
34
|
+
# any class or object. OpenStructable allows extention of data objects
|
35
|
+
# with arbitrary attributes.
|
36
|
+
#
|
37
|
+
# == Usage
|
38
|
+
#
|
39
|
+
# require 'ostructable'
|
40
|
+
#
|
41
|
+
# class Record
|
42
|
+
# include OpenStructable
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# record = Record.new
|
46
|
+
# record.name = "John Smith"
|
47
|
+
# record.age = 70
|
48
|
+
# record.pension = 300
|
49
|
+
#
|
50
|
+
# puts record.name # -> "John Smith"
|
51
|
+
# puts record.address # -> nil
|
52
|
+
#
|
53
|
+
# == Author(s)
|
54
|
+
#
|
55
|
+
# * Thomas Sawyer
|
56
|
+
# * Yukihiro Matsumoto
|
57
|
+
# * Gavin Sinclair (Documentation)
|
58
|
+
#
|
59
|
+
module OpenStructable
|
60
|
+
|
61
|
+
def initialize(hash=nil)
|
62
|
+
@__table__ = {}
|
63
|
+
if hash
|
64
|
+
for k,v in hash
|
65
|
+
@__table__[k.to_sym] = v
|
66
|
+
new_ostruct_member(k)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# duplicate an OpenStruct object members.
|
72
|
+
def initialize_copy(orig)
|
73
|
+
super
|
74
|
+
@__table__ = @__table__.dup
|
75
|
+
end
|
76
|
+
|
77
|
+
def marshal_dump
|
78
|
+
@table
|
79
|
+
end
|
80
|
+
def marshal_load(x)
|
81
|
+
@table = x
|
82
|
+
@table.each_key{|key| new_ostruct_member(key)}
|
83
|
+
end
|
84
|
+
|
85
|
+
def new_ostruct_member(name)
|
86
|
+
unless self.respond_to?(name)
|
87
|
+
self.instance_eval %{
|
88
|
+
def #{name}; @__table__[:#{name}]; end
|
89
|
+
def #{name}=(x); @__table__[:#{name}] = x; end
|
90
|
+
}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Generate additional attributes and values.
|
96
|
+
#
|
97
|
+
def update(hash)
|
98
|
+
@__table__ ||= {}
|
99
|
+
if hash
|
100
|
+
for k,v in hash
|
101
|
+
@__table__[k.to_sym] = v
|
102
|
+
new_ostruct_member(k)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def method_missing(mid, *args) # :nodoc:
|
108
|
+
mname = mid.to_s
|
109
|
+
len = args.length
|
110
|
+
if mname =~ /=$/
|
111
|
+
if len != 1
|
112
|
+
raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
|
113
|
+
end
|
114
|
+
if self.frozen?
|
115
|
+
raise TypeError, "can't modify frozen #{self.class}", caller(1)
|
116
|
+
end
|
117
|
+
mname.chop!
|
118
|
+
@__table__ ||= {}
|
119
|
+
@__table__[mname.intern] = args[0]
|
120
|
+
self.new_ostruct_member(mname)
|
121
|
+
elsif len == 0
|
122
|
+
@__table__ ||= {}
|
123
|
+
@__table__[mid]
|
124
|
+
else
|
125
|
+
raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Remove the named field from the object.
|
131
|
+
#
|
132
|
+
def delete_field(name)
|
133
|
+
@__table__ ||= {}
|
134
|
+
@__table__.delete name.to_sym
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Returns a string containing a detailed summary of the keys and values.
|
139
|
+
#
|
140
|
+
def inspect
|
141
|
+
str = "<#{self.class}"
|
142
|
+
for k,v in (@__table__ ||= {})
|
143
|
+
str << " #{k}=#{v.inspect}"
|
144
|
+
end
|
145
|
+
str << ">"
|
146
|
+
end
|
147
|
+
|
148
|
+
def __table__ # :nodoc:
|
149
|
+
@__table__ ||= {}
|
150
|
+
end
|
151
|
+
protected :__table__
|
152
|
+
|
153
|
+
# Compare this object and +other+ for equality.
|
154
|
+
def ==(other)
|
155
|
+
return false unless(other.kind_of?(OpenStruct))
|
156
|
+
return @__table__ == other.table
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
=begin
|
162
|
+
#
|
163
|
+
# It is possibe to implement OpenStruct itself with
|
164
|
+
# this OpenStructable module as follows:
|
165
|
+
#
|
166
|
+
class OpenStruct
|
167
|
+
include OpenStructable
|
168
|
+
end
|
169
|
+
=end
|
170
|
+
|
171
|
+
|
172
|
+
|
173
|
+
# _____ _
|
174
|
+
# |_ _|__ ___| |_
|
175
|
+
# | |/ _ \/ __| __|
|
176
|
+
# | | __/\__ \ |_
|
177
|
+
# |_|\___||___/\__|
|
178
|
+
#
|
179
|
+
|
180
|
+
=begin testing
|
181
|
+
|
182
|
+
require 'test/unit'
|
183
|
+
|
184
|
+
# fixture
|
185
|
+
|
186
|
+
class Record
|
187
|
+
include OpenStructable
|
188
|
+
end
|
189
|
+
|
190
|
+
# test
|
191
|
+
|
192
|
+
class TC_OpenStructable < Test::Unit::TestCase
|
193
|
+
|
194
|
+
def test_record
|
195
|
+
record = nil
|
196
|
+
assert_nothing_raised {
|
197
|
+
record = Record.new
|
198
|
+
record.name = "John Smith"
|
199
|
+
record.age = 70
|
200
|
+
record.pension = 300
|
201
|
+
}
|
202
|
+
assert_equal( "John Smith", record.name )
|
203
|
+
assert_equal( 70, record.age )
|
204
|
+
assert_equal( nil, record.address )
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
=end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# = QueryHash
|
2
|
+
#
|
3
|
+
# QueryHash is a similar to OpenHash. Like OpenHash,
|
4
|
+
# entries can be assigned via setter methods, but
|
5
|
+
# entries can only be looked up via query methods
|
6
|
+
# (i.e. methods ending in a ?-mark), hence the name
|
7
|
+
# of this class.
|
8
|
+
#
|
9
|
+
# A QueryHash is not quite as elegant as an OpenHash
|
10
|
+
# in that reader methods must end in ?-marks, but
|
11
|
+
# it remains fully compatible with Hash regardless
|
12
|
+
# of it's settings.
|
13
|
+
|
14
|
+
class QueryHash < Hash
|
15
|
+
|
16
|
+
def to_h
|
17
|
+
dup
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash
|
21
|
+
dup
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(s,*a,&b)
|
25
|
+
case s.to_s
|
26
|
+
when /\=$/
|
27
|
+
self[s.to_s.chomp('=').to_sym] = a[0]
|
28
|
+
when /\?$/
|
29
|
+
self[s.to_s.chomp('?').to_sym]
|
30
|
+
else
|
31
|
+
super(s,*a,&b)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
# Stash is just like Hash, except that all keys are
|
2
|
+
# converted to Strings.
|
3
|
+
#
|
4
|
+
# Note this doesn't yet handle default_proc.
|
5
|
+
|
6
|
+
class Stash < Hash
|
7
|
+
|
8
|
+
#
|
9
|
+
def self.[](*hash)
|
10
|
+
s = new
|
11
|
+
super(*hash).each{ |k,v| s[k] = v }
|
12
|
+
s
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
def [](key)
|
17
|
+
super(convert_key(key))
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
def []=(key,value)
|
22
|
+
super(convert_key(key), value)
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
def <<(other)
|
27
|
+
case other
|
28
|
+
when Hash
|
29
|
+
super(other.rekey{ |key| convert_key(key) })
|
30
|
+
when Array
|
31
|
+
self[other[0]] = other[1]
|
32
|
+
else
|
33
|
+
raise ArgumentError
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
def fetch(key)
|
39
|
+
super(convert_key(key))
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
def store(key, value)
|
44
|
+
super(convert_key(key), value)
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
def key?(key)
|
49
|
+
super(convert_key(key))
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
def has_key?(key)
|
54
|
+
super(convert_key(key))
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
def include?(key)
|
59
|
+
super(convert_key(key))
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
def member?(key)
|
64
|
+
super(convert_key(key))
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# Synonym for Hash#rekey, but modifies the receiver in place (and returns it).
|
69
|
+
#
|
70
|
+
# foo = { :name=>'Gavin', :wife=>:Lisa }.to_stash
|
71
|
+
# foo.rekey!{ |k| k.upcase } #=> { "NAME"=>"Gavin", "WIFE"=>:Lisa }
|
72
|
+
# foo.inspect #=> { "NAME"=>"Gavin", "WIFE"=>:Lisa }
|
73
|
+
#
|
74
|
+
def rekey!(*args, &block)
|
75
|
+
# for backward comptability (TODO: DEPRECATE?).
|
76
|
+
block = args.pop.to_sym.to_proc if args.size == 1
|
77
|
+
if args.empty?
|
78
|
+
block = lambda{|k| k} unless block
|
79
|
+
keys.each do |k|
|
80
|
+
nk = block[k]
|
81
|
+
self[nk.to_s]=delete(k) #if nk
|
82
|
+
end
|
83
|
+
else
|
84
|
+
raise ArgumentError, "3 for 2" if block
|
85
|
+
to, from = *args
|
86
|
+
self[to] = delete(from) if has_key?(from)
|
87
|
+
end
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
def rekey(*args, &block)
|
93
|
+
dup.rekey!(*args, &block)
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
def delete(key)
|
98
|
+
super(convert_key(key))
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
def update(other)
|
103
|
+
super(other.rekey{ |key| convert_key(key) })
|
104
|
+
end
|
105
|
+
|
106
|
+
# Same as #update.
|
107
|
+
def merge!(other)
|
108
|
+
super(other.rekey{ |key| convert_key(key) })
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
def merge(other)
|
113
|
+
super(other.rekey{ |key| convert_key(key) })
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
def replace(other)
|
118
|
+
super(other.rekey{ |key| convert_key(key) })
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
def values_at(*keys)
|
123
|
+
super(*keys.map{ |key| convert_key(key) })
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
def to_hash
|
128
|
+
h = {}
|
129
|
+
each{ |k,v| h[k] = v }
|
130
|
+
h
|
131
|
+
end
|
132
|
+
|
133
|
+
alias_method :to_h, :to_hash
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def convert_key(key)
|
138
|
+
key.to_s
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
class Hash
|
144
|
+
|
145
|
+
# Convert a Hash to a Stash object.
|
146
|
+
def to_stash
|
147
|
+
Stash[self]
|
148
|
+
end
|
149
|
+
|
150
|
+
# Synonym for Hash#rekey, but modifies the receiver in place (and returns it).
|
151
|
+
#
|
152
|
+
# foo = { :name=>'Gavin', :wife=>:Lisa }
|
153
|
+
# foo.rekey!{ |k| k.to_s } #=> { "name"=>"Gavin", "wife"=>:Lisa }
|
154
|
+
# foo.inspect #=> { "name"=>"Gavin", "wife"=>:Lisa }
|
155
|
+
#
|
156
|
+
# This method comes from Ruby Facets.
|
157
|
+
|
158
|
+
def rekey!(*args, &block)
|
159
|
+
# for backward comptability (TODO: DEPRECATE).
|
160
|
+
block = args.pop.to_sym.to_proc if args.size == 1
|
161
|
+
if args.empty?
|
162
|
+
block = lambda{|k| k.to_sym} unless block
|
163
|
+
keys.each do |k|
|
164
|
+
nk = block[k]
|
165
|
+
self[nk]=delete(k) if nk
|
166
|
+
end
|
167
|
+
else
|
168
|
+
raise ArgumentError, "3 for 2" if block
|
169
|
+
to, from = *args
|
170
|
+
self[to] = self.delete(from) if self.has_key?(from)
|
171
|
+
end
|
172
|
+
self
|
173
|
+
end
|
174
|
+
|
175
|
+
# Non-inplace #rekey! method.
|
176
|
+
def rekey(*args, &block)
|
177
|
+
dup.rekey!(*args, &block)
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# = StaticHash
|
2
|
+
#
|
3
|
+
# A Hash object which raises an error if any
|
4
|
+
# previously-defined key attempts to be set again.
|
5
|
+
#
|
6
|
+
# == Synopsis
|
7
|
+
#
|
8
|
+
# foo = Hash::Static.new
|
9
|
+
# foo['name'] = 'Tom' #=> 'Tom'
|
10
|
+
# foo['age'] = 30 #=> 30
|
11
|
+
# foo['name'] = 'Bob'
|
12
|
+
#
|
13
|
+
# _produces_
|
14
|
+
#
|
15
|
+
# ArgumentError: Duplicate key for StaticHash -- 'name'
|
16
|
+
#
|
17
|
+
# == Credit
|
18
|
+
#
|
19
|
+
# StaticHash has it's orgins in Gavin Kistner's WriteOnceHash
|
20
|
+
# class found in his +basiclibrary.rb+ script.
|
21
|
+
|
22
|
+
class StaticHash < Hash
|
23
|
+
|
24
|
+
# Set a value for a key. Raises an error if that key already
|
25
|
+
# exists with a different value.
|
26
|
+
|
27
|
+
def []=(key, value)
|
28
|
+
if key?(key) && self[key] != value
|
29
|
+
raise ArgumentError, "Duplicate key for StaticHash -- #{key.inspect}"
|
30
|
+
end
|
31
|
+
super(key, value)
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
def update(hash)
|
36
|
+
dups = (keys | hash.keys)
|
37
|
+
if dups.empty?
|
38
|
+
super(hash)
|
39
|
+
else
|
40
|
+
raise ArgumentError, "Duplicate key for StaticHash -- #{dups.inspect}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
alias_method :merge!, :update
|
46
|
+
|
47
|
+
end
|
48
|
+
|