hashery 1.5.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|