safer 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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