safer 0.3.1 → 0.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/History.txt +14 -0
- data/Manifest.txt +1 -1
- data/{README.txt → README.rdoc} +0 -0
- data/Rakefile +2 -1
- data/lib/safer/hashprotocol.rb +263 -265
- data/lib/safer/ivar.rb +4 -219
- data/lib/safer/protocol.rb +454 -457
- data/lib/safer.rb +1 -1
- data/test/test_safer_hashprotocol.rb +27 -27
- data/test/test_safer_ivar.rb +25 -41
- data/test/test_safer_ivar_run.rb +77 -0
- data/test/test_safer_ivarfactory.rb +36 -0
- data/test/test_safer_protocol.rb +8 -3
- metadata +11 -8
data/History.txt
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
=== 0.4.0 / 2011-10-29
|
2
|
+
|
3
|
+
* Safer::IVar functionality moved into Safer::IVarFactory class.
|
4
|
+
* Safer::IVar is now an instance of Safer::IVarFactory.
|
5
|
+
* Added DSL interface to Safer::IVarFactory.
|
6
|
+
* Safer::IVarFactory has pluggable symbol prefix generation.
|
7
|
+
* Added facility to generate symbols based on last component of class name.
|
8
|
+
|
9
|
+
=== 0.3.2 / 2010-12-01
|
10
|
+
|
11
|
+
* Made compatible with ruby 1.9.2.
|
12
|
+
* Renamed README.txt to README.rdoc
|
13
|
+
* Refactored HashProtocol tests in accordance with common ruby practice.
|
14
|
+
|
1
15
|
=== 0.3.1 / 2010-11-21
|
2
16
|
|
3
17
|
* ri Safer didn't include mention of HashProtocol. Fixed.
|
data/Manifest.txt
CHANGED
data/{README.txt → README.rdoc}
RENAMED
File without changes
|
data/Rakefile
CHANGED
data/lib/safer/hashprotocol.rb
CHANGED
@@ -1,304 +1,302 @@
|
|
1
1
|
require 'safer/ivar'
|
2
2
|
require 'safer/protocol'
|
3
3
|
|
4
|
-
|
4
|
+
##
|
5
|
+
# Check that the keys in a Hash object follow a set of constraints.
|
6
|
+
# ==Usage
|
7
|
+
# In this example, we use a Hash to simulate keyword parameters to our
|
8
|
+
# function.
|
9
|
+
# class YourClass
|
10
|
+
# SHP = Safer::HashProtocol
|
11
|
+
# # we support two versions of the keyword arguments to our function.
|
12
|
+
# # In both versions, the Hash is required to have a :type field.
|
13
|
+
# CommonKeywords = SHP::HasKey.create(:type)
|
14
|
+
# # In the old version, a person's full name is presented in the :name
|
15
|
+
# # keyword.
|
16
|
+
# V1Keywords = SHP::HasKey.create(:name)
|
17
|
+
# # In the new version, the full name is separated into :familyName
|
18
|
+
# # and :givenName.
|
19
|
+
# V2Keywords = SHP::HasKey.create(:familyName, :givenName)
|
20
|
+
# # Check if the keywords in the Hash match Version1 (that is,
|
21
|
+
# # :name and :type fields must be present in the Hash, and no other
|
22
|
+
# # fields should be present).
|
23
|
+
# V1Check = SHP::Only.new(SHP::All.new(*CommonKeywords + *V1Keywords))
|
24
|
+
# # Check if the keywords in the Hash match Version2 (that is,
|
25
|
+
# # :familyName, :givenName, and :type fields must be present in the Hash,
|
26
|
+
# # and no other fields should be present).
|
27
|
+
# V2Check = SHP::Only.new(SHP::All.new(*CommonKeywords + *V2Keywords))
|
28
|
+
# # Check if the Hash matches either version 1 or version 2.
|
29
|
+
# ValidCheck = SHP::Any.new(V1Check, V2Check)
|
30
|
+
# def my_fn(h)
|
31
|
+
# if ! (ValidCheck === h)
|
32
|
+
# raise ArgumentError, "h should conform to #{ValidCheck.description}"
|
33
|
+
# end
|
34
|
+
# case h
|
35
|
+
# when V1Check
|
36
|
+
# v1_process(h[:name], h[:type])
|
37
|
+
# when V2Check
|
38
|
+
# v2_process(h[:familyName], h[:givenName], h[:type])
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# ==Rationale
|
43
|
+
# It is a common design practice in Ruby code to use Hash objects to get an
|
44
|
+
# analogue to function keyword arguments in other languages. Among other
|
45
|
+
# uses, this practice simplifies the implementation of embedded DSLs within
|
46
|
+
# Ruby. When used in this way, the Hash keys are semantically meaningful,
|
47
|
+
# and are generally entered by hand. It's important to get these Hashes
|
48
|
+
# right. Safer::HashProtocol is intended to help, by simultaneously
|
49
|
+
# providing a means of documenting the key combinations required of a Hash,
|
50
|
+
# and by detecting when the Hash does not contain a valid set of keys.
|
51
|
+
class Safer::HashProtocol
|
5
52
|
##
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
# function.
|
10
|
-
# class YourClass
|
11
|
-
# SHP = Safer::HashProtocol
|
12
|
-
# # we support two versions of the keyword arguments to our function.
|
13
|
-
# # In both versions, the Hash is required to have a :type field.
|
14
|
-
# CommonKeywords = SHP::HasKey.create(:type)
|
15
|
-
# # In the old version, a person's full name is presented in the :name
|
16
|
-
# # keyword.
|
17
|
-
# V1Keywords = SHP::HasKey.create(:name)
|
18
|
-
# # In the new version, the full name is separated into :familyName
|
19
|
-
# # and :givenName.
|
20
|
-
# V2Keywords = SHP::HasKey.create(:familyName, :givenName)
|
21
|
-
# # Check if the keywords in the Hash match Version1 (that is,
|
22
|
-
# # :name and :type fields must be present in the Hash, and no other
|
23
|
-
# # fields should be present).
|
24
|
-
# V1Check = SHP::Only.new(SHP::All.new(*CommonKeywords + *V1Keywords))
|
25
|
-
# # Check if the keywords in the Hash match Version2 (that is,
|
26
|
-
# # :familyName, :givenName, and :type fields must be present in the Hash,
|
27
|
-
# # and no other fields should be present).
|
28
|
-
# V2Check = SHP::Only.new(SHP::All.new(*CommonKeywords + *V2Keywords))
|
29
|
-
# # Check if the Hash matches either version 1 or version 2.
|
30
|
-
# ValidCheck = SHP::Any.new(V1Check, V2Check)
|
31
|
-
# def my_fn(h)
|
32
|
-
# if ! (ValidCheck === h)
|
33
|
-
# raise ArgumentError, "h should conform to #{ValidCheck.description}"
|
34
|
-
# end
|
35
|
-
# case h
|
36
|
-
# when V1Check
|
37
|
-
# v1_process(h[:name], h[:type])
|
38
|
-
# when V2Check
|
39
|
-
# v2_process(h[:familyName], h[:givenName], h[:type])
|
40
|
-
# end
|
41
|
-
# end
|
42
|
-
# end
|
43
|
-
# ==Rationale
|
44
|
-
# It is a common design practice in Ruby code to use Hash objects to get an
|
45
|
-
# analogue to function keyword arguments in other languages. Among other
|
46
|
-
# uses, this practice simplifies the implementation of embedded DSLs within
|
47
|
-
# Ruby. When used in this way, the Hash keys are semantically meaningful,
|
48
|
-
# and are generally entered by hand. It's important to get these Hashes
|
49
|
-
# right. Safer::HashProtocol is intended to help, by simultaneously
|
50
|
-
# providing a means of documenting the key combinations required of a Hash,
|
51
|
-
# and by detecting when the Hash does not contain a valid set of keys.
|
52
|
-
class HashProtocol
|
53
|
+
# Object signature required of HashProtocol objects. Used to derive
|
54
|
+
# Safer::HashProtocol::Protocol.
|
55
|
+
class ProtocolBase
|
53
56
|
##
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
##
|
58
|
-
# Retrieve a human-readable description of this HashProtocol object.
|
59
|
-
def description
|
60
|
-
end
|
61
|
-
|
62
|
-
##
|
63
|
-
# Check that a Hash (h) follows this object's protocol.
|
64
|
-
def ===(h)
|
65
|
-
end
|
57
|
+
# Retrieve a human-readable description of this HashProtocol object.
|
58
|
+
def description
|
59
|
+
end
|
66
60
|
|
67
|
-
##
|
68
|
-
# Check that a Hash (h) follows this object's protocol, keeping
|
69
|
-
# track of which parts of (h) do not. +remaining+ should only
|
70
|
-
# be updated if +h+ matches this object.
|
71
|
-
def match(h, remaining)
|
72
|
-
end
|
73
|
-
end # Safer::HashProtocol::ProtocolBase
|
74
61
|
##
|
75
|
-
#
|
76
|
-
|
77
|
-
|
62
|
+
# Check that a Hash (h) follows this object's protocol.
|
63
|
+
def ===(h)
|
64
|
+
end
|
78
65
|
|
79
66
|
##
|
80
|
-
#
|
81
|
-
#
|
82
|
-
|
83
|
-
|
67
|
+
# Check that a Hash (h) follows this object's protocol, keeping
|
68
|
+
# track of which parts of (h) do not. +remaining+ should only
|
69
|
+
# be updated if +h+ matches this object.
|
70
|
+
def match(h, remaining)
|
71
|
+
end
|
72
|
+
end # Safer::HashProtocol::ProtocolBase
|
73
|
+
##
|
74
|
+
# Object signature required of HashProtocol objects. Derived from
|
75
|
+
# ProtocolBase.
|
76
|
+
Protocol = Safer::Protocol.create_from_class(ProtocolBase)
|
84
77
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
78
|
+
##
|
79
|
+
# Base class of all classes exported from Safer::HashProtocol. Implements
|
80
|
+
# ProtocolBase#description signature.
|
81
|
+
class Base
|
82
|
+
Safer::IVar.instance_variable(self, :description)
|
89
83
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end # Safer::HashProtocol::Base#initialize
|
95
|
-
end # Safer::HashProtocol::Base
|
84
|
+
##
|
85
|
+
# :attr_reader: description
|
86
|
+
# Human-readable description of this HashProtocol.
|
87
|
+
Safer::IVar.export_reader(self, :description)
|
96
88
|
|
97
89
|
##
|
98
|
-
#
|
99
|
-
|
100
|
-
|
101
|
-
|
90
|
+
# Store +description+ into the #description reader attribute.
|
91
|
+
def initialize(description)
|
92
|
+
self.safer_hashprotocol_base__description = description
|
93
|
+
end # Safer::HashProtocol::Base#initialize
|
94
|
+
end # Safer::HashProtocol::Base
|
102
95
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
##
|
109
|
-
# Stores the description and base HashProtocol object in this
|
110
|
-
# HashProtocol object. The description is derived from the description
|
111
|
-
# of the base object, prepended by +prefix+.
|
112
|
-
def initialize(prefix, base)
|
113
|
-
Protocol.instance_conforms?(base)
|
114
|
-
super("#{prefix}#{base.description}")
|
115
|
-
self.safer_hashprotocol_single__base = base
|
116
|
-
end # Safer::HashProtocol::Single#initialize
|
117
|
-
end # Safer::HashProtocol::Single
|
96
|
+
##
|
97
|
+
# Base class of Safer::HashProtocol implementations that provide some
|
98
|
+
# derived value from a single "base" implementation.
|
99
|
+
class Single < Safer::HashProtocol::Base
|
100
|
+
Safer::IVar.instance_variable(self, :base)
|
118
101
|
|
119
102
|
##
|
120
|
-
#
|
121
|
-
#
|
122
|
-
|
123
|
-
|
103
|
+
# :attr_reader: base
|
104
|
+
# Base HashProtocol object from which this object's match operation
|
105
|
+
# should be derived.
|
106
|
+
Safer::IVar.export_reader(self, :base)
|
107
|
+
##
|
108
|
+
# Stores the description and base HashProtocol object in this
|
109
|
+
# HashProtocol object. The description is derived from the description
|
110
|
+
# of the base object, prepended by +prefix+.
|
111
|
+
def initialize(prefix, base)
|
112
|
+
Protocol.instance_conforms?(base)
|
113
|
+
super("#{prefix}#{base.description}")
|
114
|
+
self.safer_hashprotocol_single__base = base
|
115
|
+
end # Safer::HashProtocol::Single#initialize
|
116
|
+
end # Safer::HashProtocol::Single
|
117
|
+
|
118
|
+
##
|
119
|
+
# Base class of Safer::HashProtocol implementations that provide some
|
120
|
+
# derived value from a set of other HashProtocol implementations.
|
121
|
+
class Compound < Safer::HashProtocol::Base
|
122
|
+
Safer::IVar.instance_variable(self, :list)
|
124
123
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
124
|
+
##
|
125
|
+
# :attr_reader: list
|
126
|
+
# Set of HashProtocol objects from which this object's match operation
|
127
|
+
# should be derived.
|
128
|
+
Safer::IVar.export_reader(self, :list)
|
130
129
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
130
|
+
##
|
131
|
+
# Stores the description and set of base HashProtocol objects in this
|
132
|
+
# HashProtocol object. The description is derived from the description
|
133
|
+
# of the base HashProtocol objects, with +sep+ between each element.
|
134
|
+
def initialize(sep, *list)
|
135
|
+
desc = list.map do |el|
|
136
|
+
Protocol.instance_conforms?(el)
|
137
|
+
"(#{el.description})"
|
138
|
+
end.join(sep)
|
139
|
+
super(desc)
|
140
|
+
self.safer_hashprotocol_compound__list = list
|
141
|
+
end # Safer::HashProtocol::Compound#initialize
|
142
|
+
end # Safer::HashProtocol::Compound
|
144
143
|
|
144
|
+
##
|
145
|
+
# Terminal Safer::HashProtocol implementation, checks that a key exists in
|
146
|
+
# a Hash.
|
147
|
+
class HasKey < Safer::HashProtocol::Base
|
148
|
+
Safer::IVar.instance_variable(self, :key)
|
145
149
|
##
|
146
|
-
#
|
147
|
-
#
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
# +key.inspect+ is used for the description.
|
153
|
-
def initialize(key)
|
154
|
-
super(key.inspect)
|
155
|
-
self.safer_hashprotocol_haskey__key = key
|
156
|
-
end # Safer::HashProtocol::HasKey#initialize
|
150
|
+
# The match operation will check that +key+ exists in the Hash.
|
151
|
+
# +key.inspect+ is used for the description.
|
152
|
+
def initialize(key)
|
153
|
+
super(key.inspect)
|
154
|
+
self.safer_hashprotocol_haskey__key = key
|
155
|
+
end # Safer::HashProtocol::HasKey#initialize
|
157
156
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
157
|
+
##
|
158
|
+
# Check that the +key+ from self.initialize is stored in Hash h.
|
159
|
+
def ===(h)
|
160
|
+
h.has_key?(self.safer_hashprotocol_haskey__key)
|
161
|
+
end # Safer::HashProtocol::HasKey#===
|
162
|
+
##
|
163
|
+
# Check that the +key+ from self.initialize is stored in Hash h. If it
|
164
|
+
# is, remove that key from +remaining+.
|
165
|
+
def match(h, remaining)
|
166
|
+
if h.has_key?(self.safer_hashprotocol_haskey__key)
|
167
|
+
remaining.delete(self.safer_hashprotocol_haskey__key)
|
168
|
+
true
|
169
|
+
else
|
170
|
+
false
|
171
|
+
end
|
172
|
+
end # Safer::HashProtocol::HasKey#match
|
174
173
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
174
|
+
##
|
175
|
+
# Convenience function creates a HasKey object for each argument
|
176
|
+
# passed in.
|
177
|
+
def self.create(*args)
|
178
|
+
args.map do |el| self.new(el) end
|
179
|
+
end # Safer::HashProtocol::HasKey.create
|
180
|
+
end # Safer::HashProtocol::HasKey
|
182
181
|
|
182
|
+
##
|
183
|
+
# Check that at least one of a set of Safer::HashProtocol objects matches
|
184
|
+
# a Hash.
|
185
|
+
class Any < Safer::HashProtocol::Compound
|
183
186
|
##
|
184
|
-
#
|
185
|
-
#
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
# argument, separated by " OR ".
|
190
|
-
def initialize(*list)
|
191
|
-
super(" OR ", *list)
|
192
|
-
end # Safer::HashProtocol::Any#initialize
|
187
|
+
# The description for this object will be the descriptions for each
|
188
|
+
# argument, separated by " OR ".
|
189
|
+
def initialize(*list)
|
190
|
+
super(" OR ", *list)
|
191
|
+
end # Safer::HashProtocol::Any#initialize
|
193
192
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
193
|
+
##
|
194
|
+
# Check if any elements from the +list+ argument to initialize match
|
195
|
+
# Hash h.
|
196
|
+
def ===(h)
|
197
|
+
self.list.any? do |el| el === h end
|
198
|
+
end # Safer::HashProtocol::Any#===
|
200
199
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
end
|
200
|
+
##
|
201
|
+
# Check if any elements from the +list+ argument to initialize match
|
202
|
+
# Hash h, keeping track of which elements from h have not been matched.
|
203
|
+
def match(h, remaining)
|
204
|
+
self.list.inject(false) do |memo, el|
|
205
|
+
if el.match(h, remaining)
|
206
|
+
true
|
207
|
+
else
|
208
|
+
memo
|
211
209
|
end
|
212
|
-
end
|
213
|
-
end # Safer::HashProtocol::Any
|
210
|
+
end
|
211
|
+
end # Safer::HashProtocol::Any#match
|
212
|
+
end # Safer::HashProtocol::Any
|
214
213
|
|
214
|
+
##
|
215
|
+
# Check that all of a set of Safer::HashProtocol objects match a Hash.
|
216
|
+
class All < Safer::HashProtocol::Compound
|
215
217
|
##
|
216
|
-
#
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
def initialize(*list)
|
222
|
-
super(" AND ", *list)
|
223
|
-
end # Safer::HashProtocol::All#initialize
|
218
|
+
# The description for this object will be the descriptions for each
|
219
|
+
# argument, separated by " AND ".
|
220
|
+
def initialize(*list)
|
221
|
+
super(" AND ", *list)
|
222
|
+
end # Safer::HashProtocol::All#initialize
|
224
223
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
224
|
+
##
|
225
|
+
# Check if all elements from the +list+ argument to initialize match
|
226
|
+
# Hash h.
|
227
|
+
def ===(h)
|
228
|
+
self.list.all? do |el| el === h end
|
229
|
+
end # Safer::HashProtocol::All#===
|
231
230
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
231
|
+
##
|
232
|
+
# Check if all elements from the +list+ argument to initialize match
|
233
|
+
# Hash h. If they do, +remaining+ will be updated to remove all matching
|
234
|
+
# elements from +list+. If not all elements match, +remaining+ will be
|
235
|
+
# unmodified.
|
236
|
+
def match(h, remaining)
|
237
|
+
nremaining = remaining.dup
|
238
|
+
rval = self.list.all? do |el| el.match(h, nremaining) end
|
239
|
+
if rval
|
240
|
+
remaining.update(nremaining)
|
241
|
+
end
|
242
|
+
rval
|
243
|
+
end # Safer::HashProtocol::All#match
|
244
|
+
end # Safer::HashProtocol::All
|
246
245
|
|
246
|
+
##
|
247
|
+
# Check that a base Safer::HashProtocol object does NOT match a Hash.
|
248
|
+
# That is, invert the match of a base HashProtocol object.
|
249
|
+
class Not < Safer::HashProtocol::Single
|
247
250
|
##
|
248
|
-
#
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
# The description for this object will be "NOT #{base.description}".
|
253
|
-
def initialize(base)
|
254
|
-
super("NOT ", base)
|
255
|
-
end # Safer::HashProtocol::Not#initialize
|
251
|
+
# The description for this object will be "NOT #{base.description}".
|
252
|
+
def initialize(base)
|
253
|
+
super("NOT ", base)
|
254
|
+
end # Safer::HashProtocol::Not#initialize
|
256
255
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
256
|
+
##
|
257
|
+
# Check that the base object does NOT match the hash.
|
258
|
+
def ===(h)
|
259
|
+
! (self.base === h)
|
260
|
+
end # Safer::HashProtocol::Not#===
|
262
261
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
262
|
+
##
|
263
|
+
# Check that the base object does NOT match the hash. Does not update
|
264
|
+
# +remaining+ under any circumstance.
|
265
|
+
def match(h, remaining)
|
266
|
+
self === h
|
267
|
+
end # Safer::HashProtocol::Not#match
|
268
|
+
end # Safer::HashProtocol::Not
|
270
269
|
|
270
|
+
##
|
271
|
+
# Check that the Hash only contains elements that match the base
|
272
|
+
# Safer::HashProtocol object.
|
273
|
+
class Only < Safer::HashProtocol::Single
|
271
274
|
##
|
272
|
-
#
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
if self.base.match(h, remaining)
|
286
|
-
remaining.empty?
|
287
|
-
else
|
288
|
-
false
|
289
|
-
end
|
275
|
+
# The description for this object will be "ONLY #{base.description}".
|
276
|
+
def initialize(base)
|
277
|
+
super("ONLY ", base)
|
278
|
+
end
|
279
|
+
##
|
280
|
+
# Check that all keys in a hash are matched by the base HashProtocol
|
281
|
+
# object.
|
282
|
+
def ===(h)
|
283
|
+
remaining = h.dup
|
284
|
+
if self.base.match(h, remaining)
|
285
|
+
remaining.empty?
|
286
|
+
else
|
287
|
+
false
|
290
288
|
end
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
289
|
+
end
|
290
|
+
##
|
291
|
+
# Check that all keys in a hash are matched by the base HashProtocol
|
292
|
+
# object. If they are, then empty +remaining+.
|
293
|
+
def match(h, remaining)
|
294
|
+
if self === h
|
295
|
+
remaining.clear
|
296
|
+
true
|
297
|
+
else
|
298
|
+
false
|
301
299
|
end
|
302
|
-
end
|
303
|
-
end # Safer::HashProtocol
|
304
|
-
end # Safer
|
300
|
+
end
|
301
|
+
end # Safer::HashProtocol::Only
|
302
|
+
end # Safer::HashProtocol
|