hashery 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby +57 -92
- data/.yardopts +8 -0
- data/COPYING.rdoc +45 -0
- data/HISTORY.rdoc +18 -0
- data/QED.rdoc +1 -0
- data/README.rdoc +42 -16
- data/lib/hashery.rb +16 -9
- data/lib/hashery.yml +57 -92
- data/lib/hashery/association.rb +3 -1
- data/lib/hashery/basic_object.rb +74 -0
- data/lib/hashery/basic_struct.rb +288 -1
- data/lib/hashery/basicobject.rb +1 -74
- data/lib/hashery/basicstruct.rb +1 -280
- data/lib/hashery/casting_hash.rb +171 -1
- data/lib/hashery/castinghash.rb +1 -171
- data/lib/hashery/core_ext.rb +82 -0
- data/lib/hashery/dictionary.rb +3 -0
- data/lib/hashery/fuzzy_hash.rb +154 -1
- data/lib/hashery/fuzzyhash.rb +1 -154
- data/lib/hashery/ini.rb +3 -2
- data/lib/hashery/key_hash.rb +186 -0
- data/lib/hashery/keyhash.rb +1 -0
- data/lib/hashery/linked_list.rb +195 -1
- data/lib/hashery/linkedlist.rb +1 -195
- data/lib/hashery/lru_hash.rb +273 -1
- data/lib/hashery/lruhash.rb +1 -273
- data/lib/hashery/open_cascade.rb +99 -1
- data/lib/hashery/open_hash.rb +77 -1
- data/lib/hashery/opencascade.rb +1 -99
- data/lib/hashery/openhash.rb +1 -77
- data/lib/hashery/ordered_hash.rb +168 -1
- data/lib/hashery/orderedhash.rb +1 -167
- data/lib/hashery/property_hash.rb +97 -1
- data/lib/hashery/propertyhash.rb +1 -97
- data/lib/hashery/query_hash.rb +35 -1
- data/lib/hashery/queryhash.rb +1 -35
- data/lib/hashery/stash.rb +3 -174
- data/lib/hashery/static_hash.rb +48 -1
- data/lib/hashery/statichash.rb +1 -48
- data/qed/06_opencascade.rdoc +12 -12
- data/test/case_association.rb +29 -15
- data/test/case_basicstruct.rb +192 -0
- data/test/case_dictionary.rb +149 -109
- data/test/case_keyhash.rb +175 -0
- data/test/case_opencascade.rb +89 -43
- data/test/case_openhash.rb +15 -11
- metadata +85 -78
- data/LICENSE +0 -206
- data/NOTICE +0 -11
- data/lib/hashery/sparse_array.rb +0 -1
- data/lib/hashery/sparsearray.rb +0 -577
- data/test/case_openobject.rb +0 -130
- data/test/case_sparsearray.rb +0 -316
- data/test/case_stash.rb +0 -131
data/lib/hashery/open_cascade.rb
CHANGED
@@ -1 +1,99 @@
|
|
1
|
-
require 'hashery/
|
1
|
+
require 'hashery/open_hash'
|
2
|
+
#require 'facets/nullclass'
|
3
|
+
|
4
|
+
# = OpenCascade
|
5
|
+
#
|
6
|
+
# OpenCascade is subclass of OpenHash. It differs in a few
|
7
|
+
# significant ways.
|
8
|
+
#
|
9
|
+
# The main reason this class is labeled "cascade", every internal
|
10
|
+
# Hash is transformed into an OpenCascade dynamically upon access.
|
11
|
+
# This makes it easy to create "cascading" references.
|
12
|
+
#
|
13
|
+
# h = { :x => { :y => { :z => 1 } } }
|
14
|
+
# c = OpenCascade[h]
|
15
|
+
# c.x.y.z #=> 1
|
16
|
+
#
|
17
|
+
# As soon as you access a node it automatically becomes an OpenCascade.
|
18
|
+
#
|
19
|
+
# c = OpenCascade.new #=> #<OpenCascade:0x7fac3680ccf0 {}>
|
20
|
+
# c.r #=> #<OpenCascade:0x7fac368084c0 {}>
|
21
|
+
# c.a.b #=> #<OpenCascade:0x7fac3680a4f0 {}>
|
22
|
+
#
|
23
|
+
# But if you set a node, then that will be it's value.
|
24
|
+
#
|
25
|
+
# c.a.b = 4 #=> 4
|
26
|
+
#
|
27
|
+
# To query a node without causing the auto-creation of an OpenCasade
|
28
|
+
# object, use the ?-mark.
|
29
|
+
#
|
30
|
+
# c.a.z? #=> nil
|
31
|
+
#
|
32
|
+
# OpenCascade also transforms Hashes within Arrays.
|
33
|
+
#
|
34
|
+
# h = { :x=>[ {:a=>1}, {:a=>2} ], :y=>1 }
|
35
|
+
# c = OpenCascade[h]
|
36
|
+
# c.x.first.a.assert == 1
|
37
|
+
# c.x.last.a.assert == 2
|
38
|
+
#
|
39
|
+
# Finally, you can set a node and get the reciever back using
|
40
|
+
# the !-mark.
|
41
|
+
#
|
42
|
+
# c = OpenCascade.new #=> #<OpenCascade:0x7fac3680ccf0 {}>
|
43
|
+
# c.x!(4).y!(3) #=> #<OpenCascade:0x7fac3680ccf0 {:x=>4, :y=>3}>
|
44
|
+
#
|
45
|
+
#--
|
46
|
+
# Last, when an entry is not found, 'null' is returned rather then 'nil'.
|
47
|
+
# This allows for run-on entries withuot error. Eg.
|
48
|
+
#
|
49
|
+
# o = OpenCascade.new
|
50
|
+
# o.a.b.c #=> null
|
51
|
+
#
|
52
|
+
# Unfortuately this requires an explict test for null? in 'if' conditions.
|
53
|
+
#
|
54
|
+
# if o.a.b.c.null? # true if null
|
55
|
+
# if o.a.b.c.nil? # true if nil or null
|
56
|
+
# if o.a.b.c.not? # true if nil or null or false
|
57
|
+
#
|
58
|
+
# So be sure to take that into account.
|
59
|
+
#++
|
60
|
+
|
61
|
+
class OpenCascade < OpenHash
|
62
|
+
|
63
|
+
#
|
64
|
+
def method_missing(sym, *args, &blk)
|
65
|
+
type = sym.to_s[-1,1]
|
66
|
+
name = sym.to_s.gsub(/[=!?]$/, '').to_sym
|
67
|
+
case type
|
68
|
+
when '='
|
69
|
+
self[name] = args.first
|
70
|
+
when '!'
|
71
|
+
#@hash.__send__(name, *args, &blk)
|
72
|
+
__send__(name, *args, &blk)
|
73
|
+
when '?'
|
74
|
+
self[name]
|
75
|
+
else
|
76
|
+
if key?(name)
|
77
|
+
self[name] = transform_entry(self[name])
|
78
|
+
else
|
79
|
+
self[name] = OpenCascade.new #self.class.new
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
#
|
87
|
+
def transform_entry(entry)
|
88
|
+
case entry
|
89
|
+
when Hash
|
90
|
+
OpenCascade.new(entry) #self.class.new(val)
|
91
|
+
when Array
|
92
|
+
entry.map{ |e| transform_entry(e) }
|
93
|
+
else
|
94
|
+
entry
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
data/lib/hashery/open_hash.rb
CHANGED
@@ -1 +1,77 @@
|
|
1
|
-
|
1
|
+
# = OpenHash
|
2
|
+
#
|
3
|
+
# OpenHash is very similar to Ruby's own OpenStruct, but it offers some
|
4
|
+
# useful advantages in that it is a true Hash object.
|
5
|
+
#
|
6
|
+
# Because OpenHash is a subclass of Hash, it can do everything a Hash
|
7
|
+
# can *unless* a Hash method has been explicity exempted for use
|
8
|
+
# as an open read/writer via the #omit! method.
|
9
|
+
|
10
|
+
class OpenHash < Hash
|
11
|
+
|
12
|
+
# New OpenHash.
|
13
|
+
def initialize(data={})
|
14
|
+
super()
|
15
|
+
merge!(data)
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
def <<(x)
|
20
|
+
case x
|
21
|
+
when Hash
|
22
|
+
update(x)
|
23
|
+
when Array
|
24
|
+
x.each_slice(2) do |(k,v)|
|
25
|
+
self[k] = v
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
def respond_to?(name)
|
32
|
+
key?(name.to_sym) || super(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
def to_h
|
37
|
+
dup
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
def to_hash
|
42
|
+
dup
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
def inspect
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
# Omit specific Hash methods from slot protection.
|
51
|
+
def omit!(*methods)
|
52
|
+
methods.reject!{ |x| x.to_s =~ /^__/ }
|
53
|
+
(class << self; self; end).class_eval{ private *methods }
|
54
|
+
end
|
55
|
+
|
56
|
+
# Route get and set calls.
|
57
|
+
def method_missing(s,*a, &b)
|
58
|
+
type = s.to_s[-1,1]
|
59
|
+
name = s.to_s.sub(/[!?=]$/, '')
|
60
|
+
key = name.to_sym
|
61
|
+
case type
|
62
|
+
when '='
|
63
|
+
self[key] = a[0]
|
64
|
+
#when '!'
|
65
|
+
# self[s] = OpenHash.new
|
66
|
+
when '?'
|
67
|
+
key?(key)
|
68
|
+
else
|
69
|
+
if key?(key)
|
70
|
+
self[key]
|
71
|
+
else
|
72
|
+
super(s,*a,&b)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
data/lib/hashery/opencascade.rb
CHANGED
@@ -1,99 +1 @@
|
|
1
|
-
require 'hashery/
|
2
|
-
#require 'facets/nullclass'
|
3
|
-
|
4
|
-
# = OpenCascade
|
5
|
-
#
|
6
|
-
# OpenCascade is subclass of OpenHash. It differs in a few
|
7
|
-
# significant ways.
|
8
|
-
#
|
9
|
-
# The main reason this class is labeled "cascade", every internal
|
10
|
-
# Hash is transformed into an OpenCascade dynamically upon access.
|
11
|
-
# This makes it easy to create "cascading" references.
|
12
|
-
#
|
13
|
-
# h = { :x => { :y => { :z => 1 } } }
|
14
|
-
# c = OpenCascade[h]
|
15
|
-
# c.x.y.z #=> 1
|
16
|
-
#
|
17
|
-
# As soon as you access a node it automatically becomes an OpenCascade.
|
18
|
-
#
|
19
|
-
# c = OpenCascade.new #=> #<OpenCascade:0x7fac3680ccf0 {}>
|
20
|
-
# c.r #=> #<OpenCascade:0x7fac368084c0 {}>
|
21
|
-
# c.a.b #=> #<OpenCascade:0x7fac3680a4f0 {}>
|
22
|
-
#
|
23
|
-
# But if you set a node, then that will be it's value.
|
24
|
-
#
|
25
|
-
# c.a.b = 4 #=> 4
|
26
|
-
#
|
27
|
-
# To query a node without causing the auto-creation of an OpenCasade
|
28
|
-
# object, use the ?-mark.
|
29
|
-
#
|
30
|
-
# c.a.z? #=> nil
|
31
|
-
#
|
32
|
-
# OpenCascade also transforms Hashes within Arrays.
|
33
|
-
#
|
34
|
-
# h = { :x=>[ {:a=>1}, {:a=>2} ], :y=>1 }
|
35
|
-
# c = OpenCascade[h]
|
36
|
-
# c.x.first.a.assert == 1
|
37
|
-
# c.x.last.a.assert == 2
|
38
|
-
#
|
39
|
-
# Finally, you can set a node and get the reciever back using
|
40
|
-
# the !-mark.
|
41
|
-
#
|
42
|
-
# c = OpenCascade.new #=> #<OpenCascade:0x7fac3680ccf0 {}>
|
43
|
-
# c.x!(4).y!(3) #=> #<OpenCascade:0x7fac3680ccf0 {:x=>4, :y=>3}>
|
44
|
-
#
|
45
|
-
#--
|
46
|
-
# Last, when an entry is not found, 'null' is returned rather then 'nil'.
|
47
|
-
# This allows for run-on entries withuot error. Eg.
|
48
|
-
#
|
49
|
-
# o = OpenCascade.new
|
50
|
-
# o.a.b.c #=> null
|
51
|
-
#
|
52
|
-
# Unfortuately this requires an explict test for null? in 'if' conditions.
|
53
|
-
#
|
54
|
-
# if o.a.b.c.null? # true if null
|
55
|
-
# if o.a.b.c.nil? # true if nil or null
|
56
|
-
# if o.a.b.c.not? # true if nil or null or false
|
57
|
-
#
|
58
|
-
# So be sure to take that into account.
|
59
|
-
#++
|
60
|
-
|
61
|
-
class OpenCascade < OpenHash
|
62
|
-
|
63
|
-
#
|
64
|
-
def method_missing(sym, *args, &blk)
|
65
|
-
type = sym.to_s[-1,1]
|
66
|
-
name = sym.to_s.gsub(/[=!?]$/, '').to_sym
|
67
|
-
case type
|
68
|
-
when '='
|
69
|
-
self[name] = args.first
|
70
|
-
when '!'
|
71
|
-
#@hash.__send__(name, *args, &blk)
|
72
|
-
__send__(name, *args, &blk)
|
73
|
-
when '?'
|
74
|
-
self[name]
|
75
|
-
else
|
76
|
-
if key?(name)
|
77
|
-
self[name] = transform_entry(self[name])
|
78
|
-
else
|
79
|
-
self[name] = OpenCascade.new #self.class.new
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
#
|
87
|
-
def transform_entry(entry)
|
88
|
-
case entry
|
89
|
-
when Hash
|
90
|
-
OpenCascade.new(entry) #self.class.new(val)
|
91
|
-
when Array
|
92
|
-
entry.map{ |e| transform_entry(e) }
|
93
|
-
else
|
94
|
-
entry
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
end
|
99
|
-
|
1
|
+
require 'hashery/open_cascade'
|
data/lib/hashery/openhash.rb
CHANGED
@@ -1,77 +1 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# OpenHash is very similar to Ruby's own OpenStruct, but it offers some
|
4
|
-
# useful advantages in that it is a true Hash object.
|
5
|
-
#
|
6
|
-
# Because OpenHash is a subclass of Hash, it can do everything a Hash
|
7
|
-
# can *unless* a Hash method has been explicity exempted for use
|
8
|
-
# as an open read/writer via the #omit! method.
|
9
|
-
|
10
|
-
class OpenHash < Hash
|
11
|
-
|
12
|
-
# New OpenHash.
|
13
|
-
def initialize(data={})
|
14
|
-
super()
|
15
|
-
merge!(data)
|
16
|
-
end
|
17
|
-
|
18
|
-
#
|
19
|
-
def <<(x)
|
20
|
-
case x
|
21
|
-
when Hash
|
22
|
-
update(x)
|
23
|
-
when Array
|
24
|
-
x.each_slice(2) do |(k,v)|
|
25
|
-
self[k] = v
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
#
|
31
|
-
def respond_to?(name)
|
32
|
-
key?(name.to_sym) || super(name)
|
33
|
-
end
|
34
|
-
|
35
|
-
#
|
36
|
-
def to_h
|
37
|
-
dup
|
38
|
-
end
|
39
|
-
|
40
|
-
#
|
41
|
-
def to_hash
|
42
|
-
dup
|
43
|
-
end
|
44
|
-
|
45
|
-
#
|
46
|
-
def inspect
|
47
|
-
super
|
48
|
-
end
|
49
|
-
|
50
|
-
# Omit specific Hash methods from slot protection.
|
51
|
-
def omit!(*methods)
|
52
|
-
methods.reject!{ |x| x.to_s =~ /^__/ }
|
53
|
-
(class << self; self; end).class_eval{ private *methods }
|
54
|
-
end
|
55
|
-
|
56
|
-
# Route get and set calls.
|
57
|
-
def method_missing(s,*a, &b)
|
58
|
-
type = s.to_s[-1,1]
|
59
|
-
name = s.to_s.sub(/[!?=]$/, '')
|
60
|
-
key = name.to_sym
|
61
|
-
case type
|
62
|
-
when '='
|
63
|
-
self[key] = a[0]
|
64
|
-
#when '!'
|
65
|
-
# self[s] = OpenHash.new
|
66
|
-
when '?'
|
67
|
-
key?(key)
|
68
|
-
else
|
69
|
-
if key?(key)
|
70
|
-
self[key]
|
71
|
-
else
|
72
|
-
super(s,*a,&b)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
1
|
+
require 'hashery/open_hash'
|
data/lib/hashery/ordered_hash.rb
CHANGED
@@ -1 +1,168 @@
|
|
1
|
-
|
1
|
+
# = OrderedHash
|
2
|
+
#
|
3
|
+
# A simple ordered hash implmentation, for users of
|
4
|
+
# Ruby 1.8.7 or less.
|
5
|
+
#
|
6
|
+
# NOTE: As of Ruby 1.9+ this class is not needed, since
|
7
|
+
# Ruby 1.9's standard Hash tracks inseration order.
|
8
|
+
#
|
9
|
+
# This implementation derives from the same class in
|
10
|
+
# ActiveSupport library.
|
11
|
+
|
12
|
+
class OrderedHash < ::Hash
|
13
|
+
|
14
|
+
def to_yaml_type
|
15
|
+
"!tag:yaml.org,2002:omap"
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_yaml(opts = {})
|
19
|
+
YAML.quick_emit(self, opts) do |out|
|
20
|
+
out.seq(taguri, to_yaml_style) do |seq|
|
21
|
+
each do |k, v|
|
22
|
+
seq.add(k => v)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Hash is ordered in Ruby 1.9!
|
29
|
+
if RUBY_VERSION < '1.9'
|
30
|
+
def initialize(*args, &block)
|
31
|
+
super
|
32
|
+
@keys = []
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.[](*args)
|
36
|
+
ordered_hash = new
|
37
|
+
|
38
|
+
if (args.length == 1 && args.first.is_a?(Array))
|
39
|
+
args.first.each do |key_value_pair|
|
40
|
+
next unless (key_value_pair.is_a?(Array))
|
41
|
+
ordered_hash[key_value_pair[0]] = key_value_pair[1]
|
42
|
+
end
|
43
|
+
|
44
|
+
return ordered_hash
|
45
|
+
end
|
46
|
+
|
47
|
+
unless (args.size % 2 == 0)
|
48
|
+
raise ArgumentError.new("odd number of arguments for Hash")
|
49
|
+
end
|
50
|
+
|
51
|
+
args.each_with_index do |val, ind|
|
52
|
+
next if (ind % 2 != 0)
|
53
|
+
ordered_hash[val] = args[ind + 1]
|
54
|
+
end
|
55
|
+
|
56
|
+
ordered_hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize_copy(other)
|
60
|
+
super(other)
|
61
|
+
@keys = other.keys
|
62
|
+
end
|
63
|
+
|
64
|
+
def []=(key, value)
|
65
|
+
@keys << key unless key?(key)
|
66
|
+
super(key, value)
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete(key)
|
70
|
+
if has_key? key
|
71
|
+
index = @keys.index(key)
|
72
|
+
@keys.delete_at(index)
|
73
|
+
end
|
74
|
+
super(key)
|
75
|
+
end
|
76
|
+
|
77
|
+
def delete_if
|
78
|
+
super
|
79
|
+
sync_keys!
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
def reject!
|
84
|
+
super
|
85
|
+
sync_keys!
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def reject(&block)
|
90
|
+
dup.reject!(&block)
|
91
|
+
end
|
92
|
+
|
93
|
+
def keys
|
94
|
+
@keys.dup
|
95
|
+
end
|
96
|
+
|
97
|
+
def values
|
98
|
+
@keys.collect{ |key| self[key] }
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_hash
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_a
|
106
|
+
@keys.map{ |key| [ key, self[key] ] }
|
107
|
+
end
|
108
|
+
|
109
|
+
def each_key
|
110
|
+
@keys.each{ |key| yield(key) }
|
111
|
+
end
|
112
|
+
|
113
|
+
def each_value
|
114
|
+
@keys.each{ |key| yield(self[key]) }
|
115
|
+
end
|
116
|
+
|
117
|
+
def each
|
118
|
+
@keys.each{ |key| yield(key, self[key]) }
|
119
|
+
end
|
120
|
+
|
121
|
+
alias_method :each_pair, :each
|
122
|
+
|
123
|
+
def clear
|
124
|
+
super
|
125
|
+
@keys.clear
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
def shift
|
130
|
+
k = @keys.first
|
131
|
+
v = delete(k)
|
132
|
+
[k, v]
|
133
|
+
end
|
134
|
+
|
135
|
+
def merge!(other_hash)
|
136
|
+
other_hash.each{ |k,v| self[k] = v }
|
137
|
+
self
|
138
|
+
end
|
139
|
+
|
140
|
+
def merge(other_hash)
|
141
|
+
dup.merge!(other_hash)
|
142
|
+
end
|
143
|
+
|
144
|
+
# When replacing with another hash, the initial order of our
|
145
|
+
# keys must come from the other hash, ordered or not.
|
146
|
+
def replace(other)
|
147
|
+
super
|
148
|
+
@keys = other.keys
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
def inspect
|
153
|
+
"#<OrderedHash #{super}>"
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
def sync_keys!
|
158
|
+
@keys.delete_if{ |k| !key?(k) }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
require 'yaml'
|
164
|
+
|
165
|
+
YAML.add_builtin_type("omap") do |type, val|
|
166
|
+
OrderedHash[val.map(&:to_a).map(&:first)]
|
167
|
+
end
|
168
|
+
|