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.
@@ -1,502 +1,499 @@
1
1
  require 'safer/ivar'
2
2
 
3
- class Safer
3
+ ##
4
+ # Named set of class- and instance- methods, with associated numbers of
5
+ # arguments. Call +create_from_class+ or +create_from_instance+ to create a
6
+ # new +Protocol+ object. Call +class_conforms?+ or +instance_conforms?+
7
+ # to verify that a Class or instance of the class (respectively) conforms to
8
+ # a protocol.
9
+ #
10
+ # ==Usage
11
+ # In the example, we define a class with a single instance variable,
12
+ # initialized from an argument to #initialize. We must verify that the
13
+ # object corresponding to this argument implements a particular protocol.
14
+ #
15
+ # class YourClass
16
+ # # Define a class or module containing all the class- and instance-
17
+ # # methods that you require from objects passed into your methods. Here,
18
+ # # we expect the +Class+ object to implement a +foo+ method that takes
19
+ # # three arguments, and for instances to implement a +bar+ method that
20
+ # # takes one required argument, followed by any number of optional
21
+ # # arguments:
22
+ # class FooInterface
23
+ # # Note that we have a good place to document what these interfaces
24
+ # # mean, in standard RDoc fashion.
25
+ # def self.foo(arg1, arg2, arg3)
26
+ # end
27
+ # def bar(arg1, *rest)
28
+ # end
29
+ # end
30
+ #
31
+ # # Derive a Safer::Protocol object from this class:
32
+ # FOOPROTOCOL = Safer::Protocol.create_from_class(FooProtocol)
33
+ # # Note: we could also do this step as follows:
34
+ # # FOOPROTOCOL = Safer::Protocol.create_from_instance(FooProtocol.new)
35
+ #
36
+ # # Our #initialize method takes an object that should conform to
37
+ # # FOOPROTOCOL.
38
+ # def initialize(obj)
39
+ # # verify that 'obj' conforms to FOOPROTOCOL. If it does not, a
40
+ # # Safer::Protocol::Error::InstanceError exception will be raised.
41
+ # FOOPROTOCOL.instance_conforms?(obj)
42
+ # @obj = obj
43
+ # end
44
+ #
45
+ # # Now, functions can assume that @obj implements FOOPROTOCOL.
46
+ # def quux
47
+ # @obj.class.foo(self, 2, 3)
48
+ # @obj.bar(@obj)
49
+ # end
50
+ # # including client functions.
51
+ # attr_reader :obj
52
+ # end
53
+ #
54
+ # class Foo
55
+ # # implements YourClass::FOOPROTOCOL
56
+ # def self.foo(arg1, arg2, arg3)
57
+ # return arg1 + arg2 + arg3
58
+ # end
59
+ # def bar(arg1, *rest)
60
+ # "#{arg1.class.to_s}:#{rest.inspect}"
61
+ # end
62
+ # end
63
+ # # should succeed.
64
+ # a = YourClass.new(Foo.new)
65
+ # # should raise a Safer::Protocol::Error::InstanceError.
66
+ # b = YourClass.new(15)
67
+ #
68
+ # ==Rationale
69
+ # It is often the case that we expect objects passed to our methods to
70
+ # follow some usage protocol. There are two ways this is usually accomplished
71
+ # in Ruby:
72
+ #
73
+ # 1. We can ensure that objects are of a particular type, by calling
74
+ # Object.kind_of? and related functions, disabling features or signaling
75
+ # error when the object is of the wrong type. If the object is of a
76
+ # particular type, then we can reasonably expect that the object will
77
+ # conform to that type's interface.
78
+ # 2. We can verify that objects respond to desired interfaces using
79
+ # Object#respond_to? and related functions, disabling features or
80
+ # signaling error when these objects do not support certain interfaces.
81
+ #
82
+ # The first method cannot always be used, as there exist circumstances in
83
+ # which the inheritance hierarchy cannot (or should not be used to) predict
84
+ # if an object can be used in a particular way. (For example, the object
85
+ # hierarchy does not determine if an object is serializable -- there is no
86
+ # base class for which all sub-classes are serializable, and only sub-classes
87
+ # are serializable.) So we must sometimes rely on directly verifying that an
88
+ # object supports a set of interfaces.
89
+ #
90
+ # Safer::Protocol is intended to improve the maintainability of code that
91
+ # must directly verify that objects implement desired interfaces. A Protocol
92
+ # object is derived from a class definition that includes the set of methods
93
+ # for which the Protocol should test, and provides a simple means of testing
94
+ # whether another class object (or instance objects) implement that set of
95
+ # methods. If the method set is not fully supported, a report object
96
+ # (Safer::Protocol::Error) is generated describing the differences between
97
+ # the object under test and the Protocol's requirements.
98
+ #
99
+ # Safer::Protocol is also intended to make it easier to document the
100
+ # interfaces required from a Protocol-implementing object. Protocol objects
101
+ # are derived from Class objects, so one can document the interfaces required
102
+ # by the protocol by documenting the Class definition.
103
+ class Safer::Protocol
4
104
  ##
5
- # Named set of class- and instance- methods, with associated numbers of
6
- # arguments. Call +create_from_class+ or +create_from_instance+ to create a
7
- # new +Protocol+ object. Call +class_conforms?+ or +instance_conforms?+
8
- # to verify that a Class or instance of the class (respectively) conforms to
9
- # a protocol.
10
- #
11
- # ==Usage
12
- # In the example, we define a class with a single instance variable,
13
- # initialized from an argument to #initialize. We must verify that the
14
- # object corresponding to this argument implements a particular protocol.
15
- #
16
- # class YourClass
17
- # # Define a class or module containing all the class- and instance-
18
- # # methods that you require from objects passed into your methods. Here,
19
- # # we expect the +Class+ object to implement a +foo+ method that takes
20
- # # three arguments, and for instances to implement a +bar+ method that
21
- # # takes one required argument, followed by any number of optional
22
- # # arguments:
23
- # class FooInterface
24
- # # Note that we have a good place to document what these interfaces
25
- # # mean, in standard RDoc fashion.
26
- # def self.foo(arg1, arg2, arg3)
27
- # end
28
- # def bar(arg1, *rest)
29
- # end
30
- # end
31
- #
32
- # # Derive a Safer::Protocol object from this class:
33
- # FOOPROTOCOL = Safer::Protocol.create_from_class(FooProtocol)
34
- # # Note: we could also do this step as follows:
35
- # # FOOPROTOCOL = Safer::Protocol.create_from_instance(FooProtocol.new)
36
- #
37
- # # Our #initialize method takes an object that should conform to
38
- # # FOOPROTOCOL.
39
- # def initialize(obj)
40
- # # verify that 'obj' conforms to FOOPROTOCOL. If it does not, a
41
- # # Safer::Protocol::Error::InstanceError exception will be raised.
42
- # FOOPROTOCOL.instance_conforms?(obj)
43
- # @obj = obj
44
- # end
45
- #
46
- # # Now, functions can assume that @obj implements FOOPROTOCOL.
47
- # def quux
48
- # @obj.class.foo(self, 2, 3)
49
- # @obj.bar(@obj)
50
- # end
51
- # # including client functions.
52
- # attr_reader :obj
53
- # end
54
- #
55
- # class Foo
56
- # # implements YourClass::FOOPROTOCOL
57
- # def self.foo(arg1, arg2, arg3)
58
- # return arg1 + arg2 + arg3
59
- # end
60
- # def bar(arg1, *rest)
61
- # "#{arg1.class.to_s}:#{rest.inspect}"
62
- # end
63
- # end
64
- # # should succeed.
65
- # a = YourClass.new(Foo.new)
66
- # # should raise a Safer::Protocol::Error::InstanceError.
67
- # b = YourClass.new(15)
68
- #
69
- # ==Rationale
70
- # It is often the case that we expect objects passed to our methods to
71
- # follow some usage protocol. There are two ways this is usually accomplished
72
- # in Ruby:
73
- #
74
- # 1. We can ensure that objects are of a particular type, by calling
75
- # Object.kind_of? and related functions, disabling features or signaling
76
- # error when the object is of the wrong type. If the object is of a
77
- # particular type, then we can reasonably expect that the object will
78
- # conform to that type's interface.
79
- # 2. We can verify that objects respond to desired interfaces using
80
- # Object#respond_to? and related functions, disabling features or
81
- # signaling error when these objects do not support certain interfaces.
82
- #
83
- # The first method cannot always be used, as there exist circumstances in
84
- # which the inheritance hierarchy cannot (or should not be used to) predict
85
- # if an object can be used in a particular way. (For example, the object
86
- # hierarchy does not determine if an object is serializable -- there is no
87
- # base class for which all sub-classes are serializable, and only sub-classes
88
- # are serializable.) So we must sometimes rely on directly verifying that an
89
- # object supports a set of interfaces.
90
- #
91
- # Safer::Protocol is intended to improve the maintainability of code that
92
- # must directly verify that objects implement desired interfaces. A Protocol
93
- # object is derived from a class definition that includes the set of methods
94
- # for which the Protocol should test, and provides a simple means of testing
95
- # whether another class object (or instance objects) implement that set of
96
- # methods. If the method set is not fully supported, a report object
97
- # (Safer::Protocol::Error) is generated describing the differences between
98
- # the object under test and the Protocol's requirements.
99
- #
100
- # Safer::Protocol is also intended to make it easier to document the
101
- # interfaces required from a Protocol-implementing object. Protocol objects
102
- # are derived from Class objects, so one can document the interfaces required
103
- # by the protocol by documenting the Class definition.
104
- class Protocol
105
-
106
- ##
107
- # Utility function used during Protocol operations. Given an +Array+,
108
- # construct a +Hash+ with the values in the +Array+ as keys. If no block
109
- # is provided, then the values for all keys in the +Hash+ will be +true+.
110
- # If a block is provided, the block is called for each value in the
111
- # +Array+, and its return value will be stored as the value for that
112
- # element in the +Hash+. If the block returns +nil+, then the element will
113
- # not be stored in the resulting +Hash+.
114
- def self._array_to_table(array, &block)
115
- if ! block
116
- block = proc do |h, el| true ; end
117
- end
118
- array.inject(Hash.new) do |h, el|
119
- newval = block.call(h, el)
120
- if newval
121
- h[el] = newval
122
- end
123
- h
105
+ # Utility function used during Protocol operations. Given an +Array+,
106
+ # construct a +Hash+ with the values in the +Array+ as keys. If no block
107
+ # is provided, then the values for all keys in the +Hash+ will be +true+.
108
+ # If a block is provided, the block is called for each value in the
109
+ # +Array+, and its return value will be stored as the value for that
110
+ # element in the +Hash+. If the block returns +nil+, then the element will
111
+ # not be stored in the resulting +Hash+.
112
+ def self._array_to_table(array, &block)
113
+ if ! block
114
+ block = proc do |h, el| true ; end
115
+ end
116
+ array.inject(Hash.new) do |h, el|
117
+ newval = block.call(h, el)
118
+ if newval
119
+ h[el] = newval
124
120
  end
125
- end # Safer::Protocol._array_to_table
121
+ h
122
+ end
123
+ end # Safer::Protocol._array_to_table
126
124
 
127
- ##
128
- # Describes a set of methods that should be implemented by an object, but
129
- # are not implemented. The Protocol::Error object will contain two
130
- # Protocol::Violation objects: one for class-method protocol
131
- # violations, and one for instance-method protocol violations.
132
- class Violation
133
- Safer::IVar.instance_variable(self, :table)
134
- Safer::IVar.instance_variable(self, :report)
125
+ ##
126
+ # Describes a set of methods that should be implemented by an object, but
127
+ # are not implemented. The Protocol::Error object will contain two
128
+ # Protocol::Violation objects: one for class-method protocol
129
+ # violations, and one for instance-method protocol violations.
130
+ class Violation
131
+ Safer::IVar.instance_variable(self, :table)
132
+ Safer::IVar.instance_variable(self, :report)
135
133
 
136
- ##
137
- # :attr_reader: table
138
- # Hash describing method violations. Keys are the names of methods
139
- # containing errors. If a method was unimplemented in the class, the
140
- # value for that method will be +true+. If a method was implemented, but
141
- # had a different arity (number of required / optional arguments) than
142
- # the protocol, the value for that method will be the arity discovered in
143
- # the object. (The desired arity can be accessed through the
144
- # Protocol::Signature for that method.)
145
- Safer::IVar.export_reader(self, :table)
134
+ ##
135
+ # :attr_reader: table
136
+ # Hash describing method violations. Keys are the names of methods
137
+ # containing errors. If a method was unimplemented in the class, the
138
+ # value for that method will be +true+. If a method was implemented, but
139
+ # had a different arity (number of required / optional arguments) than
140
+ # the protocol, the value for that method will be the arity discovered in
141
+ # the object. (The desired arity can be accessed through the
142
+ # Protocol::Signature for that method.)
143
+ Safer::IVar.export_reader(self, :table)
146
144
 
147
- ##
148
- # :attr_reader: report
149
- # String describing the errors from +table+ in a more human-readable
150
- # fashion.
151
- Safer::IVar.export_reader(self, :report)
145
+ ##
146
+ # :attr_reader: report
147
+ # String describing the errors from +table+ in a more human-readable
148
+ # fashion.
149
+ Safer::IVar.export_reader(self, :report)
152
150
 
153
- ##
154
- # Create a new Protocol::Violation object.
155
- def initialize(table, report)
156
- self.safer_protocol_violation__table = table
157
- self.safer_protocol_violation__report = report
158
- end # Safer::Protocol::Violation#initialize
159
- ##
160
- # Compare two Protocol::Violation objects for content equality.
161
- def ==(other_object)
162
- self.class == other_object.class &&
163
- self.safer_protocol_violation__table == other_object.safer_protocol_violation__table
164
- end # Safer::Protocol::Violation#==
165
- end # Safer::Protocol::Violation
151
+ ##
152
+ # Create a new Protocol::Violation object.
153
+ def initialize(table, report)
154
+ self.safer_protocol_violation__table = table
155
+ self.safer_protocol_violation__report = report
156
+ end # Safer::Protocol::Violation#initialize
157
+ ##
158
+ # Compare two Protocol::Violation objects for content equality.
159
+ def ==(other_object)
160
+ self.class == other_object.class &&
161
+ self.safer_protocol_violation__table == other_object.safer_protocol_violation__table
162
+ end # Safer::Protocol::Violation#==
163
+ end # Safer::Protocol::Violation
166
164
 
165
+ ##
166
+ # Hash of method_name => method.arity, describing a set of methods that
167
+ # we expect to see implemented in another class. Protocol will contain
168
+ # two of these objects - one describing class methods, and one describing
169
+ # instance methods.
170
+ class Signature
171
+ Safer::IVar.instance_variable(self, :table)
167
172
  ##
168
- # Hash of method_name => method.arity, describing a set of methods that
169
- # we expect to see implemented in another class. Protocol will contain
170
- # two of these objects - one describing class methods, and one describing
171
- # instance methods.
172
- class Signature
173
- Safer::IVar.instance_variable(self, :table)
174
- ##
175
- # :attr_reader: table
176
- # +Hash+ object, in which the keys are the names of methods, and the
177
- # value for a key is the required arity of that method.
178
- Safer::IVar.export_reader(self, :table)
173
+ # :attr_reader: table
174
+ # +Hash+ object, in which the keys are the names of methods, and the
175
+ # value for a key is the required arity of that method.
176
+ Safer::IVar.export_reader(self, :table)
179
177
 
180
- ##
181
- # Create a Protocol::Signature object.
182
- def initialize(table)
183
- self.safer_protocol_signature__table = table
184
- end # Safer::Protocol::Signature#initialize
185
- ##
186
- # Compare two Protocol::Signature objects for content equality.
187
- def ==(other_object)
188
- self.class == other_object.class &&
189
- self.safer_protocol_signature__table == other_object.safer_protocol_signature__table
190
- end # Safer::Protocol::Signature#==
178
+ ##
179
+ # Create a Protocol::Signature object.
180
+ def initialize(table)
181
+ self.safer_protocol_signature__table = table
182
+ end # Safer::Protocol::Signature#initialize
183
+ ##
184
+ # Compare two Protocol::Signature objects for content equality.
185
+ def ==(other_object)
186
+ self.class == other_object.class &&
187
+ self.safer_protocol_signature__table == other_object.safer_protocol_signature__table
188
+ end # Safer::Protocol::Signature#==
191
189
 
192
- ##
193
- # Given an object, and the name of the method for getting a named method
194
- # from that object, check that the object implements this function
195
- # signature.
196
- def find_violations(object, get_method)
197
- report = ''
198
- have_error = false
199
- error_table = Hash.new
200
- self.safer_protocol_signature__table.each_pair do |name, arity|
201
- begin
202
- m = object.send(get_method, name)
203
- if m.arity != arity
204
- report += "#{get_method}(#{name}) arity #{m.arity} != desired #{arity}.\n"
205
- error_table[name] = m.arity
206
- have_error = true
207
- end
208
- rescue NameError => e
209
- report += "#{get_method}(#{name}) unimplemented.\n"
210
- error_table[name] = true
190
+ ##
191
+ # Given an object, and the name of the method for getting a named method
192
+ # from that object, check that the object implements this function
193
+ # signature.
194
+ def find_violations(object, get_method)
195
+ report = ''
196
+ have_error = false
197
+ error_table = Hash.new
198
+ self.safer_protocol_signature__table.each_pair do |name, arity|
199
+ begin
200
+ m = object.send(get_method, name)
201
+ if m.arity != arity
202
+ report += "#{get_method}(#{name}) arity #{m.arity} != desired #{arity}.\n"
203
+ error_table[name] = m.arity
211
204
  have_error = true
212
205
  end
206
+ rescue NameError => e
207
+ report += "#{get_method}(#{name}) unimplemented.\n"
208
+ error_table[name] = true
209
+ have_error = true
213
210
  end
211
+ end
214
212
 
215
- if have_error
216
- Safer::Protocol::Violation.new(error_table, report)
217
- else
218
- nil
219
- end
220
- end # Safer::Protocol::Signature#find_violations
221
-
222
- ##
223
- # Derive a Signature from an object.
224
- def self.create(object, get_methods, get_method, &filter)
225
- method_names = object.send(get_methods)
226
- method_table = Safer::Protocol._array_to_table(method_names) do
227
- |h, method_name|
228
- if filter.call(method_name)
229
- m = object.send(get_method, method_name)
230
- m.arity
231
- end
232
- end
233
- self.new(method_table)
234
- end # Safer::Protocol::Signature.create
235
- end # Safer::Protocol::Signature
213
+ if have_error
214
+ Safer::Protocol::Violation.new(error_table, report)
215
+ else
216
+ nil
217
+ end
218
+ end # Safer::Protocol::Signature#find_violations
236
219
 
237
220
  ##
238
- # Error generated when a class does not conform to a protocol signature.
239
- class Error < StandardError
240
- Safer::IVar.instance_variable(self, :error_object)
241
- Safer::IVar.instance_variable(self, :protocol)
242
- Safer::IVar.instance_variable(self, :class_violations)
243
- Safer::IVar.instance_variable(self, :instance_violations)
244
- Safer::IVar.instance_variable(self, :report)
245
-
246
- ##
247
- # :attr_reader: error_object
248
- # Object that does not properly implement a desired method protocol.
249
- Safer::IVar.export_reader(self, :error_object)
250
- ##
251
- # :attr_reader: protocol
252
- # Protocol object describing the signature that error_object failed
253
- # to properly implement.
254
- Safer::IVar.export_reader(self, :protocol)
255
- ##
256
- # :attr_reader: class_violations
257
- # Protocol::Violation describing class-method protocol violations.
258
- Safer::IVar.export_reader(self, :class_violations)
259
- ##
260
- # :attr_reader: instance_violations
261
- # Protocol::Violation describing instance-method protocol violations.
262
- Safer::IVar.export_reader(self, :instance_violations)
263
- ##
264
- # :attr_reader: report
265
- # String for displaying human-readable error message describing protocol
266
- # violations.
267
- Safer::IVar.export_reader(self, :report)
268
-
269
-
270
- ##
271
- # Create a Safer::Protocol::Error object.
272
- # Used internally.
273
- def initialize(message, error_object, protocol, class_violations, instance_violations)
274
- super(message)
275
- self.safer_protocol_error__error_object = error_object
276
- self.safer_protocol_error__protocol = protocol
277
- self.safer_protocol_error__class_violations = class_violations
278
- self.safer_protocol_error__instance_violations = instance_violations
279
- report = ''
280
- if class_violations
281
- report +=
282
- "Class method errors:\n" +
283
- self.safer_protocol_error__class_violations.report
284
- if instance_violations
285
- report += "\n\n"
286
- end
221
+ # Derive a Signature from an object.
222
+ def self.create(object, get_methods, get_method, &filter)
223
+ method_names = object.send(get_methods)
224
+ method_table = Safer::Protocol._array_to_table(method_names) do
225
+ |h, method_name|
226
+ if filter.call(method_name)
227
+ m = object.send(get_method, method_name)
228
+ m.arity
287
229
  end
288
- if instance_violations
289
- report +=
290
- "Instance method errors:\n" +
291
- self.safer_protocol_error__instance_violations.report
292
- end
293
- self.safer_protocol_error__report = report
294
- end # Safer::Protocol::Error#initialize
230
+ end
231
+ self.new(method_table)
232
+ end # Safer::Protocol::Signature.create
233
+ end # Safer::Protocol::Signature
295
234
 
296
- ##
297
- # Compare two Safer::Protocol::Error objects for content equality.
298
- def ==(other_object)
299
- self.class == other_object.class &&
300
- self.error_object == other_object.error_object &&
301
- self.protocol == other_object.protocol &&
302
- self.class_violations == other_object.class_violations &&
303
- self.instance_violations == other_object.instance_violations
304
- end # Safer::Protocol::Error#==
235
+ ##
236
+ # Error generated when a class does not conform to a protocol signature.
237
+ class Error < StandardError
238
+ Safer::IVar.instance_variable(self, :error_object)
239
+ Safer::IVar.instance_variable(self, :protocol)
240
+ Safer::IVar.instance_variable(self, :class_violations)
241
+ Safer::IVar.instance_variable(self, :instance_violations)
242
+ Safer::IVar.instance_variable(self, :report)
305
243
 
306
- ##
307
- # Error generated when a Class object does not conform to a desired
308
- # protocol.
309
- class ClassError < Safer::Protocol::Error
310
- ##
311
- # Create a Safer::Protocol::Error::ClassError object.
312
- def initialize(error_object, protocol, class_violations, instance_violations)
313
- super(
314
- "Class #{error_object} does not implement protocol" +
315
- "#{protocol.name}.",
316
- error_object, protocol, class_violations, instance_violations)
317
- end # Safer::Protocol::Error::ClassError#initialize
318
- end # Safer::Protocol::Error::ClassError
244
+ ##
245
+ # :attr_reader: error_object
246
+ # Object that does not properly implement a desired method protocol.
247
+ Safer::IVar.export_reader(self, :error_object)
248
+ ##
249
+ # :attr_reader: protocol
250
+ # Protocol object describing the signature that error_object failed
251
+ # to properly implement.
252
+ Safer::IVar.export_reader(self, :protocol)
253
+ ##
254
+ # :attr_reader: class_violations
255
+ # Protocol::Violation describing class-method protocol violations.
256
+ Safer::IVar.export_reader(self, :class_violations)
257
+ ##
258
+ # :attr_reader: instance_violations
259
+ # Protocol::Violation describing instance-method protocol violations.
260
+ Safer::IVar.export_reader(self, :instance_violations)
261
+ ##
262
+ # :attr_reader: report
263
+ # String for displaying human-readable error message describing protocol
264
+ # violations.
265
+ Safer::IVar.export_reader(self, :report)
319
266
 
320
- ##
321
- # Error generated when an instance object does not conform to a desired
322
- # protocol.
323
- class InstanceError < Safer::Protocol::Error
324
- ##
325
- # Create a Safer::Protocol::Error::InstanceError object.
326
- def initialize(error_object, protocol, class_violations, instance_violations)
327
- super(
328
- "Instance of #{error_object.class} does not implement protocol" +
329
- "#{protocol.name}.",
330
- error_object, protocol, class_violations, instance_violations)
331
- end # Safer::Protocol::Error::InstanceError#initialize
332
- end # Safer::Protocol::Error::InstanceError
333
- end # Safer::Protocol::Error
334
267
 
335
268
  ##
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
269
+ # Create a Safer::Protocol::Error object.
270
+ # Used internally.
271
+ def initialize(message, error_object, protocol, class_violations, instance_violations)
272
+ super(message)
273
+ self.safer_protocol_error__error_object = error_object
274
+ self.safer_protocol_error__protocol = protocol
275
+ self.safer_protocol_error__class_violations = class_violations
276
+ self.safer_protocol_error__instance_violations = instance_violations
277
+ report = ''
278
+ if class_violations
279
+ report +=
280
+ "Class method errors:\n" +
281
+ self.safer_protocol_error__class_violations.report
282
+ if instance_violations
283
+ report += "\n\n"
358
284
  end
359
- end # Safer::Protocol::Match#===
360
- end # Safer::Protocol::Match
361
-
362
- Safer::IVar.instance_variable(self, :name)
363
- Safer::IVar.instance_variable(self, :class_signature)
364
- Safer::IVar.instance_variable(self, :instance_signature)
365
- Safer::IVar.instance_variable(self, :match_class)
366
- Safer::IVar.instance_variable(self, :match_instance)
285
+ end
286
+ if instance_violations
287
+ report +=
288
+ "Instance method errors:\n" +
289
+ self.safer_protocol_error__instance_violations.report
290
+ end
291
+ self.safer_protocol_error__report = report
292
+ end # Safer::Protocol::Error#initialize
367
293
 
368
294
  ##
369
- # :attr_reader: name
370
- # Name of this protocol signature. Typically derived from the class name
371
- # from which this signature is derived.
372
- Safer::IVar.export_reader(self, :name)
295
+ # Compare two Safer::Protocol::Error objects for content equality.
296
+ def ==(other_object)
297
+ self.class == other_object.class &&
298
+ self.error_object == other_object.error_object &&
299
+ self.protocol == other_object.protocol &&
300
+ self.class_violations == other_object.class_violations &&
301
+ self.instance_violations == other_object.instance_violations
302
+ end # Safer::Protocol::Error#==
303
+
373
304
  ##
374
- # :attr_reader: class_signature
375
- # Signatures of required class methods for objects implementing this
305
+ # Error generated when a Class object does not conform to a desired
376
306
  # protocol.
377
- Safer::IVar.export_reader(self, :class_signature)
307
+ class ClassError < Safer::Protocol::Error
308
+ ##
309
+ # Create a Safer::Protocol::Error::ClassError object.
310
+ def initialize(error_object, protocol, class_violations, instance_violations)
311
+ super(
312
+ "Class #{error_object} does not implement protocol" +
313
+ "#{protocol.name}.",
314
+ error_object, protocol, class_violations, instance_violations)
315
+ end # Safer::Protocol::Error::ClassError#initialize
316
+ end # Safer::Protocol::Error::ClassError
317
+
378
318
  ##
379
- # :attr_reader: instance_signature
380
- # Signatures of required instance methods for objects implementing this
319
+ # Error generated when an instance object does not conform to a desired
381
320
  # protocol.
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)
321
+ class InstanceError < Safer::Protocol::Error
322
+ ##
323
+ # Create a Safer::Protocol::Error::InstanceError object.
324
+ def initialize(error_object, protocol, class_violations, instance_violations)
325
+ super(
326
+ "Instance of #{error_object.class} does not implement protocol" +
327
+ "#{protocol.name}.",
328
+ error_object, protocol, class_violations, instance_violations)
329
+ end # Safer::Protocol::Error::InstanceError#initialize
330
+ end # Safer::Protocol::Error::InstanceError
331
+ end # Safer::Protocol::Error
393
332
 
333
+ ##
334
+ # Object provides an +===+ operator, which will match when the class
335
+ # conforms to the protocol. Instances are not created directly by clients,
336
+ # but instances can be retrieved through
337
+ # Safer::Protocol#match_class and Safer::Protocol#match_instance.
338
+ class Match
339
+ Safer::IVar.instance_variable(self, :method)
394
340
  ##
395
- # Create a +Protocol+ object.
396
- # Used internally.
397
- # [+name+] Value for +name+ field object.
398
- # [+class_signature+] Value for +class_signature+ field.
399
- # [+instance_signature+] Value for +instance_signature+ field.
400
- def initialize(name, class_signature, instance_signature)
401
- self.safer_protocol__name = name
402
- self.safer_protocol__class_signature = class_signature
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))
406
- end # Safer::Protocol#initialize
341
+ # [<tt>method</tt>] Method to call from owning Protocol to verify that
342
+ # an object matches an interface.
343
+ def initialize(method)
344
+ self.safer_protocol_match__method = method
345
+ end # Safer::Protocol::Match#initialize
407
346
 
408
347
  ##
409
- # Given a Class object, find the protocol methods that are not supported by
410
- # a class, or its instances.
411
- # [_returns_] If protocol violations are found, returns a
412
- # Safer::Protocol::Error::ClassError object describing the
413
- # violations. Otherwise, returns +nil+.
414
- def violations_from_class(klass)
415
- class_violations = self.safer_protocol__class_signature.find_violations(klass, :method)
416
- instance_violations = self.safer_protocol__instance_signature.find_violations(klass, :instance_method)
417
-
418
- if class_violations || instance_violations
419
- Error::ClassError.new(klass, self, class_violations, instance_violations)
348
+ # Returns true if the object conforms to the protocol, and false if
349
+ # it does not.
350
+ def ===(obj)
351
+ violations = self.safer_protocol_match__method.call(obj)
352
+ if violations
353
+ false
420
354
  else
421
- nil
355
+ true
422
356
  end
423
- end # Safer::Protocol#violations_from_class
357
+ end # Safer::Protocol::Match#===
358
+ end # Safer::Protocol::Match
424
359
 
425
- ##
426
- # Given an instance object, find the protocol methods that are not supported
427
- # by the instance, or its class.
428
- # [_returns_] If protocol violations are found, returns a
429
- # Safer::Protocol::Error::InstanceError object describing the
430
- # violations. Otherwise, returns +nil+.
431
- def violations_from_instance(instance)
432
- class_violations = self.safer_protocol__class_signature.find_violations(instance.class, :method)
433
- instance_violations = self.safer_protocol__instance_signature.find_violations(instance, :method)
434
- if class_violations || instance_violations
435
- Error::InstanceError.new(instance, self, class_violations, instance_violations)
436
- else
437
- nil
438
- end
439
- end # Safer::Protocol#violations_from_instance
360
+ Safer::IVar.instance_variable(self, :name)
361
+ Safer::IVar.instance_variable(self, :class_signature)
362
+ Safer::IVar.instance_variable(self, :instance_signature)
363
+ Safer::IVar.instance_variable(self, :match_class)
364
+ Safer::IVar.instance_variable(self, :match_instance)
440
365
 
441
- ##
442
- # Check that a Class object conforms to this protocol.
443
- def class_conforms?(klass)
444
- violations = self.violations_from_class(klass)
445
- if violations
446
- raise violations
447
- end
448
- true
449
- end # Safer::Protocol#class_conforms?
366
+ ##
367
+ # :attr_reader: name
368
+ # Name of this protocol signature. Typically derived from the class name
369
+ # from which this signature is derived.
370
+ Safer::IVar.export_reader(self, :name)
371
+ ##
372
+ # :attr_reader: class_signature
373
+ # Signatures of required class methods for objects implementing this
374
+ # protocol.
375
+ Safer::IVar.export_reader(self, :class_signature)
376
+ ##
377
+ # :attr_reader: instance_signature
378
+ # Signatures of required instance methods for objects implementing this
379
+ # protocol.
380
+ Safer::IVar.export_reader(self, :instance_signature)
381
+ ##
382
+ # :attr_reader: match_class
383
+ # Object provides === operation matching when #violations_from_class would
384
+ # return no violations.
385
+ Safer::IVar.export_reader(self, :match_class)
386
+ ##
387
+ # :attr_reader: match_instance
388
+ # Object provides === operation matching when #violations_from_instance
389
+ # would return no violations.
390
+ Safer::IVar.export_reader(self, :match_instance)
450
391
 
451
- ##
452
- # Check that an instance object conforms to this protocol.
453
- def instance_conforms?(instance)
454
- violations = self.violations_from_instance(instance)
455
- if violations
456
- raise violations
457
- end
458
- true
459
- end # Safer::Protocol#instance_conforms?
392
+ ##
393
+ # Create a +Protocol+ object.
394
+ # Used internally.
395
+ # [+name+] Value for +name+ field object.
396
+ # [+class_signature+] Value for +class_signature+ field.
397
+ # [+instance_signature+] Value for +instance_signature+ field.
398
+ def initialize(name, class_signature, instance_signature)
399
+ self.safer_protocol__name = name
400
+ self.safer_protocol__class_signature = class_signature
401
+ self.safer_protocol__instance_signature = instance_signature
402
+ self.safer_protocol__match_class = Safer::Protocol::Match.new(self.method(:violations_from_class))
403
+ self.safer_protocol__match_instance = Safer::Protocol::Match.new(self.method(:violations_from_instance))
404
+ end # Safer::Protocol#initialize
460
405
 
461
- ##
462
- # Given a +Class+ object +klass+ and a +Class+ object +base+, create a
463
- # +Protocol+ object representing the methods implemented in +klass+
464
- # that are not present in +base+.
465
- # [+klass+] +Class+ object from which to derive a +Protocol+.
466
- # [+base+] +Class+ object describing routines that will be implemented by
467
- # +klass+, but which should not be included in the returned
468
- # +Protocol+.
469
- def self.create_from_class(klass, base = Object)
470
- class_signature = Signature.create(klass, :methods, :method) do
471
- |method_name|
472
- ! Class.respond_to?(method_name)
473
- end
474
- baseinstance_table = self._array_to_table(base.instance_methods)
475
- instance_signature = Signature.create(klass, :instance_methods, :instance_method) do
476
- |method_name|
477
- ! baseinstance_table.has_key?(method_name)
478
- end
406
+ ##
407
+ # Given a Class object, find the protocol methods that are not supported by
408
+ # a class, or its instances.
409
+ # [_returns_] If protocol violations are found, returns a
410
+ # Safer::Protocol::Error::ClassError object describing the
411
+ # violations. Otherwise, returns +nil+.
412
+ def violations_from_class(klass)
413
+ class_violations = self.safer_protocol__class_signature.find_violations(klass, :method)
414
+ instance_violations = self.safer_protocol__instance_signature.find_violations(klass, :instance_method)
415
+
416
+ if class_violations || instance_violations
417
+ Error::ClassError.new(klass, self, class_violations, instance_violations)
418
+ else
419
+ nil
420
+ end
421
+ end # Safer::Protocol#violations_from_class
479
422
 
480
- self.new(klass.to_s, class_signature, instance_signature)
481
- end # Safer::Protocol.create_from_class
423
+ ##
424
+ # Given an instance object, find the protocol methods that are not supported
425
+ # by the instance, or its class.
426
+ # [_returns_] If protocol violations are found, returns a
427
+ # Safer::Protocol::Error::InstanceError object describing the
428
+ # violations. Otherwise, returns +nil+.
429
+ def violations_from_instance(instance)
430
+ class_violations = self.safer_protocol__class_signature.find_violations(instance.class, :method)
431
+ instance_violations = self.safer_protocol__instance_signature.find_violations(instance, :method)
432
+ if class_violations || instance_violations
433
+ Error::InstanceError.new(instance, self, class_violations, instance_violations)
434
+ else
435
+ nil
436
+ end
437
+ end # Safer::Protocol#violations_from_instance
482
438
 
483
- ##
484
- # Given an instance object +instance+ and a +Class+ object +baseclass+,
485
- # create a +Protocol+ object representing the methods implemented in
486
- # +klass+ that are not present in +base+.
487
- def self.create_from_instance(instance, baseclass = Object)
488
- class_signature = Signature.create(instance.class, :methods, :method) do
489
- |method_name|
490
- ! Class.method_defined?(method_name)
491
- end
492
- baseinstance_table = self._array_to_table(base.instance_methods)
493
- instance_signature = Signature.create(instance, :methods, :method) do
494
- |method_name|
495
- ! baseinstance_table.has_key?(method_name)
496
- end
439
+ ##
440
+ # Check that a Class object conforms to this protocol.
441
+ def class_conforms?(klass)
442
+ violations = self.violations_from_class(klass)
443
+ if violations
444
+ raise violations
445
+ end
446
+ true
447
+ end # Safer::Protocol#class_conforms?
448
+
449
+ ##
450
+ # Check that an instance object conforms to this protocol.
451
+ def instance_conforms?(instance)
452
+ violations = self.violations_from_instance(instance)
453
+ if violations
454
+ raise violations
455
+ end
456
+ true
457
+ end # Safer::Protocol#instance_conforms?
497
458
 
498
- self.new(instance.class.to_s, class_signature, instance_signature)
499
- end # Safer::Protocol.create_from_instance
459
+ ##
460
+ # Given a +Class+ object +klass+ and a +Class+ object +base+, create a
461
+ # +Protocol+ object representing the methods implemented in +klass+
462
+ # that are not present in +base+.
463
+ # [+klass+] +Class+ object from which to derive a +Protocol+.
464
+ # [+base+] +Class+ object describing routines that will be implemented by
465
+ # +klass+, but which should not be included in the returned
466
+ # +Protocol+.
467
+ def self.create_from_class(klass, base = Object)
468
+ class_signature = Signature.create(klass, :methods, :method) do
469
+ |method_name|
470
+ ! Class.respond_to?(method_name)
471
+ end
472
+ baseinstance_table = self._array_to_table(base.instance_methods)
473
+ instance_signature = Signature.create(klass, :instance_methods, :instance_method) do
474
+ |method_name|
475
+ ! baseinstance_table.has_key?(method_name)
476
+ end
477
+
478
+ self.new(klass.to_s, class_signature, instance_signature)
479
+ end # Safer::Protocol.create_from_class
500
480
 
501
- end # Safer::Protocol
502
- end # Safer
481
+ ##
482
+ # Given an instance object +instance+ and a +Class+ object +baseclass+,
483
+ # create a +Protocol+ object representing the methods implemented in
484
+ # +klass+ that are not present in +base+.
485
+ def self.create_from_instance(instance, baseclass = Object)
486
+ class_signature = Signature.create(instance.class, :methods, :method) do
487
+ |method_name|
488
+ ! Class.method_defined?(method_name)
489
+ end
490
+ baseinstance_table = self._array_to_table(base.instance_methods)
491
+ instance_signature = Signature.create(instance, :methods, :method) do
492
+ |method_name|
493
+ ! baseinstance_table.has_key?(method_name)
494
+ end
495
+
496
+ self.new(instance.class.to_s, class_signature, instance_signature)
497
+ end # Safer::Protocol.create_from_instance
498
+
499
+ end # Safer::Protocol