hashery 1.0.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/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
|
+
|