hashery 1.3.0 → 1.4.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/.ruby +98 -0
- data/HISTORY.rdoc +17 -4
- data/NOTICE +11 -0
- data/lib/hashery.rb +1 -1
- data/lib/hashery.yml +98 -0
- data/lib/hashery/basic_struct.rb +1 -0
- data/lib/hashery/basicobject.rb +74 -0
- data/lib/hashery/basicstruct.rb +280 -0
- data/lib/hashery/open_object.rb +1 -1
- data/lib/hashery/openobject.rb +1 -279
- data/lib/hashery/ostructable.rb +48 -71
- data/qed/01_openhash.rdoc +57 -0
- data/qed/02_queryhash.rdoc +21 -0
- data/qed/03_castinghash.rdoc +13 -0
- data/qed/04_statichash.rdoc +22 -0
- data/qed/05_association.rdoc +59 -0
- data/qed/06_opencascade.rdoc +58 -0
- data/qed/07_fuzzyhash.rdoc +141 -0
- data/qed/08_properyhash.rdoc +38 -0
- data/qed/09_ostructable.rdoc +56 -0
- data/qed/applique/ae.rb +1 -0
- metadata +53 -9
- data/PROFILE +0 -35
- data/VERSION +0 -3
data/lib/hashery/open_object.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
raise 'Use BasicStruct in place of OpenObject for future versions.'
|
data/lib/hashery/openobject.rb
CHANGED
@@ -1,279 +1 @@
|
|
1
|
-
|
2
|
-
#require 'facets/hash/to_h'
|
3
|
-
#require 'facets/kernel/object_class'
|
4
|
-
#require 'facets/kernel/object_hexid'
|
5
|
-
|
6
|
-
# = OpenObject
|
7
|
-
#
|
8
|
-
# OpenObject is very similar to Ruby's own OpenStruct, but it offers some
|
9
|
-
# advantages. With OpenStruct, slots with the same name as predefined
|
10
|
-
# Object methods cannot be used. With OpenObject, almost any slot can be
|
11
|
-
# defined. OpenObject is a subclass of BasicObject to ensure all method
|
12
|
-
# slots, except those that are absolutely essential, are open for use.
|
13
|
-
#
|
14
|
-
#--
|
15
|
-
# If you wish to pass an OpenObject to a routine that normal takes a Hash,
|
16
|
-
# but are uncertain it can handle the distictions properly you can convert
|
17
|
-
# easily to a Hash using #as_hash! and the result will automatically be
|
18
|
-
# converted back to an OpenObject on return.
|
19
|
-
#
|
20
|
-
# o = OpenObject.new(:a=>1,:b=>2)
|
21
|
-
# o.as_hash!{ |h| h.update(:a=>6) }
|
22
|
-
# o #=> #<OpenObject {:a=>6,:b=>2}>
|
23
|
-
#++
|
24
|
-
#
|
25
|
-
# Unlike a Hash, all OpenObject's keys are symbols and all keys are converted
|
26
|
-
# to such using #to_sym on the fly.
|
27
|
-
|
28
|
-
class OpenObject < BasicObject
|
29
|
-
|
30
|
-
#PUBLIC_METHODS = /(^__|^instance_|^object_|^\W|^as$|^send$|^class$|\?$)/
|
31
|
-
#protected(*public_instance_methods.select{ |m| m !~ PUBLIC_METHODS })
|
32
|
-
|
33
|
-
def self.[](hash=nil)
|
34
|
-
new(hash)
|
35
|
-
end
|
36
|
-
|
37
|
-
# Inititalizer for OpenObject is slightly different than that of Hash.
|
38
|
-
# It does not take a default parameter, but an initial priming Hash,
|
39
|
-
# like OpenStruct. The initializer can still take a default block
|
40
|
-
# however. To set the default value use <code>#default!(value)</code>.
|
41
|
-
#
|
42
|
-
# OpenObject.new(:a=>1).default!(0)
|
43
|
-
#
|
44
|
-
def initialize(hash=nil, &yld)
|
45
|
-
@hash = Hash.new(&yld)
|
46
|
-
if hash
|
47
|
-
hash.each{ |k,v| store(k,v) }
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
#
|
52
|
-
def initialize_copy( orig )
|
53
|
-
orig.each{ |k,v| store(k,v) }
|
54
|
-
end
|
55
|
-
|
56
|
-
# Object inspection.
|
57
|
-
# TODO: Need to get __class__ and __id__ in hex form.
|
58
|
-
def inspect
|
59
|
-
#@hash.inspect
|
60
|
-
hexid = __id__
|
61
|
-
klass = "OpenObject" # __class__
|
62
|
-
"#<#{klass}:#{hexid} #{@hash.inspect}>"
|
63
|
-
end
|
64
|
-
|
65
|
-
# Convert to an associative array.
|
66
|
-
def to_a
|
67
|
-
@hash.to_a
|
68
|
-
end
|
69
|
-
|
70
|
-
#
|
71
|
-
def to_h
|
72
|
-
@hash.dup
|
73
|
-
end
|
74
|
-
|
75
|
-
#
|
76
|
-
def to_hash
|
77
|
-
@hash.dup
|
78
|
-
end
|
79
|
-
|
80
|
-
#
|
81
|
-
def to_openobject
|
82
|
-
self
|
83
|
-
end
|
84
|
-
|
85
|
-
# Convert to an assignment procedure.
|
86
|
-
def to_proc(response=false)
|
87
|
-
hash = @hash
|
88
|
-
if response
|
89
|
-
lambda do |o|
|
90
|
-
hash.each do |k,v|
|
91
|
-
o.__send__("#{k}=", v) rescue nil
|
92
|
-
end
|
93
|
-
end
|
94
|
-
else
|
95
|
-
lambda do |o|
|
96
|
-
hash.each{ |k,v| o.__send__("#{k}=", v) }
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
# NOT SURE ABOUT THIS
|
102
|
-
def as_hash
|
103
|
-
@hash
|
104
|
-
end
|
105
|
-
|
106
|
-
# Is a given +key+ defined?
|
107
|
-
def key?(key)
|
108
|
-
@hash.key?(key.to_sym)
|
109
|
-
end
|
110
|
-
|
111
|
-
#
|
112
|
-
def is_a?(klass)
|
113
|
-
return true if klass == Hash # TODO: Is this wise? How to fake a subclass?
|
114
|
-
return true if klass == OpenObject
|
115
|
-
false
|
116
|
-
end
|
117
|
-
|
118
|
-
# Iterate over each key-value pair.
|
119
|
-
def each(&yld)
|
120
|
-
@hash.each(&yld)
|
121
|
-
end
|
122
|
-
|
123
|
-
# Set the default value.
|
124
|
-
def default!(default)
|
125
|
-
@hash.default = default
|
126
|
-
end
|
127
|
-
|
128
|
-
# Check equality.
|
129
|
-
def ==( other )
|
130
|
-
case other
|
131
|
-
when OpenObject
|
132
|
-
@hash == other.as_hash
|
133
|
-
when Hash
|
134
|
-
@hash == other
|
135
|
-
else
|
136
|
-
if other.respond_to?(:to_hash)
|
137
|
-
@hash == other.to_hash
|
138
|
-
else
|
139
|
-
false
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
#
|
145
|
-
def eql?( other )
|
146
|
-
case other
|
147
|
-
when OpenObject
|
148
|
-
@hash.eql?(other.as_hash)
|
149
|
-
else
|
150
|
-
false
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
#
|
155
|
-
def <<(x)
|
156
|
-
case x
|
157
|
-
when Hash
|
158
|
-
@hash.update(x)
|
159
|
-
when Array
|
160
|
-
x.each_slice(2) do |(k,v)|
|
161
|
-
@hash[k] = v
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
#
|
167
|
-
def []=(key, value)
|
168
|
-
@hash[key.to_sym] = value
|
169
|
-
end
|
170
|
-
|
171
|
-
#
|
172
|
-
def [](key)
|
173
|
-
@hash[key.to_sym]
|
174
|
-
end
|
175
|
-
|
176
|
-
#
|
177
|
-
def merge!(other)
|
178
|
-
OpenObject.new(@hash.merge!(other))
|
179
|
-
end
|
180
|
-
|
181
|
-
#
|
182
|
-
def update!(other)
|
183
|
-
@hash.update(other)
|
184
|
-
self
|
185
|
-
end
|
186
|
-
|
187
|
-
protected
|
188
|
-
|
189
|
-
def store(k, v)
|
190
|
-
@hash.store(k.to_sym, v)
|
191
|
-
end
|
192
|
-
|
193
|
-
def fetch(k, *d, &b)
|
194
|
-
@hash.fetch(k.to_sym, *d, &b)
|
195
|
-
end
|
196
|
-
|
197
|
-
#def as_hash!
|
198
|
-
# Functor.new do |op,*a,&b|
|
199
|
-
# result = @hash.__send__(op,*a,&b)
|
200
|
-
# case result
|
201
|
-
# when Hash
|
202
|
-
# OpenObject.new(result)
|
203
|
-
# else
|
204
|
-
# result
|
205
|
-
# end
|
206
|
-
# end
|
207
|
-
#end
|
208
|
-
|
209
|
-
#def define_slot(key, value=nil)
|
210
|
-
# @hash[key.to_sym] = value
|
211
|
-
#end
|
212
|
-
|
213
|
-
#def protect_slot( key )
|
214
|
-
# (class << self; self; end).class_eval {
|
215
|
-
# protected key rescue nil
|
216
|
-
# }
|
217
|
-
#end
|
218
|
-
|
219
|
-
def method_missing(sym, *args, &blk)
|
220
|
-
type = sym.to_s[-1,1]
|
221
|
-
key = sym.to_s.sub(/[=?!]$/,'').to_sym
|
222
|
-
case type
|
223
|
-
when '='
|
224
|
-
store(key, args[0])
|
225
|
-
when '!'
|
226
|
-
@hash.__send__(key, *args, &blk)
|
227
|
-
# if key?(key)
|
228
|
-
# fetch(key)
|
229
|
-
# else
|
230
|
-
# store(key, OpenObject.new)
|
231
|
-
# end
|
232
|
-
when '?'
|
233
|
-
fetch(key)
|
234
|
-
else
|
235
|
-
fetch(key)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
end
|
240
|
-
|
241
|
-
# Core Extensions
|
242
|
-
|
243
|
-
class Hash
|
244
|
-
# Convert a Hash into an OpenObject.
|
245
|
-
def to_openobject
|
246
|
-
OpenObject[self]
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
=begin
|
251
|
-
class NilClass
|
252
|
-
# Nil converts to an empty OpenObject.
|
253
|
-
def to_openobject
|
254
|
-
OpenObject.new
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
class Proc
|
259
|
-
# Translates a Proc into an OpenObject. By droping an OpenObject into
|
260
|
-
# the Proc, the resulting assignments incured as the procedure is
|
261
|
-
# evaluated produce the OpenObject. This technique is simlar to that
|
262
|
-
# of MethodProbe.
|
263
|
-
#
|
264
|
-
# p = lambda { |x|
|
265
|
-
# x.word = "Hello"
|
266
|
-
# }
|
267
|
-
# o = p.to_openobject
|
268
|
-
# o.word #=> "Hello"
|
269
|
-
#
|
270
|
-
# NOTE The Proc must have an arity of one --no more and no less.
|
271
|
-
def to_openobject
|
272
|
-
raise ArgumentError, 'bad arity for converting Proc to openobject' if arity != 1
|
273
|
-
o = OpenObject.new
|
274
|
-
self.call( o )
|
275
|
-
o
|
276
|
-
end
|
277
|
-
end
|
278
|
-
=end
|
279
|
-
|
1
|
+
raise 'Use BasicStruct in place of OpenObject for future versions.'
|
data/lib/hashery/ostructable.rb
CHANGED
@@ -3,15 +3,6 @@
|
|
3
3
|
#
|
4
4
|
# Copyright (c) 2005 Thomas Sawyer
|
5
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
6
|
# ==========================================================================
|
16
7
|
# Revision History ::
|
17
8
|
# --------------------------------------------------------------------------
|
@@ -58,35 +49,53 @@
|
|
58
49
|
#
|
59
50
|
module OpenStructable
|
60
51
|
|
52
|
+
def self.include(base)
|
53
|
+
if Hash > base
|
54
|
+
base.module_eval do
|
55
|
+
define_method(:__table__) do
|
56
|
+
self
|
57
|
+
end
|
58
|
+
end
|
59
|
+
protected :__table__
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
61
63
|
def initialize(hash=nil)
|
62
64
|
@__table__ = {}
|
63
65
|
if hash
|
64
66
|
for k,v in hash
|
65
|
-
|
67
|
+
__table__[k.to_sym] = v
|
66
68
|
new_ostruct_member(k)
|
67
69
|
end
|
68
70
|
end
|
69
71
|
end
|
70
72
|
|
73
|
+
#
|
74
|
+
def __table__
|
75
|
+
@__table__ ||= {}
|
76
|
+
end
|
77
|
+
protected :__table__
|
78
|
+
|
71
79
|
# duplicate an OpenStruct object members.
|
72
80
|
def initialize_copy(orig)
|
73
81
|
super
|
74
|
-
|
82
|
+
__table__.replace(__table__.dup)
|
75
83
|
end
|
76
84
|
|
77
85
|
def marshal_dump
|
78
|
-
|
86
|
+
__table__
|
79
87
|
end
|
80
|
-
|
81
|
-
|
82
|
-
|
88
|
+
|
89
|
+
def marshal_load(hash)
|
90
|
+
__table__.replace(hash)
|
91
|
+
__table__.each_key{|key| new_ostruct_member(key)}
|
83
92
|
end
|
84
93
|
|
85
94
|
def new_ostruct_member(name)
|
86
95
|
unless self.respond_to?(name)
|
87
96
|
self.instance_eval %{
|
88
|
-
def #{name};
|
89
|
-
def #{name}=(x);
|
97
|
+
def #{name}; __table__[:#{name}]; end
|
98
|
+
def #{name}=(x); __table__[:#{name}] = x; end
|
90
99
|
}
|
91
100
|
end
|
92
101
|
end
|
@@ -95,15 +104,16 @@ module OpenStructable
|
|
95
104
|
# Generate additional attributes and values.
|
96
105
|
#
|
97
106
|
def update(hash)
|
98
|
-
|
107
|
+
#__table__ ||= {}
|
99
108
|
if hash
|
100
109
|
for k,v in hash
|
101
|
-
|
110
|
+
__table__[k.to_sym] = v
|
102
111
|
new_ostruct_member(k)
|
103
112
|
end
|
104
113
|
end
|
105
114
|
end
|
106
115
|
|
116
|
+
#
|
107
117
|
def method_missing(mid, *args) # :nodoc:
|
108
118
|
mname = mid.to_s
|
109
119
|
len = args.length
|
@@ -115,12 +125,12 @@ module OpenStructable
|
|
115
125
|
raise TypeError, "can't modify frozen #{self.class}", caller(1)
|
116
126
|
end
|
117
127
|
mname.chop!
|
118
|
-
|
119
|
-
|
128
|
+
#@__table__ ||= {}
|
129
|
+
__table__[mname.intern] = args[0]
|
120
130
|
self.new_ostruct_member(mname)
|
121
131
|
elsif len == 0
|
122
|
-
|
123
|
-
|
132
|
+
#@__table__ ||= {}
|
133
|
+
__table__[mid]
|
124
134
|
else
|
125
135
|
raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
|
126
136
|
end
|
@@ -130,8 +140,8 @@ module OpenStructable
|
|
130
140
|
# Remove the named field from the object.
|
131
141
|
#
|
132
142
|
def delete_field(name)
|
133
|
-
|
134
|
-
|
143
|
+
#@__table__ ||= {}
|
144
|
+
__table__.delete(name.to_sym)
|
135
145
|
end
|
136
146
|
|
137
147
|
#
|
@@ -145,15 +155,21 @@ module OpenStructable
|
|
145
155
|
str << ">"
|
146
156
|
end
|
147
157
|
|
148
|
-
def __table__ # :nodoc:
|
149
|
-
@__table__ ||= {}
|
150
|
-
end
|
151
|
-
protected :__table__
|
152
|
-
|
153
158
|
# Compare this object and +other+ for equality.
|
159
|
+
#--
|
160
|
+
# TODO: OpenStruct could be compared too, but only if it is loaded. How?
|
161
|
+
#++
|
154
162
|
def ==(other)
|
155
|
-
|
156
|
-
|
163
|
+
case other
|
164
|
+
when OpenStructable
|
165
|
+
__table__ == other.__table__
|
166
|
+
#when OpenStruct
|
167
|
+
# __table__ == other.__table__
|
168
|
+
when Hash
|
169
|
+
__table__ == other
|
170
|
+
else
|
171
|
+
false
|
172
|
+
end
|
157
173
|
end
|
158
174
|
|
159
175
|
end
|
@@ -168,42 +184,3 @@ class OpenStruct
|
|
168
184
|
end
|
169
185
|
=end
|
170
186
|
|
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
|