hashery 1.4.0 → 1.5.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 +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
|
+
|