safer 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -2,3 +2,8 @@
2
2
 
3
3
  * First release
4
4
 
5
+ === 0.2.0 / 2010-10-10
6
+
7
+ * ClassProtocol renamed to Protocol.
8
+ * Protocol can non-violently check if a class or instance conforms.
9
+ * Added much documentation.
data/README.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  = safer
2
2
 
3
- http://safer.rubyforge.org/
3
+ http://safer.rubyforge.org
4
4
 
5
5
  == DESCRIPTION:
6
6
 
@@ -19,7 +19,7 @@ modules under the safer umbrella:
19
19
 
20
20
  * Name instance variables after the class in which they are defined. Generate
21
21
  accessor functions for these instance variables.
22
- * Define an "Protocol" class, and derive a signature from it. Verify that
22
+ * Define a "Protocol" class, and derive a signature from it. Verify that
23
23
  another class (or an instance of that class) implements the protocol
24
24
  signature.
25
25
 
@@ -53,7 +53,7 @@ modules under the safer umbrella:
53
53
 
54
54
  == INSTALL:
55
55
 
56
- * sudo gem install
56
+ * sudo gem install safer
57
57
 
58
58
  == DEVELOPERS:
59
59
 
data/Rakefile CHANGED
@@ -4,5 +4,5 @@ require 'hoe'
4
4
  Hoe.spec 'safer' do
5
5
  developer('Aidan Cully', 'aidan@panix.com')
6
6
 
7
- self.rubyforge_name = 'aidancully'
7
+ self.rubyforge_name = 'safer'
8
8
  end
data/lib/safer/ivar.rb CHANGED
@@ -3,10 +3,95 @@ module Safer
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
5
5
  # variable is defined.
6
+ #
7
+ # ==Usage
8
+ # Create one or more instance variables using instance_variable .
9
+ # For example, the following code:
10
+ # class Outer
11
+ # Safer::IVar.instance_variable(self, :variable)
12
+ # class Inner < Outer
13
+ # Safer::IVar.instance_variable(self, :variable)
14
+ # end
15
+ # end
16
+ # puts(Outer.instance_methods.grep(/__variable/).inspect)
17
+ # puts(Outer::Inner.instance_methods.grep(/__variable/).inspect)
18
+ # produces the following output:
19
+ # ["outer__variable", "outer__variable="]
20
+ # ["outer_inner__variable", "outer_inner__variable=", "outer__variable", "outer__variable="]
21
+ #
22
+ # Accessors for Safer::IVar-defined instance variables can be created using
23
+ # export_reader, export_writer, and export_accessor .
24
+ #
25
+ # ==Rationale
26
+ # Safer::IVar is intended to improve the clarity and consistency of ruby
27
+ # code in at least the following (related) ways:
28
+ #
29
+ # 1. Reducing the probability of instance variable usage errors.
30
+ # 2. Documenting the instance variables attached to a class.
31
+ #
32
+ # ===Error Reduction
33
+ # Safer::IVar should help in reducing errors in at least the following ways:
34
+ #
35
+ # * Encapsulation errors. Traditional ruby instance variables defined by one
36
+ # class are transparently accessible to all subclasses. They are not,
37
+ # however, part of the public interface to a class (unless an accessor is
38
+ # defined), which means they are not generally documented. These two
39
+ # factors create a situation in which it is quite possible that a subclass
40
+ # inadvertantly re-define an instance variable in such a way as to render
41
+ # the subclass unusable. Bugs of this sort can be very difficult to
42
+ # resolve.
43
+ #
44
+ # Because instance variables generated by Safer::IVar are prefixed with a
45
+ # string derived from the class in which the variable is defined, it is much
46
+ # less likely that developers _inadvertantly_ re-define instance variables
47
+ # in sub-classes, dramatically reducing the likelihood of this type of
48
+ # error.
49
+ #
50
+ # * Misspelling errors. For example, suppose you typically write software
51
+ # using the en-us dialect, and have an object with a +@color+ instance
52
+ # variable. A en-uk speaker then submits a patch that sets the default
53
+ # color to green:
54
+ # --- ex1.rb 2010-09-28 06:24:52.000000000 -0400
55
+ # +++ ex2.rb 2010-09-28 06:25:00.000000000 -0400
56
+ # @@ -1,6 +1,7 @@
57
+ # class Foo
58
+ # def initialize
59
+ # @size = 3
60
+ # + @colour = "green"
61
+ # end
62
+ # attr_reader :color
63
+ # end
64
+ # This code will not raise any exceptions, but its behavior does not match
65
+ # developer intent. On the other hand, using Safer::IVar
66
+ # --- ex1-safer.rb 2010-09-28 06:31:51.000000000 -0400
67
+ # +++ ex2-safer.rb 2010-09-28 06:32:08.000000000 -0400
68
+ # @@ -1,7 +1,8 @@
69
+ # class Foo
70
+ # Safer::IVar.instance_variable(self, :size, :color)
71
+ # Safer::IVar.export_reader(self, :color)
72
+ # def initialize
73
+ # self.foo__size = 3
74
+ # + self.foo__colour = "green"
75
+ # end
76
+ # end
77
+ # The new code will raise an exception at the call to Foo.new, making it
78
+ # much less likely that the error will go undetected.
79
+ #
80
+ # ===Documentation
81
+ # Traditional ruby instance variables are defined and used in an <i>ad hoc</i>
82
+ # manner. As such, there is no natural location in which the instance
83
+ # variables defined by a class can be documented, and no obvious way to
84
+ # determine the set of instance variables used in a class. Safer::IVar
85
+ # instance variables will all be associated with a single call to
86
+ # Safer::IVar.instance_variable. This provides both a natural location for
87
+ # documenting an instance variable's interpretation, as well as a string to
88
+ # search for when determining the set of instance variables defined by a
89
+ # class.
90
+ #
6
91
  module IVar
7
92
 
8
93
  ##
9
- # Given a class name, derive the prefix string for the accessor functions
94
+ # Given a Class object, derive the prefix string for the accessor functions
10
95
  # that instance_variable will create.
11
96
  def self.class_symbol_prefix(klass)
12
97
  klass.to_s.split('::').inject(nil) do |memo, el|
@@ -19,7 +104,21 @@ module Safer
19
104
  el.downcase
20
105
  end
21
106
  end
22
- end
107
+ end # Safer::IVar.class_symbol_prefix
108
+
109
+ ##
110
+ # Used internally.
111
+ # [+klass+] Class object for which to generate a symbol name.
112
+ # [+nmlist+] Array of +Symbol+ objects.
113
+ # [_return_] Array of +Symbol+ objects generated from +klass+ and each
114
+ # element of +nmlist+.
115
+ def self._symbol_names(klass, nmlist)
116
+ kname = self.class_symbol_prefix(klass)
117
+
118
+ nmlist.map do |nm|
119
+ (kname + '__' + nm.to_s).to_sym
120
+ end
121
+ end # Safer::IVar._symbol_names
23
122
 
24
123
  ##
25
124
  # compute accessor routine symbol names, so that the names include the
@@ -29,21 +128,17 @@ module Safer
29
128
  # [_return_] Array of +Symbol+ objects generated from +klass+ and each
30
129
  # element of +nmlist+.
31
130
  # For example, given the following listing:
32
- # class MyClass
33
- # class SubClass
131
+ # class OuterClass
132
+ # class InnerClass
34
133
  # SYMNM = Safer::IVar::symbol_names(self, :foo)
35
- # puts(SYMNM)
134
+ # puts(SYMNM.to_s)
36
135
  # end
37
136
  # end
38
137
  # the following output would be produced:
39
- # myclass_subclass__foo
40
- def self.symbol_names(klass, nmlist)
41
- kname = self.class_symbol_prefix(klass)
42
-
43
- nmlist.map do |nm|
44
- (kname + '__' + nm.to_s).to_sym
45
- end
46
- end # Safer::IVar::symbol_names
138
+ # outerclass_innerclass__foo
139
+ def self.symbol_names(klass, *nmlist)
140
+ self._symbol_names(klass, nmlist)
141
+ end # Safer::IVar.symbol_names
47
142
 
48
143
  ##
49
144
  # create accessor routines for an instance variable, with variable name
@@ -69,12 +164,12 @@ module Safer
69
164
  # The name-mangling signals that these instance variables are, for all
70
165
  # intents and purposes, private to +klass+.
71
166
  def self.instance_variable(klass, *nmlist)
72
- self.symbol_names(klass, nmlist).each do |symnm|
167
+ self.symbol_names(klass, *nmlist).each do |symnm|
73
168
  klass.class_eval do
74
169
  attr_accessor symnm
75
170
  end
76
171
  end
77
- end # Safer::IVar::instance_variable
172
+ end # Safer::IVar.instance_variable
78
173
 
79
174
  ##
80
175
  # export the reader routines for instance variables in a nicer way.
@@ -84,13 +179,13 @@ module Safer
84
179
  # Each symbol in +nmlist+ should have previously been given as
85
180
  # an argument to Safer::IVar::instance_variables(+klass+).
86
181
  def self.export_reader(klass, *nmlist)
87
- symlist = self.symbol_names(klass, nmlist)
182
+ symlist = self.symbol_names(klass, *nmlist)
88
183
  nmlist.size.times do |index|
89
184
  thisnm = nmlist[index]
90
185
  thissym = symlist[index]
91
186
  klass.class_eval("def #{thisnm} ; self.#{thissym} ; end")
92
187
  end
93
- end # Safer::IVar::export_reader
188
+ end # Safer::IVar.export_reader
94
189
 
95
190
  ##
96
191
  # export the writer routines for instance variables in a nicer way.
@@ -100,7 +195,7 @@ module Safer
100
195
  # Each symbol in +nmlist+ should have previously been given as
101
196
  # an argument to Safer::IVar::instance_variables(+klass+).
102
197
  def self.export_writer(klass, *nmlist)
103
- symlist = self.symbol_names(klass, nmlist)
198
+ symlist = self.symbol_names(klass, *nmlist)
104
199
  nmlist.size.times do |index|
105
200
  thisnm = nmlist[index]
106
201
  thissym = symlist[index]
@@ -108,7 +203,7 @@ module Safer
108
203
  "def #{thisnm}=(value) ; self.#{thissym} = value ; end"
109
204
  )
110
205
  end
111
- end # Safer::IVar::export_writer
206
+ end # Safer::IVar.export_writer
112
207
 
113
208
  ##
114
209
  # export both reader and writer routines for instance variables in a
@@ -121,7 +216,7 @@ module Safer
121
216
  def self.export_accessor(klass, *nmlist)
122
217
  self.export_reader(klass, *nmlist)
123
218
  self.export_writer(klass, *nmlist)
124
- end # Safer::IVar::export_accessor
219
+ end # Safer::IVar.export_accessor
125
220
 
126
221
  end # Safer::IVar
127
222
  end # Safer
@@ -1,25 +1,119 @@
1
1
  require 'safer/ivar'
2
2
 
3
3
  module Safer
4
- ## Safer::ClassProtocol
4
+ ## Safer::Protocol
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
- # new +ClassProtocol+ object. Call +class_conforms?+ or +instance_conforms?+
7
+ # new +Protocol+ object. Call +class_conforms?+ or +instance_conforms?+
8
8
  # to verify that a Class or instance of the class (respectively) conforms to
9
9
  # a protocol.
10
- class ClassProtocol
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
11
105
 
12
106
  ##
13
- # Utility function used during ClassProtocol operations. Given an
14
- # +Array+, construct a +Hash+ with the values in the +Array+ as keys.
15
- # If no block is provided, then the values for all keys in the +Hash+ will
16
- # be +true+. If a block is provided, the block is called for each value
17
- # in the +Array+, and its return value will be stored as the value for
18
- # that element in the +Hash+. If the block returns +nil+, then the element
19
- # will not be stored in the resulting +Hash+.
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+.
20
114
  def self._array_to_table(array, &block)
21
115
  if ! block
22
- block = proc do |el| true ; end
116
+ block = proc do |h, el| true ; end
23
117
  end
24
118
  array.inject(Hash::new) do |h, el|
25
119
  newval = block.call(h, el)
@@ -28,12 +122,12 @@ module Safer
28
122
  end
29
123
  h
30
124
  end
31
- end
125
+ end # Safer::Protocol._array_to_table
32
126
 
33
127
  ##
34
128
  # Describes a set of methods that should be implemented by an object, but
35
- # are not implemented. The ClassProtocol::Error object will contain two
36
- # ClassProtocol::Violation objects: one for class-method protocol
129
+ # are not implemented. The Protocol::Error object will contain two
130
+ # Protocol::Violation objects: one for class-method protocol
37
131
  # violations, and one for instance-method protocol violations.
38
132
  class Violation
39
133
  Safer::IVar::instance_variable(self, :table)
@@ -47,7 +141,7 @@ module Safer
47
141
  # had a different arity (number of required / optional arguments) than
48
142
  # the protocol, the value for that method will be the arity discovered in
49
143
  # the object. (The desired arity can be accessed through the
50
- # ClassProtocol::Signature for that method.)
144
+ # Protocol::Signature for that method.)
51
145
  Safer::IVar::export_reader(self, :table)
52
146
  ##
53
147
  # :attr_reader: report
@@ -56,22 +150,22 @@ module Safer
56
150
  Safer::IVar::export_reader(self, :report)
57
151
 
58
152
  ##
59
- # Create a new ClassProtocol::Violation object.
153
+ # Create a new Protocol::Violation object.
60
154
  def initialize(table, report)
61
- self.safer_classprotocol_violation__table = table
62
- self.safer_classprotocol_violation__report = report
63
- end
155
+ self.safer_protocol_violation__table = table
156
+ self.safer_protocol_violation__report = report
157
+ end # Safer::Protocol::Violation#initialize
64
158
  ##
65
- # Compare two ClassProtocol::Violation objects for content equality.
159
+ # Compare two Protocol::Violation objects for content equality.
66
160
  def ==(other_object)
67
161
  self.class == other_object.class &&
68
- self.safer_classprotocol_violation__table == other_object.safer_classprotocol_violation__table
69
- end
70
- end
162
+ self.safer_protocol_violation__table == other_object.safer_protocol_violation__table
163
+ end # Safer::Protocol::Violation#==
164
+ end # Safer::Protocol::Violation
71
165
 
72
166
  ##
73
167
  # Hash of method_name => method.arity, describing a set of methods that
74
- # we expect to see implemented in another class. ClassProtocol will contain
168
+ # we expect to see implemented in another class. Protocol will contain
75
169
  # two of these objects - one describing class methods, and one describing
76
170
  # instance methods.
77
171
  class Signature
@@ -83,16 +177,16 @@ module Safer
83
177
  Safer::IVar::export_reader(self, :table)
84
178
 
85
179
  ##
86
- # Create a ClassProtocol::Signature object.
180
+ # Create a Protocol::Signature object.
87
181
  def initialize(table)
88
- self.safer_classprotocol_signature__table = table
89
- end
182
+ self.safer_protocol_signature__table = table
183
+ end # Safer::Protocol::Signature#initialize
90
184
  ##
91
- # Compare two ClassProtocol::Signature objects for content equality.
185
+ # Compare two Protocol::Signature objects for content equality.
92
186
  def ==(other_object)
93
187
  self.class == other_object.class &&
94
- self.safer_classprotocol_signature__table == other_object.safer_classprotocol_signature__table
95
- end
188
+ self.safer_protocol_signature__table == other_object.safer_protocol_signature__table
189
+ end # Safer::Protocol::Signature#==
96
190
 
97
191
  ##
98
192
  # Given an object, and the name of the method for getting a named method
@@ -102,7 +196,7 @@ module Safer
102
196
  report = ''
103
197
  have_error = false
104
198
  error_table = Hash::new
105
- self.safer_classprotocol_signature__table.each_pair do |name, arity|
199
+ self.safer_protocol_signature__table.each_pair do |name, arity|
106
200
  begin
107
201
  m = object.send(get_method, name)
108
202
  if m.arity != arity
@@ -118,17 +212,17 @@ module Safer
118
212
  end
119
213
 
120
214
  if have_error
121
- Safer::ClassProtocol::Violation.new(error_table, report)
215
+ Safer::Protocol::Violation.new(error_table, report)
122
216
  else
123
217
  nil
124
218
  end
125
- end
219
+ end # Safer::Protocol::Signature#find_violations
126
220
 
127
221
  ##
128
222
  # Derive a Signature from an object.
129
223
  def self.create(object, get_methods, get_method, &filter)
130
224
  method_names = object.send(get_methods)
131
- method_table = Safer::ClassProtocol._array_to_table(method_names) do
225
+ method_table = Safer::Protocol._array_to_table(method_names) do
132
226
  |h, method_name|
133
227
  if filter.call(method_name)
134
228
  m = object.send(get_method, method_name)
@@ -136,10 +230,10 @@ module Safer
136
230
  end
137
231
  end
138
232
  self.new(method_table)
139
- end
140
- end
233
+ end # Safer::Protocol::Signature.create
234
+ end # Safer::Protocol::Signature
141
235
 
142
- ## Safer::ClassProtocol::Error
236
+ ## Safer::Protocol::Error
143
237
  # Error generated when a class does not conform to a protocol signature.
144
238
  class Error < StandardError
145
239
  Safer::IVar::instance_variable(self, :error_object)
@@ -154,16 +248,16 @@ module Safer
154
248
  Safer::IVar::export_reader(self, :error_object)
155
249
  ##
156
250
  # :attr_reader: protocol
157
- # ClassProtocol object describing the signature that error_object failed
251
+ # Protocol object describing the signature that error_object failed
158
252
  # to properly implement.
159
253
  Safer::IVar::export_reader(self, :protocol)
160
254
  ##
161
255
  # :attr_reader: class_violations
162
- # ClassProtocol::Violation describing class-method protocol violations.
256
+ # Protocol::Violation describing class-method protocol violations.
163
257
  Safer::IVar::export_reader(self, :class_violations)
164
258
  ##
165
259
  # :attr_reader: instance_violations
166
- # ClassProtocol::Violation describing instance-method protocol violations.
260
+ # Protocol::Violation describing instance-method protocol violations.
167
261
  Safer::IVar::export_reader(self, :instance_violations)
168
262
  ##
169
263
  # :attr_reader: report
@@ -173,19 +267,19 @@ module Safer
173
267
 
174
268
 
175
269
  ##
176
- # Create a Safer::ClassProtocol::Error object.
270
+ # Create a Safer::Protocol::Error object.
177
271
  # Used internally.
178
272
  def initialize(message, error_object, protocol, class_violations, instance_violations)
179
273
  super(message)
180
- self.safer_classprotocol_error__error_object = error_object
181
- self.safer_classprotocol_error__protocol = protocol
182
- self.safer_classprotocol_error__class_violations = class_violations
183
- self.safer_classprotocol_error__instance_violations = instance_violations
274
+ self.safer_protocol_error__error_object = error_object
275
+ self.safer_protocol_error__protocol = protocol
276
+ self.safer_protocol_error__class_violations = class_violations
277
+ self.safer_protocol_error__instance_violations = instance_violations
184
278
  report = ''
185
279
  if class_violations
186
280
  report +=
187
281
  "Class method errors:\n" +
188
- self.safer_classprotocol_error__class_violations.report
282
+ self.safer_protocol_error__class_violations.report
189
283
  if instance_violations
190
284
  report += "\n\n"
191
285
  end
@@ -193,49 +287,49 @@ module Safer
193
287
  if instance_violations
194
288
  report +=
195
289
  "Instance method errors:\n" +
196
- self.safer_classprotocol_error__instance_violations.report
290
+ self.safer_protocol_error__instance_violations.report
197
291
  end
198
- self.safer_classprotocol_error__report = report
199
- end # Safer::ClassProtocol::Error#initialize
292
+ self.safer_protocol_error__report = report
293
+ end # Safer::Protocol::Error#initialize
200
294
 
201
295
  ##
202
- # Compare two Safer::ClassProtocol::Error objects for content equality.
296
+ # Compare two Safer::Protocol::Error objects for content equality.
203
297
  def ==(other_object)
204
298
  self.class == other_object.class &&
205
299
  self.error_object == other_object.error_object &&
206
300
  self.protocol == other_object.protocol &&
207
301
  self.class_violations == other_object.class_violations &&
208
302
  self.instance_violations == other_object.instance_violations
209
- end
303
+ end # Safer::Protocol::Error#==
210
304
 
211
305
  ##
212
306
  # Error generated when a Class object does not conform to a desired
213
307
  # protocol.
214
- class ClassError < Safer::ClassProtocol::Error
308
+ class ClassError < Safer::Protocol::Error
215
309
  ##
216
- # Create a Safer::ClassProtocol::Error::ClassError object.
310
+ # Create a Safer::Protocol::Error::ClassError object.
217
311
  def initialize(error_object, protocol, class_violations, instance_violations)
218
312
  super(
219
313
  "Class #{error_object} does not implement protocol" +
220
314
  "#{protocol.name}.",
221
315
  error_object, protocol, class_violations, instance_violations)
222
- end
223
- end # Safer::ClassProtocol::Error::ClassError
316
+ end # Safer::Protocol::Error::ClassError#initialize
317
+ end # Safer::Protocol::Error::ClassError
224
318
 
225
319
  ##
226
320
  # Error generated when an instance object does not conform to a desired
227
321
  # protocol.
228
- class InstanceError < Safer::ClassProtocol::Error
322
+ class InstanceError < Safer::Protocol::Error
229
323
  ##
230
- # Create a Safer::ClassProtocol::Error::InstanceError object.
324
+ # Create a Safer::Protocol::Error::InstanceError object.
231
325
  def initialize(error_object, protocol, class_violations, instance_violations)
232
326
  super(
233
327
  "Instance of #{error_object.class} does not implement protocol" +
234
328
  "#{protocol.name}.",
235
329
  error_object, protocol, class_violations, instance_violations)
236
- end
237
- end # Safer::ClassProtocol::Error::InstanceError
238
- end # Safer::ClassProtocol::Error
330
+ end # Safer::Protocol::Error::InstanceError#initialize
331
+ end # Safer::Protocol::Error::InstanceError
332
+ end # Safer::Protocol::Error
239
333
 
240
334
 
241
335
  Safer::IVar::instance_variable(self, :name)
@@ -259,46 +353,78 @@ module Safer
259
353
  Safer::IVar::export_reader(self, :instance_signature)
260
354
 
261
355
  ##
262
- # Create a +ClassProtocol+ object.
356
+ # Create a +Protocol+ object.
263
357
  # Used internally.
264
358
  # [+name+] Value for +name+ field object.
265
359
  # [+class_signature+] Value for +class_signature+ field.
266
360
  # [+instance_signature+] Value for +instance_signature+ field.
267
361
  def initialize(name, class_signature, instance_signature)
268
- self.safer_classprotocol__name = name
269
- self.safer_classprotocol__class_signature = class_signature
270
- self.safer_classprotocol__instance_signature = instance_signature
271
- end # Safer::ClassProtocol#initialize
362
+ self.safer_protocol__name = name
363
+ self.safer_protocol__class_signature = class_signature
364
+ self.safer_protocol__instance_signature = instance_signature
365
+ end # Safer::Protocol#initialize
366
+
367
+ ##
368
+ # Given a Class object, find the protocol methods that are not supported by
369
+ # a class, or its instances.
370
+ # [_returns_] If protocol violations are found, returns a
371
+ # Safer::Protocol::Error::ClassError object describing the
372
+ # violations. Otherwise, returns +nil+.
373
+ def violations_from_class(klass)
374
+ class_violations = self.safer_protocol__class_signature.find_violations(klass, :method)
375
+ instance_violations = self.safer_protocol__instance_signature.find_violations(klass, :instance_method)
376
+
377
+ if class_violations || instance_violations
378
+ Error::ClassError.new(klass, self, class_violations, instance_violations)
379
+ else
380
+ nil
381
+ end
382
+ end # Safer::Protocol#violations_from_class
383
+
384
+ ##
385
+ # Given an instance object, find the protocol methods that are not supported
386
+ # by the instance, or its class.
387
+ # [_returns_] If protocol violations are found, returns a
388
+ # Safer::Protocol::Error::InstanceError object describing the
389
+ # violations. Otherwise, returns +nil+.
390
+ def violations_from_instance(instance)
391
+ class_violations = self.safer_protocol__class_signature.find_violations(instance.class, :method)
392
+ instance_violations = self.safer_protocol__instance_signature.find_violations(instance, :method)
393
+ if class_violations || instance_violations
394
+ Error::InstanceError.new(instance, self, class_violations, instance_violations)
395
+ else
396
+ nil
397
+ end
398
+ end # Safer::Protocol#violations_from_instance
272
399
 
273
400
  ##
274
401
  # Check that a Class object conforms to this protocol.
275
402
  def class_conforms?(klass)
276
- class_violations = self.safer_classprotocol__class_signature.find_violations(klass, :method)
277
- instance_violations = self.safer_classprotocol__instance_signature.find_violations(klass, :instance_method)
278
- if class_violations || instance_violations
279
- raise(Error::ClassError.new(klass, self, class_violations, instance_violations))
403
+ violations = self.violations_from_class(klass)
404
+ if violations
405
+ raise violations
280
406
  end
281
407
  true
282
- end # Safer::ClassProtocol#class_conforms?
408
+ end # Safer::Protocol#class_conforms?
409
+
283
410
  ##
284
411
  # Check that an instance object conforms to this protocol.
285
412
  def instance_conforms?(instance)
286
- class_violations = self.safer_classprotocol__class_signature.find_violations(instance.class, :method)
287
- instance_violations = self.safer_classprotocol__instance_signature.find_violations(instance, :method)
288
- if class_violations || instance_violations
289
- raise(Error::InstanceError.new(instance, self, class_violations, instance_violations))
413
+ violations = self.violations_from_instance(instance)
414
+ if violations
415
+ raise violations
290
416
  end
291
417
  true
292
- end # Safer::ClassProtocol#instance_conforms?
418
+ end # Safer::Protocol#instance_conforms?
293
419
 
294
420
  ##
295
421
  # Given a +Class+ object +klass+ and a +Class+ object +base+, create a
296
- # +ClassProtocol+ object representing the methods implemented in +klass+
422
+ # +Protocol+ object representing the methods implemented in +klass+
297
423
  # that are not present in +base+.
298
- # [+klass+] +Class+ object from which to derive a +ClassProtocol+.
424
+ # [+klass+] +Class+ object from which to derive a +Protocol+.
299
425
  # [+base+] +Class+ object describing routines that will be implemented by
300
426
  # +klass+, but which should not be included in the returned
301
- # +ClassProtocol+.
427
+ # +Protocol+.
302
428
  def self.create_from_class(klass, base = Object)
303
429
  class_signature = Signature.create(klass, :methods, :method) do
304
430
  |method_name|
@@ -311,11 +437,11 @@ module Safer
311
437
  end
312
438
 
313
439
  self.new(klass.to_s, class_signature, instance_signature)
314
- end # Safer::ClassProtocol.create_from_class
440
+ end # Safer::Protocol.create_from_class
315
441
 
316
442
  ##
317
443
  # Given an instance object +instance+ and a +Class+ object +baseclass+,
318
- # create a +ClassProtocol+ object representing the methods implemented in
444
+ # create a +Protocol+ object representing the methods implemented in
319
445
  # +klass+ that are not present in +base+.
320
446
  def self.create_from_instance(instance, baseclass = Object)
321
447
  class_signature = Signature.create(instance.class, :methods, :method) do
@@ -329,7 +455,7 @@ module Safer
329
455
  end
330
456
 
331
457
  self.new(instance.class.to_s, class_signature, instance_signature)
332
- end # Safer::ClassProtocol.create_from_instance
458
+ end # Safer::Protocol.create_from_instance
333
459
 
334
- end # Safer::ClassProtocol
460
+ end # Safer::Protocol
335
461
  end # Safer
data/lib/safer.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  module Safer
5
5
  ##
6
6
  # Current release of Safer.
7
- VERSION = "0.1.0"
7
+ VERSION = "0.2.0"
8
8
  end
@@ -34,9 +34,9 @@ end
34
34
 
35
35
  class TC_SaferProtocol < Test::Unit::TestCase
36
36
  def setup
37
- @class_protocol = Safer::ClassProtocol.create_from_class(TestArityClass)
38
- @instance_protocol = Safer::ClassProtocol.create_from_class(TestArityInstance)
39
- @both_protocol = Safer::ClassProtocol.create_from_class(TestArityBoth)
37
+ @class_protocol = Safer::Protocol.create_from_class(TestArityClass)
38
+ @instance_protocol = Safer::Protocol.create_from_class(TestArityInstance)
39
+ @both_protocol = Safer::Protocol.create_from_class(TestArityBoth)
40
40
 
41
41
  @class_instance = TestArityClass::new
42
42
  @instance_instance = TestArityInstance::new
@@ -56,9 +56,19 @@ class TC_SaferProtocol < Test::Unit::TestCase
56
56
 
57
57
  def get_methods(useinstance)
58
58
  if useinstance
59
- [ :instance_conforms?, proc do |obj| obj ; end, Safer::ClassProtocol::Error::InstanceError ]
59
+ {
60
+ :conforms => :instance_conforms?,
61
+ :violations => :violations_from_instance,
62
+ :get_object => proc do |obj| obj ; end,
63
+ :error_type => Safer::Protocol::Error::InstanceError
64
+ }
60
65
  else
61
- [ :class_conforms?, proc do |obj| obj.class ; end, Safer::ClassProtocol::Error::ClassError ]
66
+ {
67
+ :conforms => :class_conforms?,
68
+ :violations => :violations_from_class,
69
+ :get_object => proc do |obj| obj.class ; end,
70
+ :error_type => Safer::Protocol::Error::ClassError
71
+ }
62
72
  end
63
73
  end
64
74
 
@@ -71,14 +81,19 @@ class TC_SaferProtocol < Test::Unit::TestCase
71
81
  end
72
82
 
73
83
  def run_shouldConform(useinstance)
74
- conforms_method, object_get, except_type = get_methods(useinstance)
84
+ methods = get_methods(useinstance)
75
85
  assert_nothing_thrown do
76
- @class_protocol.send(conforms_method, object_get.call(@class_instance))
77
- @instance_protocol.send(conforms_method, object_get.call(@instance_instance))
78
- @both_protocol.send(conforms_method, object_get.call(@both_instance))
79
- @class_protocol.send(conforms_method, object_get.call(@both_instance))
80
- @instance_protocol.send(conforms_method, object_get.call(@both_instance))
86
+ @class_protocol.send(methods[:conforms], methods[:get_object].call(@class_instance))
87
+ @instance_protocol.send(methods[:conforms], methods[:get_object].call(@instance_instance))
88
+ @both_protocol.send(methods[:conforms], methods[:get_object].call(@both_instance))
89
+ @class_protocol.send(methods[:conforms], methods[:get_object].call(@both_instance))
90
+ @instance_protocol.send(methods[:conforms], methods[:get_object].call(@both_instance))
81
91
  end
92
+ assert_same(@class_protocol.send(methods[:violations], methods[:get_object].call(@class_instance)), nil)
93
+ assert_same(@instance_protocol.send(methods[:violations], methods[:get_object].call(@instance_instance)), nil)
94
+ assert_same(@both_protocol.send(methods[:violations], methods[:get_object].call(@both_instance)), nil)
95
+ assert_same(@class_protocol.send(methods[:violations], methods[:get_object].call(@both_instance)), nil)
96
+ assert_same(@instance_protocol.send(methods[:violations], methods[:get_object].call(@both_instance)), nil)
82
97
  end
83
98
  def test_shouldConform
84
99
  run_shouldConform(false)
@@ -86,16 +101,18 @@ class TC_SaferProtocol < Test::Unit::TestCase
86
101
  end
87
102
 
88
103
  def run_arity(useinstance)
89
- conforms_method, object_get, except_type = get_methods(useinstance)
104
+ methods = get_methods(useinstance)
105
+ violations = @instance_protocol.send(methods[:violations], methods[:get_object].call(@error_instance))
106
+ assert_same(violations.class, methods[:error_type])
107
+ assert_same(methods[:get_object].call(@error_instance), violations.error_object)
108
+ assert_same(@instance_protocol, violations.protocol)
109
+ assert_same(nil, violations.class_violations)
110
+ assert_equal({ 'arity_empty' => 1 }, violations.instance_violations.table)
90
111
  begin
91
- @instance_protocol.send(conforms_method, object_get.call(@error_instance))
112
+ @instance_protocol.send(methods[:conforms], methods[:get_object].call(@error_instance))
92
113
  flunk("Expected exception.")
93
- rescue Safer::ClassProtocol::Error => e
94
- assert_same(e.class, except_type)
95
- assert_same(object_get.call(@error_instance), e.error_object)
96
- assert_same(@instance_protocol, e.protocol)
97
- assert_same(nil, e.class_violations)
98
- assert_equal({ 'arity_empty' => 1 }, e.instance_violations.table)
114
+ rescue Safer::Protocol::Error => e
115
+ assert_equal(violations, e)
99
116
  end
100
117
  end
101
118
  def test_arity
@@ -104,37 +121,41 @@ class TC_SaferProtocol < Test::Unit::TestCase
104
121
  end
105
122
 
106
123
  def run_classInstanceError(useinstance)
107
- conforms_method, object_get, except_type = get_methods(useinstance)
124
+ methods = get_methods(useinstance)
125
+
126
+ class_instance_violations = @instance_protocol.send(methods[:violations], methods[:get_object].call(@class_instance))
127
+ assert_same(class_instance_violations.class, methods[:error_type])
128
+ assert_same(class_instance_violations.error_object, methods[:get_object].call(@class_instance))
129
+ assert_same(class_instance_violations.protocol, @instance_protocol)
130
+ assert_same(nil, class_instance_violations.class_violations)
131
+ assert_equal(class_instance_violations.instance_violations.table.keys.size, @instance_protocol.instance_signature.table.keys.size)
132
+ @instance_protocol.instance_signature.table.each_pair do |key, arity|
133
+ assert(class_instance_violations.instance_violations.table.has_key?(key))
134
+ assert_same(class_instance_violations.instance_violations.table[key], true)
135
+ end
108
136
 
109
- class_instance_error = nil
110
137
  begin
111
- @instance_protocol.send(conforms_method, object_get.call(@class_instance))
138
+ @instance_protocol.send(methods[:conforms], methods[:get_object].call(@class_instance))
112
139
  flunk("Expected exception.")
113
- rescue Safer::ClassProtocol::Error => e
114
- assert_same(e.class, except_type)
115
- assert_same(e.error_object, object_get.call(@class_instance))
116
- assert_same(e.protocol, @instance_protocol)
117
- assert_same(nil, e.class_violations)
118
- assert_equal(e.instance_violations.table.keys.size, @instance_protocol.instance_signature.table.keys.size)
119
- @instance_protocol.instance_signature.table.each_pair do |key, arity|
120
- assert(e.instance_violations.table.has_key?(key))
121
- assert_same(e.instance_violations.table[key], true)
122
- end
123
- class_instance_error = e
140
+ rescue Safer::Protocol::Error => e
141
+ assert_equal(e, class_instance_violations)
124
142
  end
125
143
 
144
+ both_instance_violations = @both_protocol.send(methods[:violations], methods[:get_object].call(@class_instance))
145
+ assert_same(both_instance_violations.class, methods[:error_type])
146
+ assert_same(both_instance_violations.error_object, methods[:get_object].call(@class_instance))
147
+ assert_same(both_instance_violations.protocol, @both_protocol)
148
+ assert_equal(
149
+ both_instance_violations.instance_violations, class_instance_violations.instance_violations)
150
+ assert_same(
151
+ both_instance_violations.class_violations, class_instance_violations.class_violations)
152
+ assert_equal(both_instance_violations.report, class_instance_violations.report)
153
+
126
154
  begin
127
- @both_protocol.send(conforms_method, object_get.call(@class_instance))
155
+ @both_protocol.send(methods[:conforms], methods[:get_object].call(@class_instance))
128
156
  flunk("Expected exception.")
129
- rescue Safer::ClassProtocol::Error => e
130
- assert_same(e.class, except_type)
131
- assert_same(e.error_object, object_get.call(@class_instance))
132
- assert_same(e.protocol, @both_protocol)
133
- assert_equal(
134
- e.instance_violations, class_instance_error.instance_violations)
135
- assert_same(
136
- e.class_violations, class_instance_error.class_violations)
137
- assert_equal(e.report, class_instance_error.report)
157
+ rescue Safer::Protocol::Error => e
158
+ assert_equal(both_instance_violations, e)
138
159
  end
139
160
  end
140
161
  def test_classInstanceError
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: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.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-09-23 00:00:00 -04:00
18
+ date: 2010-10-10 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -83,7 +83,7 @@ files:
83
83
  - test/test_safer_ivar.rb
84
84
  - test/test_safer_protocol.rb
85
85
  has_rdoc: true
86
- homepage: http://safer.rubyforge.org/
86
+ homepage: http://safer.rubyforge.org
87
87
  licenses: []
88
88
 
89
89
  post_install_message:
@@ -112,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
112
  version: "0"
113
113
  requirements: []
114
114
 
115
- rubyforge_project: aidancully
115
+ rubyforge_project: safer
116
116
  rubygems_version: 1.3.7
117
117
  signing_key:
118
118
  specification_version: 3