safer 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.txt 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