hashery 1.5.0 → 2.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/.ruby +30 -17
- data/.yardopts +1 -0
- data/Config.rb +28 -0
- data/{QED.rdoc → DEMO.rdoc} +0 -0
- data/HISTORY.rdoc +37 -0
- data/LICENSE.txt +26 -0
- data/NOTICE.txt +46 -0
- data/README.rdoc +10 -7
- data/lib/hashery.rb +6 -6
- data/lib/hashery.yml +30 -17
- data/lib/hashery/association.rb +169 -109
- data/lib/hashery/casting_hash.rb +128 -135
- data/lib/hashery/core_ext.rb +89 -61
- data/lib/hashery/crud_hash.rb +365 -0
- data/lib/hashery/dictionary.rb +545 -345
- data/lib/hashery/fuzzy_hash.rb +177 -125
- data/lib/hashery/ini_hash.rb +321 -0
- data/lib/hashery/key_hash.rb +54 -179
- data/lib/hashery/linked_list.rb +245 -191
- data/lib/hashery/lru_hash.rb +292 -202
- data/lib/hashery/open_cascade.rb +133 -78
- data/lib/hashery/open_hash.rb +127 -61
- data/lib/hashery/ordered_hash.rb +128 -122
- data/lib/hashery/path_hash.rb +238 -0
- data/lib/hashery/property_hash.rb +144 -80
- data/lib/hashery/query_hash.rb +85 -29
- data/lib/hashery/stash.rb +7 -3
- data/lib/hashery/static_hash.rb +46 -41
- data/test/case_association.rb +65 -4
- data/test/case_dictionary.rb +149 -5
- data/test/{case_keyhash.rb → case_key_hash.rb} +20 -14
- data/test/case_lru_hash.rb +162 -0
- data/test/{case_opencascade.rb → case_open_cascade.rb} +4 -8
- data/test/case_open_hash.rb +87 -0
- data/test/case_query_hash.rb +226 -0
- data/test/helper.rb +8 -0
- metadata +33 -63
- data/COPYING.rdoc +0 -45
- data/lib/hashery/basic_object.rb +0 -74
- data/lib/hashery/basic_struct.rb +0 -288
- data/lib/hashery/basicobject.rb +0 -1
- data/lib/hashery/basicstruct.rb +0 -1
- data/lib/hashery/castinghash.rb +0 -1
- data/lib/hashery/fuzzyhash.rb +0 -1
- data/lib/hashery/ini.rb +0 -268
- data/lib/hashery/keyhash.rb +0 -1
- data/lib/hashery/linkedlist.rb +0 -1
- data/lib/hashery/lruhash.rb +0 -1
- data/lib/hashery/memoizer.rb +0 -64
- data/lib/hashery/open_object.rb +0 -1
- data/lib/hashery/opencascade.rb +0 -1
- data/lib/hashery/openhash.rb +0 -1
- data/lib/hashery/openobject.rb +0 -1
- data/lib/hashery/orderedhash.rb +0 -1
- data/lib/hashery/ostructable.rb +0 -186
- data/lib/hashery/propertyhash.rb +0 -1
- data/lib/hashery/queryhash.rb +0 -1
- data/lib/hashery/statichash.rb +0 -1
- data/qed/01_openhash.rdoc +0 -57
- data/qed/02_queryhash.rdoc +0 -21
- data/qed/03_castinghash.rdoc +0 -13
- data/qed/04_statichash.rdoc +0 -22
- data/qed/05_association.rdoc +0 -59
- data/qed/06_opencascade.rdoc +0 -58
- data/qed/07_fuzzyhash.rdoc +0 -141
- data/qed/08_properyhash.rdoc +0 -38
- data/qed/09_ostructable.rdoc +0 -56
- data/qed/applique/ae.rb +0 -1
- data/test/case_basicstruct.rb +0 -192
- data/test/case_openhash.rb +0 -22
data/lib/hashery/open_cascade.rb
CHANGED
@@ -1,99 +1,154 @@
|
|
1
1
|
require 'hashery/open_hash'
|
2
|
-
#require 'facets/nullclass'
|
3
2
|
|
4
|
-
|
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
|
3
|
+
module Hashery
|
62
4
|
|
5
|
+
# OpenCascade is subclass of OpenHash. It differs in a few
|
6
|
+
# significant ways. The reason this class is called "cascade" is that
|
7
|
+
# every internal Hash is transformed into an OpenCascade dynamically
|
8
|
+
# upon access. This makes it easy to create "cascading" references.
|
9
|
+
#
|
10
|
+
# h = { :x => { :y => { :z => 1 } } }
|
11
|
+
# c = OpenCascade[h]
|
12
|
+
# c.x.y.z #=> 1
|
13
|
+
#
|
14
|
+
# As soon as you access a node it automatically becomes an OpenCascade.
|
15
|
+
#
|
16
|
+
# c = OpenCascade.new #=> #<OpenCascade:0x7fac3680ccf0 {}>
|
17
|
+
# c.r #=> #<OpenCascade:0x7fac368084c0 {}>
|
18
|
+
# c.a.b #=> #<OpenCascade:0x7fac3680a4f0 {}>
|
63
19
|
#
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
20
|
+
# But if you set a node, then that will be that value.
|
21
|
+
#
|
22
|
+
# c.a.b = 4 #=> 4
|
23
|
+
#
|
24
|
+
# To query a node without causing the auto-creation of an OpenCasade
|
25
|
+
# instance, use the `?`-mark.
|
26
|
+
#
|
27
|
+
# c.a.z? #=> nil
|
28
|
+
#
|
29
|
+
# OpenCascade also transforms Hashes within Arrays.
|
30
|
+
#
|
31
|
+
# h = { :x=>[ {:a=>1}, {:a=>2} ], :y=>1 }
|
32
|
+
# c = OpenCascade[h]
|
33
|
+
# c.x.first.a.assert == 1
|
34
|
+
# c.x.last.a.assert == 2
|
35
|
+
#
|
36
|
+
# Finally, you can set call a private method via bang methods using the `!`-mark.
|
37
|
+
#
|
38
|
+
# c = OpenCascade.new #=> #<OpenCascade:0x7fac3680ccf0 {}>
|
39
|
+
# c.each = 4
|
40
|
+
# c.each! do |k,v|
|
41
|
+
# ...
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# c.x!(4).y!(3) #=> #<OpenCascade:0x7fac3680ccf0 {:x=>4, :y=>3}>
|
45
|
+
#
|
46
|
+
class OpenCascade < OpenHash
|
47
|
+
|
48
|
+
#
|
49
|
+
#def self.[](hash)
|
50
|
+
# oc = new
|
51
|
+
# hash.each{ |(k,v)| oc.store(k,v) }
|
52
|
+
# oc
|
53
|
+
#end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Initialize new OpenCascade instance.
|
57
|
+
#
|
58
|
+
# default - The usual default object.
|
59
|
+
#
|
60
|
+
def initialize(*default)
|
61
|
+
@read = {}
|
62
|
+
|
63
|
+
leet = lambda { |h,k| h[k] = OpenCascade.new(&leet) }
|
64
|
+
super(*default, &leet)
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Alias for original read method.
|
69
|
+
#
|
70
|
+
alias :read! :read
|
71
|
+
|
72
|
+
#
|
73
|
+
# Read value given a +key+.
|
74
|
+
#
|
75
|
+
# key - Index keey to lookup.
|
76
|
+
#
|
77
|
+
# Returns value.
|
78
|
+
#
|
79
|
+
def read(key)
|
80
|
+
if @read[cast_key(key)]
|
81
|
+
super(key)
|
78
82
|
else
|
79
|
-
|
83
|
+
@read[cast_key(key)] = store(key, cast_value(super(key)))
|
80
84
|
end
|
81
85
|
end
|
82
|
-
|
86
|
+
|
87
|
+
#
|
88
|
+
#
|
89
|
+
#
|
90
|
+
def method_missing(sym, *args, &blk)
|
91
|
+
type = sym.to_s[-1,1]
|
92
|
+
name = sym.to_s.gsub(/[=!?]$/, '').to_sym
|
93
|
+
|
94
|
+
case type
|
95
|
+
when '='
|
96
|
+
store(name, args.first)
|
97
|
+
when '?'
|
98
|
+
key?(name) ? read!(name) : nil # key?(name)
|
99
|
+
when '!'
|
100
|
+
__send__(name, *args, &blk)
|
101
|
+
else
|
102
|
+
#if key?(name)
|
103
|
+
read(name)
|
104
|
+
#else
|
105
|
+
# #default = OpenCascade.new #self.class.new
|
106
|
+
# #default = default_proc ? default_proc.call(self, name) : default
|
107
|
+
# store(name, read(name))
|
108
|
+
#end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
#def each
|
113
|
+
# super do |key, entry|
|
114
|
+
# yield([key, transform_entry(entry)])
|
115
|
+
# end
|
116
|
+
#end
|
83
117
|
|
84
118
|
private
|
85
119
|
|
86
120
|
#
|
87
|
-
|
121
|
+
# Cast value, such that Hashes are converted to OpenCascades.
|
122
|
+
# And Hashes in Arrays are converted to OpenCascades as well.
|
123
|
+
#
|
124
|
+
def cast_value(entry)
|
88
125
|
case entry
|
89
126
|
when Hash
|
90
|
-
OpenCascade
|
127
|
+
OpenCascade[entry] #self.class.new(val)
|
91
128
|
when Array
|
92
|
-
entry.map{ |e|
|
129
|
+
entry.map{ |e| cast_value(e) }
|
93
130
|
else
|
94
131
|
entry
|
95
132
|
end
|
96
133
|
end
|
97
134
|
|
135
|
+
end
|
136
|
+
|
98
137
|
end
|
99
138
|
|
139
|
+
#--
|
140
|
+
# Last, when an entry is not found, 'null' is returned rather then 'nil'.
|
141
|
+
# This allows for run-on entries withuot error. Eg.
|
142
|
+
#
|
143
|
+
# o = OpenCascade.new
|
144
|
+
# o.a.b.c #=> null
|
145
|
+
#
|
146
|
+
# Unfortuately this requires an explict test for null? in 'if' conditions.
|
147
|
+
#
|
148
|
+
# if o.a.b.c.null? # true if null
|
149
|
+
# if o.a.b.c.nil? # true if nil or null
|
150
|
+
# if o.a.b.c.not? # true if nil or null or false
|
151
|
+
#
|
152
|
+
# So be sure to take that into account.
|
153
|
+
#++
|
154
|
+
|
data/lib/hashery/open_hash.rb
CHANGED
@@ -1,77 +1,143 @@
|
|
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.
|
1
|
+
require 'hashery/crud_hash'
|
9
2
|
|
10
|
-
|
11
|
-
|
12
|
-
# New OpenHash.
|
13
|
-
def initialize(data={})
|
14
|
-
super()
|
15
|
-
merge!(data)
|
16
|
-
end
|
3
|
+
module Hashery
|
17
4
|
|
5
|
+
# OpenHash is a Hash, but also supports open properties much like
|
6
|
+
# OpenStruct.
|
18
7
|
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
8
|
+
# Only names that are name methods of Hash can be used as open slots.
|
9
|
+
# To open a slot for a name that would otherwise be a method, the
|
10
|
+
# method needs to be made private. The `#open!` method can be used
|
11
|
+
# to handle this.
|
12
|
+
#
|
13
|
+
# Examples
|
14
|
+
#
|
15
|
+
# o = OpenHash.new
|
16
|
+
# o.open!(:send)
|
17
|
+
# o.send = 4
|
18
|
+
#
|
19
|
+
class OpenHash < CRUDHash
|
20
|
+
|
21
|
+
alias :object_class :class
|
22
|
+
|
23
|
+
#FILTER = /(^__|^\W|^instance_|^object_|^to_)/
|
24
|
+
#methods = Hash.instance_methods(true).select{ |m| m !~ FILTER }
|
25
|
+
#methods = methods - [:each, :inspect, :send] # :class, :as]
|
26
|
+
#private *methods
|
27
|
+
|
28
|
+
#
|
29
|
+
# Initialize new OpenHash instance.
|
30
|
+
#
|
31
|
+
# TODO: Maybe `safe` should be the first argument?
|
32
|
+
#
|
33
|
+
def initialize(default=nil, safe=false, &block)
|
34
|
+
@safe = safe
|
35
|
+
super(*[default].compact, &block)
|
27
36
|
end
|
28
|
-
end
|
29
37
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
38
|
+
#
|
39
|
+
# If safe is set to true, then public methods cannot be overriden
|
40
|
+
# by hash keys.
|
41
|
+
#
|
42
|
+
attr_accessor :safe
|
34
43
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
44
|
+
#
|
45
|
+
# Index `value` to `key`. Unless safe mode, will also open up the
|
46
|
+
# key if it is not already open.
|
47
|
+
#
|
48
|
+
# key - Index key to associate with value.
|
49
|
+
# value - Value to be associate with key.
|
50
|
+
#
|
51
|
+
# Returns +value+.
|
52
|
+
#
|
53
|
+
def store(key, value)
|
54
|
+
open!(key)
|
55
|
+
super(key, value)
|
56
|
+
end
|
39
57
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
58
|
+
#
|
59
|
+
# Open up a slot that that would normally be a Hash method.
|
60
|
+
#
|
61
|
+
# The only methods that can't be opened are ones starting with `__`.
|
62
|
+
#
|
63
|
+
# methods - [Array<String,Symbol>] method names
|
64
|
+
#
|
65
|
+
# Returns Array of slot names that were opened.
|
66
|
+
#
|
67
|
+
def open!(*methods)
|
68
|
+
# only select string and symbols, any other type of key is allowed,
|
69
|
+
# it just won't be accessible via dynamic methods.
|
70
|
+
methods = methods.select{ |x| String === x || Symbol === x }
|
71
|
+
# @todo should we just ignore these instead of raising an error?
|
72
|
+
#methods.reject!{ |x| x.to_s =~ /^__/ }
|
73
|
+
if methods.any?{ |m| m.to_s.start_with?('__') }
|
74
|
+
raise ArgumentError, "cannot set shadow methods"
|
75
|
+
end
|
76
|
+
# only public methods need to be made private
|
77
|
+
methods = methods.map{ |x| x.to_sym }
|
78
|
+
methods = methods & public_methods(true)
|
79
|
+
if @safe
|
80
|
+
raise ArgumentError, "cannot set public method" unless methods.empty?
|
81
|
+
else
|
82
|
+
(class << self; self; end).class_eval{ private *methods }
|
83
|
+
end
|
84
|
+
methods
|
85
|
+
end
|
44
86
|
|
45
|
-
|
46
|
-
|
47
|
-
super
|
48
|
-
end
|
87
|
+
# @deprecated
|
88
|
+
alias :omit! :open!
|
49
89
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
90
|
+
#
|
91
|
+
# Is a slot open?
|
92
|
+
#
|
93
|
+
# method - [String,Symbol] method name
|
94
|
+
#
|
95
|
+
# Returns `true` or `false`.
|
96
|
+
#
|
97
|
+
def open?(method)
|
98
|
+
! public_methods(true).include?(method.to_sym)
|
99
|
+
end
|
55
100
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
101
|
+
#
|
102
|
+
# Make specific Hash methods available for use that have previously opened.
|
103
|
+
#
|
104
|
+
# methods - [Array<String,Symbol>] method names
|
105
|
+
#
|
106
|
+
# Returns +methods+.
|
107
|
+
#
|
108
|
+
def close!(*methods)
|
109
|
+
(class << self; self; end).class_eval{ public *methods }
|
110
|
+
methods
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
#
|
115
|
+
#
|
116
|
+
def method_missing(s,*a, &b)
|
117
|
+
type = s.to_s[-1,1]
|
118
|
+
name = s.to_s.sub(/[!?=]$/, '')
|
119
|
+
key = name.to_sym
|
120
|
+
|
121
|
+
case type
|
122
|
+
when '='
|
123
|
+
#open!(key) unless open?(key)
|
124
|
+
#self[key] = a.first
|
125
|
+
store(key, a.first)
|
126
|
+
when '?'
|
127
|
+
key?(key)
|
128
|
+
when '!'
|
129
|
+
# call an underlying private method
|
130
|
+
# TODO: limit this to omitted methods (from included) ?
|
131
|
+
__send__(name, *a, &b)
|
71
132
|
else
|
72
|
-
|
133
|
+
#if key?(key)
|
134
|
+
read(key)
|
135
|
+
#else
|
136
|
+
# super(s,*a,&b)
|
137
|
+
#end
|
73
138
|
end
|
74
139
|
end
|
140
|
+
|
75
141
|
end
|
76
142
|
|
77
143
|
end
|
data/lib/hashery/ordered_hash.rb
CHANGED
@@ -1,163 +1,169 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
1
|
+
module Hashery
|
2
|
+
|
3
|
+
# OrderedHash is 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
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
26
|
-
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
# Hash is ordered in Ruby 1.9!
|
29
|
+
if RUBY_VERSION < '1.9'
|
30
|
+
|
31
|
+
def initialize(*args, &block)
|
32
|
+
super
|
33
|
+
@keys = []
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.[](*args)
|
37
|
+
ordered_hash = new
|
34
38
|
|
35
|
-
|
36
|
-
|
39
|
+
if (args.length == 1 && args.first.is_a?(Array))
|
40
|
+
args.first.each do |key_value_pair|
|
41
|
+
next unless (key_value_pair.is_a?(Array))
|
42
|
+
ordered_hash[key_value_pair[0]] = key_value_pair[1]
|
43
|
+
end
|
37
44
|
|
38
|
-
|
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]
|
45
|
+
return ordered_hash
|
42
46
|
end
|
43
47
|
|
44
|
-
|
45
|
-
|
48
|
+
unless (args.size % 2 == 0)
|
49
|
+
raise ArgumentError.new("odd number of arguments for Hash")
|
50
|
+
end
|
46
51
|
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
args.each_with_index do |val, ind|
|
53
|
+
next if (ind % 2 != 0)
|
54
|
+
ordered_hash[val] = args[ind + 1]
|
55
|
+
end
|
50
56
|
|
51
|
-
|
52
|
-
next if (ind % 2 != 0)
|
53
|
-
ordered_hash[val] = args[ind + 1]
|
57
|
+
ordered_hash
|
54
58
|
end
|
55
59
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
super(other)
|
61
|
-
@keys = other.keys
|
62
|
-
end
|
60
|
+
def initialize_copy(other)
|
61
|
+
super(other)
|
62
|
+
@keys = other.keys
|
63
|
+
end
|
63
64
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
65
|
+
def []=(key, value)
|
66
|
+
@keys << key unless key?(key)
|
67
|
+
super(key, value)
|
68
|
+
end
|
68
69
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
70
|
+
def delete(key)
|
71
|
+
if has_key? key
|
72
|
+
index = @keys.index(key)
|
73
|
+
@keys.delete_at(index)
|
74
|
+
end
|
75
|
+
super(key)
|
73
76
|
end
|
74
|
-
super(key)
|
75
|
-
end
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
78
|
+
def delete_if
|
79
|
+
super
|
80
|
+
sync_keys!
|
81
|
+
self
|
82
|
+
end
|
82
83
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
84
|
+
def reject!
|
85
|
+
super
|
86
|
+
sync_keys!
|
87
|
+
self
|
88
|
+
end
|
88
89
|
|
89
|
-
|
90
|
-
|
91
|
-
|
90
|
+
def reject(&block)
|
91
|
+
dup.reject!(&block)
|
92
|
+
end
|
92
93
|
|
93
|
-
|
94
|
-
|
95
|
-
|
94
|
+
def keys
|
95
|
+
@keys.dup
|
96
|
+
end
|
96
97
|
|
97
|
-
|
98
|
-
|
99
|
-
|
98
|
+
def values
|
99
|
+
@keys.collect{ |key| self[key] }
|
100
|
+
end
|
100
101
|
|
101
|
-
|
102
|
-
|
103
|
-
|
102
|
+
def to_hash
|
103
|
+
self
|
104
|
+
end
|
104
105
|
|
105
|
-
|
106
|
-
|
107
|
-
|
106
|
+
def to_a
|
107
|
+
@keys.map{ |key| [ key, self[key] ] }
|
108
|
+
end
|
108
109
|
|
109
|
-
|
110
|
-
|
111
|
-
|
110
|
+
def each_key
|
111
|
+
@keys.each{ |key| yield(key) }
|
112
|
+
end
|
112
113
|
|
113
|
-
|
114
|
-
|
115
|
-
|
114
|
+
def each_value
|
115
|
+
@keys.each{ |key| yield(self[key]) }
|
116
|
+
end
|
116
117
|
|
117
|
-
|
118
|
-
|
119
|
-
|
118
|
+
def each
|
119
|
+
@keys.each{ |key| yield(key, self[key]) }
|
120
|
+
end
|
120
121
|
|
121
|
-
|
122
|
+
alias_method :each_pair, :each
|
122
123
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
124
|
+
def clear
|
125
|
+
super
|
126
|
+
@keys.clear
|
127
|
+
self
|
128
|
+
end
|
128
129
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
130
|
+
def shift
|
131
|
+
k = @keys.first
|
132
|
+
v = delete(k)
|
133
|
+
[k, v]
|
134
|
+
end
|
134
135
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
136
|
+
def merge!(other_hash)
|
137
|
+
other_hash.each{ |k,v| self[k] = v }
|
138
|
+
self
|
139
|
+
end
|
139
140
|
|
140
|
-
|
141
|
-
|
142
|
-
|
141
|
+
def merge(other_hash)
|
142
|
+
dup.merge!(other_hash)
|
143
|
+
end
|
143
144
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
145
|
+
# When replacing with another hash, the initial order of our
|
146
|
+
# keys must come from the other hash, ordered or not.
|
147
|
+
def replace(other)
|
148
|
+
super
|
149
|
+
@keys = other.keys
|
150
|
+
self
|
151
|
+
end
|
151
152
|
|
152
|
-
|
153
|
-
|
154
|
-
|
153
|
+
def inspect
|
154
|
+
"#<OrderedHash #{super}>"
|
155
|
+
end
|
155
156
|
|
156
157
|
private
|
158
|
+
|
157
159
|
def sync_keys!
|
158
160
|
@keys.delete_if{ |k| !key?(k) }
|
159
161
|
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
160
165
|
end
|
166
|
+
|
161
167
|
end
|
162
168
|
|
163
169
|
require 'yaml'
|