safer 0.2.1 → 0.3.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 -6
- data/Manifest.txt +2 -0
- data/README.txt +32 -9
- data/lib/safer/hashprotocol.rb +304 -0
- data/lib/safer/ivar.rb +2 -2
- data/lib/safer/protocol.rb +43 -3
- data/lib/safer.rb +2 -2
- data/test/test_safer_hashprotocol.rb +47 -0
- data/test/test_safer_protocol.rb +28 -0
- metadata +18 -12
data/History.txt
CHANGED
@@ -1,6 +1,16 @@
|
|
1
|
-
=== 0.
|
1
|
+
=== 0.3.0 / 2010-11-20
|
2
2
|
|
3
|
-
*
|
3
|
+
* Make Safer a Class, instead of a Module, to prevent embedding.
|
4
|
+
* Protocol provides match_class and match_instance methods, allowing use of
|
5
|
+
case statements to verify protocol conformity.
|
6
|
+
* Added HashProtocol suite, to verify that keys of a Hash follow desired
|
7
|
+
usage guidelines.
|
8
|
+
|
9
|
+
=== 0.2.1 / 2010-10-17
|
10
|
+
|
11
|
+
* README.txt still referred to ClassProtocol. Fixed.
|
12
|
+
* The Safer module documentation now refers to its submodules.
|
13
|
+
* Regularized use of '::' and '.'.
|
4
14
|
|
5
15
|
=== 0.2.0 / 2010-10-10
|
6
16
|
|
@@ -8,8 +18,6 @@
|
|
8
18
|
* Protocol can non-violently check if a class or instance conforms.
|
9
19
|
* Added much documentation.
|
10
20
|
|
11
|
-
=== 0.
|
21
|
+
=== 0.1.0 / 2010-09-22
|
12
22
|
|
13
|
-
*
|
14
|
-
* The Safer module documentation now refers to its submodules.
|
15
|
-
* Regularized use of '::' and '.'.
|
23
|
+
* First release
|
data/Manifest.txt
CHANGED
data/README.txt
CHANGED
@@ -5,15 +5,18 @@ http://safer.rubyforge.org
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
7
7
|
Safer is an umbrella library, with components designed to make it simple to
|
8
|
-
verify and improve the safety of your ruby code. There are at present
|
8
|
+
verify and improve the safety of your ruby code. There are at present three
|
9
9
|
modules under the safer umbrella:
|
10
10
|
|
11
|
-
[<tt>Safer::IVar</tt>]
|
12
|
-
|
13
|
-
[<tt>Safer::Protocol</tt>]
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
[<tt>Safer::IVar</tt>] generates specially-named accessor functions
|
12
|
+
for class instance variables.
|
13
|
+
[<tt>Safer::Protocol</tt>] is used to provide a ruby analogue to
|
14
|
+
Objective-C Protocols (which are similar to
|
15
|
+
Java interfaces, but do not require
|
16
|
+
inheritance).
|
17
|
+
[<tt>Safer::HashProtocol</tt>] verifies that a Hash keys follow a defined
|
18
|
+
format. Intended to help use of Hash objects as
|
19
|
+
keyword parameters.
|
17
20
|
|
18
21
|
== FEATURES/PROBLEMS:
|
19
22
|
|
@@ -22,11 +25,13 @@ modules under the safer umbrella:
|
|
22
25
|
* Define a "Protocol" class, and derive a signature from it. Verify that
|
23
26
|
another class (or an instance of that class) implements the protocol
|
24
27
|
signature.
|
28
|
+
* Define a check if a Hash contains a desired combination of keys.
|
25
29
|
|
26
30
|
== SYNOPSIS:
|
27
31
|
|
28
32
|
require 'safer/ivar'
|
29
33
|
require 'safer/protocol'
|
34
|
+
require 'safer/hashprotocol'
|
30
35
|
|
31
36
|
module MyModule
|
32
37
|
class MyProtocolClass
|
@@ -39,11 +44,29 @@ modules under the safer umbrella:
|
|
39
44
|
class MyClass
|
40
45
|
Safer::IVar.instance_variable(self, :var1, :var2)
|
41
46
|
def initialize(var1, var2)
|
42
|
-
PROTOCOL.
|
43
|
-
PROTOCOL.
|
47
|
+
PROTOCOL.instance_conforms?(var1)
|
48
|
+
PROTOCOL.instance_conforms?(var2)
|
44
49
|
self.mymodule_myclass__var1 = var1
|
45
50
|
self.mymodule_myclass__var2 = var2
|
46
51
|
end
|
52
|
+
|
53
|
+
SHP = Safer::HashProtocol
|
54
|
+
V1Keywords = SHP::HasKey.create(:name, :type)
|
55
|
+
V2Keywords = SHP::HasKey.create(:first, :last, :type)
|
56
|
+
V1Check = SHP::Only.new(SHP::Any.new(*V1Keywords))
|
57
|
+
V2Check = SHP::Only.new(SHP::Any.new(*V2Keywords))
|
58
|
+
ValidCheck = SHP::Any.new(V1Check, V2Check)
|
59
|
+
def my_fn(h)
|
60
|
+
if ! (ValidCheck === h)
|
61
|
+
raise ArgumentError, "h should conform to #{ValidCheck.description}"
|
62
|
+
end
|
63
|
+
case h
|
64
|
+
when V1Check
|
65
|
+
v1_process(h[:name], h[:type])
|
66
|
+
when V2Check
|
67
|
+
v2_process(h[:first], h[:last], h[:type])
|
68
|
+
end
|
69
|
+
end
|
47
70
|
end
|
48
71
|
end
|
49
72
|
|
@@ -0,0 +1,304 @@
|
|
1
|
+
require 'safer/ivar'
|
2
|
+
require 'safer/protocol'
|
3
|
+
|
4
|
+
class Safer
|
5
|
+
##
|
6
|
+
# Check that the keys in a Hash object follow a set of constraints.
|
7
|
+
# ==Usage
|
8
|
+
# In this example, we use a Hash to simulate keyword parameters to our
|
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
|
+
##
|
54
|
+
# Object signature required of HashProtocol objects. Used to derive
|
55
|
+
# Safer::HashProtocol::Protocol.
|
56
|
+
class ProtocolBase
|
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
|
66
|
+
|
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
|
+
##
|
75
|
+
# Object signature required of HashProtocol objects. Derived from
|
76
|
+
# ProtocolBase.
|
77
|
+
Protocol = Safer::Protocol.create_from_class(ProtocolBase)
|
78
|
+
|
79
|
+
##
|
80
|
+
# Base class of all classes exported from Safer::HashProtocol. Implements
|
81
|
+
# ProtocolBase#description signature.
|
82
|
+
class Base
|
83
|
+
Safer::IVar.instance_variable(self, :description)
|
84
|
+
|
85
|
+
##
|
86
|
+
# :attr_reader: description
|
87
|
+
# Human-readable description of this HashProtocol.
|
88
|
+
Safer::IVar.export_reader(self, :description)
|
89
|
+
|
90
|
+
##
|
91
|
+
# Store +description+ into the #description reader attribute.
|
92
|
+
def initialize(description)
|
93
|
+
self.safer_hashprotocol_base__description = description
|
94
|
+
end # Safer::HashProtocol::Base#initialize
|
95
|
+
end # Safer::HashProtocol::Base
|
96
|
+
|
97
|
+
##
|
98
|
+
# Base class of Safer::HashProtocol implementations that provide some
|
99
|
+
# derived value from a single "base" implementation.
|
100
|
+
class Single < Safer::HashProtocol::Base
|
101
|
+
Safer::IVar.instance_variable(self, :base)
|
102
|
+
|
103
|
+
##
|
104
|
+
# :attr_reader: base
|
105
|
+
# Base HashProtocol object from which this object's match operation
|
106
|
+
# should be derived.
|
107
|
+
Safer::IVar.export_reader(self, :base)
|
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
|
118
|
+
|
119
|
+
##
|
120
|
+
# Base class of Safer::HashProtocol implementations that provide some
|
121
|
+
# derived value from a set of other HashProtocol implementations.
|
122
|
+
class Compound < Safer::HashProtocol::Base
|
123
|
+
Safer::IVar.instance_variable(self, :list)
|
124
|
+
|
125
|
+
##
|
126
|
+
# :attr_reader: list
|
127
|
+
# Set of HashProtocol objects from which this object's match operation
|
128
|
+
# should be derived.
|
129
|
+
Safer::IVar.export_reader(self, :list)
|
130
|
+
|
131
|
+
##
|
132
|
+
# Stores the description and set of base HashProtocol objects in this
|
133
|
+
# HashProtocol object. The description is derived from the description
|
134
|
+
# of the base HashProtocol objects, with +sep+ between each element.
|
135
|
+
def initialize(sep, *list)
|
136
|
+
desc = list.map do |el|
|
137
|
+
Protocol.instance_conforms?(el)
|
138
|
+
"(#{el.description})"
|
139
|
+
end.join(sep)
|
140
|
+
super(desc)
|
141
|
+
self.safer_hashprotocol_compound__list = list
|
142
|
+
end # Safer::HashProtocol::Compound#initialize
|
143
|
+
end # Safer::HashProtocol::Compound
|
144
|
+
|
145
|
+
##
|
146
|
+
# Terminal Safer::HashProtocol implementation, checks that a key exists in
|
147
|
+
# a Hash.
|
148
|
+
class HasKey < Safer::HashProtocol::Base
|
149
|
+
Safer::IVar.instance_variable(self, :key)
|
150
|
+
##
|
151
|
+
# The match operation will check that +key+ exists in the Hash.
|
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
|
157
|
+
|
158
|
+
##
|
159
|
+
# Check that the +key+ from self.initialize is stored in Hash h.
|
160
|
+
def ===(h)
|
161
|
+
h.has_key?(self.safer_hashprotocol_haskey__key)
|
162
|
+
end # Safer::HashProtocol::HasKey#===
|
163
|
+
##
|
164
|
+
# Check that the +key+ from self.initialize is stored in Hash h. If it
|
165
|
+
# is, remove that key from +remaining+.
|
166
|
+
def match(h, remaining)
|
167
|
+
if h.has_key?(self.safer_hashprotocol_haskey__key)
|
168
|
+
remaining.delete(self.safer_hashprotocol_haskey__key)
|
169
|
+
true
|
170
|
+
else
|
171
|
+
false
|
172
|
+
end
|
173
|
+
end # Safer::HashProtocol::HasKey#match
|
174
|
+
|
175
|
+
##
|
176
|
+
# Convenience function creates a HasKey object for each argument
|
177
|
+
# passed in.
|
178
|
+
def self.create(*args)
|
179
|
+
args.map do |el| self.new(el) end
|
180
|
+
end # Safer::HashProtocol::HasKey.create
|
181
|
+
end # Safer::HashProtocol::HasKey
|
182
|
+
|
183
|
+
##
|
184
|
+
# Check that at least one of a set of Safer::HashProtocol objects matches
|
185
|
+
# a Hash.
|
186
|
+
class Any < Safer::HashProtocol::Compound
|
187
|
+
##
|
188
|
+
# The description for this object will be the descriptions for each
|
189
|
+
# argument, separated by " OR ".
|
190
|
+
def initialize(*list)
|
191
|
+
super(" OR ", *list)
|
192
|
+
end # Safer::HashProtocol::Any#initialize
|
193
|
+
|
194
|
+
##
|
195
|
+
# Check if any elements from the +list+ argument to initialize match
|
196
|
+
# Hash h.
|
197
|
+
def ===(h)
|
198
|
+
self.list.any? do |el| el === h end
|
199
|
+
end # Safer::HashProtocol::Any#===
|
200
|
+
|
201
|
+
##
|
202
|
+
# Check if any elements from the +list+ argument to initialize match
|
203
|
+
# Hash h, keeping track of which elements from h have not been matched.
|
204
|
+
def match(h, remaining)
|
205
|
+
self.list.inject(false) do |memo, el|
|
206
|
+
if el.match(h, remaining)
|
207
|
+
true
|
208
|
+
else
|
209
|
+
memo
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end # Safer::HashProtocol::Any#match
|
213
|
+
end # Safer::HashProtocol::Any
|
214
|
+
|
215
|
+
##
|
216
|
+
# Check that all of a set of Safer::HashProtocol objects match a Hash.
|
217
|
+
class All < Safer::HashProtocol::Compound
|
218
|
+
##
|
219
|
+
# The description for this object will be the descriptions for each
|
220
|
+
# argument, separated by " AND ".
|
221
|
+
def initialize(*list)
|
222
|
+
super(" AND ", *list)
|
223
|
+
end # Safer::HashProtocol::All#initialize
|
224
|
+
|
225
|
+
##
|
226
|
+
# Check if all elements from the +list+ argument to initialize match
|
227
|
+
# Hash h.
|
228
|
+
def ===(h)
|
229
|
+
self.list.all? do |el| el === h end
|
230
|
+
end # Safer::HashProtocol::All#===
|
231
|
+
|
232
|
+
##
|
233
|
+
# Check if all elements from the +list+ argument to initialize match
|
234
|
+
# Hash h. If they do, +remaining+ will be updated to remove all matching
|
235
|
+
# elements from +list+. If not all elements match, +remaining+ will be
|
236
|
+
# unmodified.
|
237
|
+
def match(h, remaining)
|
238
|
+
nremaining = remaining.dup
|
239
|
+
rval = self.list.all? do |el| el.match(h, nremaining) end
|
240
|
+
if rval
|
241
|
+
remaining.update(nremaining)
|
242
|
+
end
|
243
|
+
rval
|
244
|
+
end # Safer::HashProtocol::All#match
|
245
|
+
end # Safer::HashProtocol::All
|
246
|
+
|
247
|
+
##
|
248
|
+
# Check that a base Safer::HashProtocol object does NOT match a Hash.
|
249
|
+
# That is, invert the match of a base HashProtocol object.
|
250
|
+
class Not < Safer::HashProtocol::Single
|
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
|
256
|
+
|
257
|
+
##
|
258
|
+
# Check that the base object does NOT match the hash.
|
259
|
+
def ===(h)
|
260
|
+
! (self.base === h)
|
261
|
+
end # Safer::HashProtocol::Not#===
|
262
|
+
|
263
|
+
##
|
264
|
+
# Check that the base object does NOT match the hash. Does not update
|
265
|
+
# +remaining+ under any circumstance.
|
266
|
+
def match(h, remaining)
|
267
|
+
self === h
|
268
|
+
end # Safer::HashProtocol::Not#match
|
269
|
+
end # Safer::HashProtocol::Not
|
270
|
+
|
271
|
+
##
|
272
|
+
# Check that the Hash only contains elements that match the base
|
273
|
+
# Safer::HashProtocol object.
|
274
|
+
class Only < Safer::HashProtocol::Single
|
275
|
+
##
|
276
|
+
# The description for this object will be "ONLY #{base.description}".
|
277
|
+
def initialize(base)
|
278
|
+
super("ONLY ", base)
|
279
|
+
end
|
280
|
+
##
|
281
|
+
# Check that all keys in a hash are matched by the base HashProtocol
|
282
|
+
# object.
|
283
|
+
def ===(h)
|
284
|
+
remaining = h.dup
|
285
|
+
if self.base.match(h, remaining)
|
286
|
+
remaining.empty?
|
287
|
+
else
|
288
|
+
false
|
289
|
+
end
|
290
|
+
end
|
291
|
+
##
|
292
|
+
# Check that all keys in a hash are matched by the base HashProtocol
|
293
|
+
# object. If they are, then empty +remaining+.
|
294
|
+
def match(h, remaining)
|
295
|
+
if self === h
|
296
|
+
remaining.clear
|
297
|
+
true
|
298
|
+
else
|
299
|
+
false
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end # Safer::HashProtocol::Only
|
303
|
+
end # Safer::HashProtocol
|
304
|
+
end # Safer
|
data/lib/safer/ivar.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
class Safer
|
2
2
|
##
|
3
3
|
# Create accessor functions for instance variables, in which the accessor
|
4
4
|
# function is prefixed with the name of the class in which the instance
|
@@ -88,7 +88,7 @@ module Safer
|
|
88
88
|
# search for when determining the set of instance variables defined by a
|
89
89
|
# class.
|
90
90
|
#
|
91
|
-
|
91
|
+
class IVar
|
92
92
|
|
93
93
|
##
|
94
94
|
# Given a Class object, derive the prefix string for the accessor functions
|
data/lib/safer/protocol.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'safer/ivar'
|
2
2
|
|
3
|
-
|
4
|
-
##
|
3
|
+
class Safer
|
4
|
+
##
|
5
5
|
# Named set of class- and instance- methods, with associated numbers of
|
6
6
|
# arguments. Call +create_from_class+ or +create_from_instance+ to create a
|
7
7
|
# new +Protocol+ object. Call +class_conforms?+ or +instance_conforms?+
|
@@ -234,7 +234,7 @@ module Safer
|
|
234
234
|
end # Safer::Protocol::Signature.create
|
235
235
|
end # Safer::Protocol::Signature
|
236
236
|
|
237
|
-
##
|
237
|
+
##
|
238
238
|
# Error generated when a class does not conform to a protocol signature.
|
239
239
|
class Error < StandardError
|
240
240
|
Safer::IVar.instance_variable(self, :error_object)
|
@@ -332,10 +332,38 @@ module Safer
|
|
332
332
|
end # Safer::Protocol::Error::InstanceError
|
333
333
|
end # Safer::Protocol::Error
|
334
334
|
|
335
|
+
##
|
336
|
+
# Object provides an +===+ operator, which will match when the class
|
337
|
+
# conforms to the protocol. Instances are not created directly by clients,
|
338
|
+
# but instances can be retrieved through
|
339
|
+
# Safer::Protocol#match_class and Safer::Protocol#match_instance.
|
340
|
+
class Match
|
341
|
+
Safer::IVar.instance_variable(self, :method)
|
342
|
+
##
|
343
|
+
# [<tt>method</tt>] Method to call from owning Protocol to verify that
|
344
|
+
# an object matches an interface.
|
345
|
+
def initialize(method)
|
346
|
+
self.safer_protocol_match__method = method
|
347
|
+
end # Safer::Protocol::Match#initialize
|
348
|
+
|
349
|
+
##
|
350
|
+
# Returns true if the object conforms to the protocol, and false if
|
351
|
+
# it does not.
|
352
|
+
def ===(obj)
|
353
|
+
violations = self.safer_protocol_match__method.call(obj)
|
354
|
+
if violations
|
355
|
+
false
|
356
|
+
else
|
357
|
+
true
|
358
|
+
end
|
359
|
+
end # Safer::Protocol::Match#===
|
360
|
+
end # Safer::Protocol::Match
|
335
361
|
|
336
362
|
Safer::IVar.instance_variable(self, :name)
|
337
363
|
Safer::IVar.instance_variable(self, :class_signature)
|
338
364
|
Safer::IVar.instance_variable(self, :instance_signature)
|
365
|
+
Safer::IVar.instance_variable(self, :match_class)
|
366
|
+
Safer::IVar.instance_variable(self, :match_instance)
|
339
367
|
|
340
368
|
##
|
341
369
|
# :attr_reader: name
|
@@ -352,6 +380,16 @@ module Safer
|
|
352
380
|
# Signatures of required instance methods for objects implementing this
|
353
381
|
# protocol.
|
354
382
|
Safer::IVar.export_reader(self, :instance_signature)
|
383
|
+
##
|
384
|
+
# :attr_reader: match_class
|
385
|
+
# Object provides === operation matching when #violations_from_class would
|
386
|
+
# return no violations.
|
387
|
+
Safer::IVar.export_reader(self, :match_class)
|
388
|
+
##
|
389
|
+
# :attr_reader: match_instance
|
390
|
+
# Object provides === operation matching when #violations_from_instance
|
391
|
+
# would return no violations.
|
392
|
+
Safer::IVar.export_reader(self, :match_instance)
|
355
393
|
|
356
394
|
##
|
357
395
|
# Create a +Protocol+ object.
|
@@ -363,6 +401,8 @@ module Safer
|
|
363
401
|
self.safer_protocol__name = name
|
364
402
|
self.safer_protocol__class_signature = class_signature
|
365
403
|
self.safer_protocol__instance_signature = instance_signature
|
404
|
+
self.safer_protocol__match_class = Safer::Protocol::Match.new(self.method(:violations_from_class))
|
405
|
+
self.safer_protocol__match_instance = Safer::Protocol::Match.new(self.method(:violations_from_instance))
|
366
406
|
end # Safer::Protocol#initialize
|
367
407
|
|
368
408
|
##
|
data/lib/safer.rb
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'safer/hashprotocol'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class TC_SaferHashProtocol < Test::Unit::TestCase
|
5
|
+
SHP = Safer::HashProtocol
|
6
|
+
def setup
|
7
|
+
@any_hash = { :foo => nil }
|
8
|
+
@all_hash = { :foo => nil, :bar => nil, :baz => nil }
|
9
|
+
@fail_hash = { :blubby => nil }
|
10
|
+
@has_key = SHP::HasKey.create(*@all_hash.keys)
|
11
|
+
end
|
12
|
+
def teardown
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_any
|
16
|
+
any = SHP::Any.new(*@has_key)
|
17
|
+
not_any = SHP::Not.new(any)
|
18
|
+
assert(any === @any_hash)
|
19
|
+
assert(! (not_any === @any_hash))
|
20
|
+
assert(any === @all_hash)
|
21
|
+
assert(! (not_any === @all_hash))
|
22
|
+
assert(! (any === @fail_hash))
|
23
|
+
assert(not_any === @fail_hash)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_all
|
27
|
+
all = SHP::All.new(*@has_key)
|
28
|
+
not_all = SHP::Not.new(all)
|
29
|
+
|
30
|
+
assert(! (all === @any_hash))
|
31
|
+
assert(not_all === @any_hash)
|
32
|
+
assert(all === @all_hash)
|
33
|
+
assert(! (not_all === @all_hash))
|
34
|
+
assert(! (all === @fail_hash))
|
35
|
+
assert(not_all === @fail_hash)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_only
|
39
|
+
any = SHP::Any.new(*@has_key)
|
40
|
+
only = SHP::Only.new(any)
|
41
|
+
all_plus = @all_hash.merge(@fail_hash)
|
42
|
+
assert(only === @all_hash)
|
43
|
+
assert(any === @all_hash)
|
44
|
+
assert(! (only === all_plus))
|
45
|
+
assert(any === all_plus)
|
46
|
+
end
|
47
|
+
end
|
data/test/test_safer_protocol.rb
CHANGED
@@ -60,6 +60,7 @@ class TC_SaferProtocol < Test::Unit::TestCase
|
|
60
60
|
:conforms => :instance_conforms?,
|
61
61
|
:violations => :violations_from_instance,
|
62
62
|
:get_object => proc do |obj| obj ; end,
|
63
|
+
:match => :match_instance,
|
63
64
|
:error_type => Safer::Protocol::Error::InstanceError
|
64
65
|
}
|
65
66
|
else
|
@@ -67,6 +68,7 @@ class TC_SaferProtocol < Test::Unit::TestCase
|
|
67
68
|
:conforms => :class_conforms?,
|
68
69
|
:violations => :violations_from_class,
|
69
70
|
:get_object => proc do |obj| obj.class ; end,
|
71
|
+
:match => :match_class,
|
70
72
|
:error_type => Safer::Protocol::Error::ClassError
|
71
73
|
}
|
72
74
|
end
|
@@ -162,4 +164,30 @@ class TC_SaferProtocol < Test::Unit::TestCase
|
|
162
164
|
run_classInstanceError(false)
|
163
165
|
run_classInstanceError(true)
|
164
166
|
end
|
167
|
+
def run_matchCase(methods, protocol, instance)
|
168
|
+
case methods[:get_object].call(instance)
|
169
|
+
when protocol.send(methods[:match])
|
170
|
+
true
|
171
|
+
else
|
172
|
+
false
|
173
|
+
end
|
174
|
+
end
|
175
|
+
def run_match(useinstance)
|
176
|
+
methods = get_methods(useinstance)
|
177
|
+
assert_same(run_matchCase(methods, @class_protocol, @class_instance), true)
|
178
|
+
assert_same(run_matchCase(methods, @class_protocol, @instance_instance), false)
|
179
|
+
assert_same(run_matchCase(methods, @class_protocol, @both_instance), true)
|
180
|
+
|
181
|
+
assert_same(run_matchCase(methods, @instance_protocol, @class_instance), false)
|
182
|
+
assert_same(run_matchCase(methods, @instance_protocol, @instance_instance), true)
|
183
|
+
assert_same(run_matchCase(methods, @instance_protocol, @both_instance), true)
|
184
|
+
|
185
|
+
assert_same(run_matchCase(methods, @both_protocol, @class_instance), false)
|
186
|
+
assert_same(run_matchCase(methods, @both_protocol, @instance_instance), false)
|
187
|
+
assert_same(run_matchCase(methods, @both_protocol, @both_instance), true)
|
188
|
+
end
|
189
|
+
def test_match
|
190
|
+
run_match(false)
|
191
|
+
run_match(true)
|
192
|
+
end
|
165
193
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Aidan Cully
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-11-20 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -52,15 +52,18 @@ dependencies:
|
|
52
52
|
version_requirements: *id002
|
53
53
|
description: |-
|
54
54
|
Safer is an umbrella library, with components designed to make it simple to
|
55
|
-
verify and improve the safety of your ruby code. There are at present
|
55
|
+
verify and improve the safety of your ruby code. There are at present three
|
56
56
|
modules under the safer umbrella:
|
57
57
|
|
58
|
-
[<tt>Safer::IVar</tt>]
|
59
|
-
|
60
|
-
[<tt>Safer::Protocol</tt>]
|
61
|
-
|
62
|
-
|
63
|
-
|
58
|
+
[<tt>Safer::IVar</tt>] generates specially-named accessor functions
|
59
|
+
for class instance variables.
|
60
|
+
[<tt>Safer::Protocol</tt>] is used to provide a ruby analogue to
|
61
|
+
Objective-C Protocols (which are similar to
|
62
|
+
Java interfaces, but do not require
|
63
|
+
inheritance).
|
64
|
+
[<tt>Safer::HashProtocol</tt>] verifies that a Hash keys follow a defined
|
65
|
+
format. Intended to help use of Hash objects as
|
66
|
+
keyword parameters.
|
64
67
|
email:
|
65
68
|
- aidan@panix.com
|
66
69
|
executables: []
|
@@ -80,8 +83,10 @@ files:
|
|
80
83
|
- lib/safer.rb
|
81
84
|
- lib/safer/ivar.rb
|
82
85
|
- lib/safer/protocol.rb
|
86
|
+
- lib/safer/hashprotocol.rb
|
83
87
|
- test/test_safer_ivar.rb
|
84
88
|
- test/test_safer_protocol.rb
|
89
|
+
- test/test_safer_hashprotocol.rb
|
85
90
|
has_rdoc: true
|
86
91
|
homepage: http://safer.rubyforge.org
|
87
92
|
licenses: []
|
@@ -118,5 +123,6 @@ signing_key:
|
|
118
123
|
specification_version: 3
|
119
124
|
summary: Safer is an umbrella library, with components designed to make it simple to verify and improve the safety of your ruby code
|
120
125
|
test_files:
|
126
|
+
- test/test_safer_hashprotocol.rb
|
121
127
|
- test/test_safer_ivar.rb
|
122
128
|
- test/test_safer_protocol.rb
|