safer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest ADDED
@@ -0,0 +1,23 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.extra_files << "../some/external/dependency.rb"
7
+ #
8
+ # at.libs << ":../some/external"
9
+ #
10
+ # at.add_exception 'vendor'
11
+ #
12
+ # at.add_mapping(/dependency.rb/) do |f, _|
13
+ # at.files_matching(/test_.*rb$/)
14
+ # end
15
+ #
16
+ # %w(TestA TestB).each do |klass|
17
+ # at.extra_class_map[klass] = "test/test_misc.rb"
18
+ # end
19
+ # end
20
+
21
+ # Autotest.add_hook :run_command do |at|
22
+ # system "rake build"
23
+ # end
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.1.0 / 2010-09-22
2
+
3
+ * First release
4
+
data/Manifest.txt ADDED
@@ -0,0 +1,10 @@
1
+ .autotest
2
+ History.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ lib/safer.rb
7
+ lib/safer/ivar.rb
8
+ lib/safer/protocol.rb
9
+ test/test_safer_ivar.rb
10
+ test/test_safer_protocol.rb
data/README.txt ADDED
@@ -0,0 +1,69 @@
1
+ = safer
2
+
3
+ http://safer.rubyforge.org/
4
+
5
+ == DESCRIPTION:
6
+
7
+ Safer is an umbrella library, with components designed to make it simple to
8
+ verify and improve the safety of your ruby code. There are at present two
9
+ modules under the safer umbrella:
10
+
11
+ [<tt>Safer::IVar</tt>] generates specially-named accessor functions
12
+ for class instance variables.
13
+ [<tt>Safer::ClassProtocol</tt>] is used to provide a ruby analogue to
14
+ Objective-C Protocols (which are similar to
15
+ Java interfaces, but do not require
16
+ inheritance).
17
+
18
+ == FEATURES/PROBLEMS:
19
+
20
+ * Name instance variables after the class in which they are defined. Generate
21
+ accessor functions for these instance variables.
22
+ * Define an "Protocol" class, and derive a signature from it. Verify that
23
+ another class (or an instance of that class) implements the protocol
24
+ signature.
25
+
26
+ == SYNOPSIS:
27
+
28
+ require 'safer/ivar'
29
+ require 'safer/protocol'
30
+
31
+ module MyModule
32
+ class MyProtocolClass
33
+ def self.class_method_foo(arg1, arg2)
34
+ end
35
+ def instance_method(arg3)
36
+ end
37
+ end
38
+ PROTOCOL = Safer::Protocol.create_from_class(MyProtocolClass)
39
+ class MyClass
40
+ Safer::IVar.instance_variable(self, :var1, :var2)
41
+ def initialize(var1, var2)
42
+ PROTOCOL.check_instance_conforms(var1)
43
+ PROTOCOL.check_instance_conforms(var2)
44
+ self.mymodule_myclass__var1 = var1
45
+ self.mymodule_myclass__var2 = var2
46
+ end
47
+ end
48
+ end
49
+
50
+ == REQUIREMENTS:
51
+
52
+ * None
53
+
54
+ == INSTALL:
55
+
56
+ * sudo gem install
57
+
58
+ == DEVELOPERS:
59
+
60
+ After checking out the source, run:
61
+
62
+ $ rake newb
63
+
64
+ This task will install any missing dependencies, run the tests/specs,
65
+ and generate the RDoc.
66
+
67
+ == LICENSE:
68
+
69
+ This software is placed into the public domain.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+
4
+ Hoe.spec 'safer' do
5
+ developer('Aidan Cully', 'aidan@panix.com')
6
+
7
+ self.rubyforge_name = 'aidancully'
8
+ end
data/lib/safer/ivar.rb ADDED
@@ -0,0 +1,127 @@
1
+ module Safer
2
+ ##
3
+ # Create accessor functions for instance variables, in which the accessor
4
+ # function is prefixed with the name of the class in which the instance
5
+ # variable is defined.
6
+ module IVar
7
+
8
+ ##
9
+ # Given a class name, derive the prefix string for the accessor functions
10
+ # that instance_variable will create.
11
+ def self.class_symbol_prefix(klass)
12
+ klass.to_s.split('::').inject(nil) do |memo, el|
13
+ # reset the symbol name when an anonymous class is encountered.
14
+ if /^\#<.*>$/.match(el)
15
+ nil
16
+ elsif memo
17
+ memo + '_' + el.downcase
18
+ else
19
+ el.downcase
20
+ end
21
+ end
22
+ end
23
+
24
+ ##
25
+ # compute accessor routine symbol names, so that the names include the
26
+ # entire class namespace.
27
+ # [+klass+] Class object for which to generate a symbol name.
28
+ # [+nmlist+] Array of +Symbol+ objects.
29
+ # [_return_] Array of +Symbol+ objects generated from +klass+ and each
30
+ # element of +nmlist+.
31
+ # For example, given the following listing:
32
+ # class MyClass
33
+ # class SubClass
34
+ # SYMNM = Safer::IVar::symbol_names(self, :foo)
35
+ # puts(SYMNM)
36
+ # end
37
+ # end
38
+ # 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
47
+
48
+ ##
49
+ # create accessor routines for an instance variable, with variable name
50
+ # determined by the class in which the instance variable was declared.
51
+ # [+klass+] Class object into which to generate the variable accessor
52
+ # functions.
53
+ # [+nmlist+] List of symbols for which to create accessor routines.
54
+ # Uses Safer::IVar::symbol_names to determine the symbol names of the
55
+ # accessor routines.
56
+ # For example, the listing:
57
+ # class MyClass
58
+ # class SubClass
59
+ # Safer::IVar::instance_variable(self, :foo, :bar)
60
+ # end
61
+ # end
62
+ # is equivalent to the following code:
63
+ # class MyClass
64
+ # class SubClass
65
+ # attr_accessor :myclass_subclass__foo
66
+ # attr_accessor :myclass_subclass__bar
67
+ # end
68
+ # end
69
+ # The name-mangling signals that these instance variables are, for all
70
+ # intents and purposes, private to +klass+.
71
+ def self.instance_variable(klass, *nmlist)
72
+ self.symbol_names(klass, nmlist).each do |symnm|
73
+ klass.class_eval do
74
+ attr_accessor symnm
75
+ end
76
+ end
77
+ end # Safer::IVar::instance_variable
78
+
79
+ ##
80
+ # export the reader routines for instance variables in a nicer way.
81
+ # [+klass+] Class object for which to define reader accessors for
82
+ # instance variables defined by Safer::IVar::instance_variables.
83
+ # [+nmlist+] List of symbols for which to define reader accessors.
84
+ # Each symbol in +nmlist+ should have previously been given as
85
+ # an argument to Safer::IVar::instance_variables(+klass+).
86
+ def self.export_reader(klass, *nmlist)
87
+ symlist = self.symbol_names(klass, nmlist)
88
+ nmlist.size.times do |index|
89
+ thisnm = nmlist[index]
90
+ thissym = symlist[index]
91
+ klass.class_eval("def #{thisnm} ; self.#{thissym} ; end")
92
+ end
93
+ end # Safer::IVar::export_reader
94
+
95
+ ##
96
+ # export the writer routines for instance variables in a nicer way.
97
+ # [+klass+] Class object for which to define writer accessors for
98
+ # instance variables defined by Safer::IVar::instance_variables.
99
+ # [+nmlist+] List of symbols for which to define writer accessors.
100
+ # Each symbol in +nmlist+ should have previously been given as
101
+ # an argument to Safer::IVar::instance_variables(+klass+).
102
+ def self.export_writer(klass, *nmlist)
103
+ symlist = self.symbol_names(klass, nmlist)
104
+ nmlist.size.times do |index|
105
+ thisnm = nmlist[index]
106
+ thissym = symlist[index]
107
+ klass.class_eval(
108
+ "def #{thisnm}=(value) ; self.#{thissym} = value ; end"
109
+ )
110
+ end
111
+ end # Safer::IVar::export_writer
112
+
113
+ ##
114
+ # export both reader and writer routines for instance variables in a
115
+ # nicer way.
116
+ # [+klass+] Class object for which to define accessors for
117
+ # instance variables defined by Safer::IVar::instance_variables.
118
+ # [+nmlist+] List of symbols for which to define accessors.
119
+ # Each symbol in +nmlist+ should have previously been given as
120
+ # an argument to Safer::IVar::instance_variables(+klass+).
121
+ def self.export_accessor(klass, *nmlist)
122
+ self.export_reader(klass, *nmlist)
123
+ self.export_writer(klass, *nmlist)
124
+ end # Safer::IVar::export_accessor
125
+
126
+ end # Safer::IVar
127
+ end # Safer
@@ -0,0 +1,335 @@
1
+ require 'safer/ivar'
2
+
3
+ module Safer
4
+ ## Safer::ClassProtocol
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 +ClassProtocol+ 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
+ class ClassProtocol
11
+
12
+ ##
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+.
20
+ def self._array_to_table(array, &block)
21
+ if ! block
22
+ block = proc do |el| true ; end
23
+ end
24
+ array.inject(Hash::new) do |h, el|
25
+ newval = block.call(h, el)
26
+ if newval
27
+ h[el] = newval
28
+ end
29
+ h
30
+ end
31
+ end
32
+
33
+ ##
34
+ # 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
37
+ # violations, and one for instance-method protocol violations.
38
+ class Violation
39
+ Safer::IVar::instance_variable(self, :table)
40
+ Safer::IVar::instance_variable(self, :report)
41
+
42
+ ##
43
+ # :attr_reader: table
44
+ # Hash describing method violations. Keys are the names of methods
45
+ # containing errors. If a method was unimplemented in the class, the
46
+ # value for that method will be +true+. If a method was implemented, but
47
+ # had a different arity (number of required / optional arguments) than
48
+ # the protocol, the value for that method will be the arity discovered in
49
+ # the object. (The desired arity can be accessed through the
50
+ # ClassProtocol::Signature for that method.)
51
+ Safer::IVar::export_reader(self, :table)
52
+ ##
53
+ # :attr_reader: report
54
+ # String describing the errors from +table+ in a more human-readable
55
+ # fashion.
56
+ Safer::IVar::export_reader(self, :report)
57
+
58
+ ##
59
+ # Create a new ClassProtocol::Violation object.
60
+ def initialize(table, report)
61
+ self.safer_classprotocol_violation__table = table
62
+ self.safer_classprotocol_violation__report = report
63
+ end
64
+ ##
65
+ # Compare two ClassProtocol::Violation objects for content equality.
66
+ def ==(other_object)
67
+ self.class == other_object.class &&
68
+ self.safer_classprotocol_violation__table == other_object.safer_classprotocol_violation__table
69
+ end
70
+ end
71
+
72
+ ##
73
+ # Hash of method_name => method.arity, describing a set of methods that
74
+ # we expect to see implemented in another class. ClassProtocol will contain
75
+ # two of these objects - one describing class methods, and one describing
76
+ # instance methods.
77
+ class Signature
78
+ Safer::IVar::instance_variable(self, :table)
79
+ ##
80
+ # :attr_reader: table
81
+ # +Hash+ object, in which the keys are the names of methods, and the
82
+ # value for a key is the required arity of that method.
83
+ Safer::IVar::export_reader(self, :table)
84
+
85
+ ##
86
+ # Create a ClassProtocol::Signature object.
87
+ def initialize(table)
88
+ self.safer_classprotocol_signature__table = table
89
+ end
90
+ ##
91
+ # Compare two ClassProtocol::Signature objects for content equality.
92
+ def ==(other_object)
93
+ self.class == other_object.class &&
94
+ self.safer_classprotocol_signature__table == other_object.safer_classprotocol_signature__table
95
+ end
96
+
97
+ ##
98
+ # Given an object, and the name of the method for getting a named method
99
+ # from that object, check that the object implements this function
100
+ # signature.
101
+ def find_violations(object, get_method)
102
+ report = ''
103
+ have_error = false
104
+ error_table = Hash::new
105
+ self.safer_classprotocol_signature__table.each_pair do |name, arity|
106
+ begin
107
+ m = object.send(get_method, name)
108
+ if m.arity != arity
109
+ report += "#{get_method}(#{name}) arity #{m.arity} != desired #{arity}.\n"
110
+ error_table[name] = m.arity
111
+ have_error = true
112
+ end
113
+ rescue NameError => e
114
+ report += "#{get_method}(#{name}) unimplemented.\n"
115
+ error_table[name] = true
116
+ have_error = true
117
+ end
118
+ end
119
+
120
+ if have_error
121
+ Safer::ClassProtocol::Violation.new(error_table, report)
122
+ else
123
+ nil
124
+ end
125
+ end
126
+
127
+ ##
128
+ # Derive a Signature from an object.
129
+ def self.create(object, get_methods, get_method, &filter)
130
+ method_names = object.send(get_methods)
131
+ method_table = Safer::ClassProtocol._array_to_table(method_names) do
132
+ |h, method_name|
133
+ if filter.call(method_name)
134
+ m = object.send(get_method, method_name)
135
+ m.arity
136
+ end
137
+ end
138
+ self.new(method_table)
139
+ end
140
+ end
141
+
142
+ ## Safer::ClassProtocol::Error
143
+ # Error generated when a class does not conform to a protocol signature.
144
+ class Error < StandardError
145
+ Safer::IVar::instance_variable(self, :error_object)
146
+ Safer::IVar::instance_variable(self, :protocol)
147
+ Safer::IVar::instance_variable(self, :class_violations)
148
+ Safer::IVar::instance_variable(self, :instance_violations)
149
+ Safer::IVar::instance_variable(self, :report)
150
+
151
+ ##
152
+ # :attr_reader: error_object
153
+ # Object that does not properly implement a desired method protocol.
154
+ Safer::IVar::export_reader(self, :error_object)
155
+ ##
156
+ # :attr_reader: protocol
157
+ # ClassProtocol object describing the signature that error_object failed
158
+ # to properly implement.
159
+ Safer::IVar::export_reader(self, :protocol)
160
+ ##
161
+ # :attr_reader: class_violations
162
+ # ClassProtocol::Violation describing class-method protocol violations.
163
+ Safer::IVar::export_reader(self, :class_violations)
164
+ ##
165
+ # :attr_reader: instance_violations
166
+ # ClassProtocol::Violation describing instance-method protocol violations.
167
+ Safer::IVar::export_reader(self, :instance_violations)
168
+ ##
169
+ # :attr_reader: report
170
+ # String for displaying human-readable error message describing protocol
171
+ # violations.
172
+ Safer::IVar::export_reader(self, :report)
173
+
174
+
175
+ ##
176
+ # Create a Safer::ClassProtocol::Error object.
177
+ # Used internally.
178
+ def initialize(message, error_object, protocol, class_violations, instance_violations)
179
+ 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
184
+ report = ''
185
+ if class_violations
186
+ report +=
187
+ "Class method errors:\n" +
188
+ self.safer_classprotocol_error__class_violations.report
189
+ if instance_violations
190
+ report += "\n\n"
191
+ end
192
+ end
193
+ if instance_violations
194
+ report +=
195
+ "Instance method errors:\n" +
196
+ self.safer_classprotocol_error__instance_violations.report
197
+ end
198
+ self.safer_classprotocol_error__report = report
199
+ end # Safer::ClassProtocol::Error#initialize
200
+
201
+ ##
202
+ # Compare two Safer::ClassProtocol::Error objects for content equality.
203
+ def ==(other_object)
204
+ self.class == other_object.class &&
205
+ self.error_object == other_object.error_object &&
206
+ self.protocol == other_object.protocol &&
207
+ self.class_violations == other_object.class_violations &&
208
+ self.instance_violations == other_object.instance_violations
209
+ end
210
+
211
+ ##
212
+ # Error generated when a Class object does not conform to a desired
213
+ # protocol.
214
+ class ClassError < Safer::ClassProtocol::Error
215
+ ##
216
+ # Create a Safer::ClassProtocol::Error::ClassError object.
217
+ def initialize(error_object, protocol, class_violations, instance_violations)
218
+ super(
219
+ "Class #{error_object} does not implement protocol" +
220
+ "#{protocol.name}.",
221
+ error_object, protocol, class_violations, instance_violations)
222
+ end
223
+ end # Safer::ClassProtocol::Error::ClassError
224
+
225
+ ##
226
+ # Error generated when an instance object does not conform to a desired
227
+ # protocol.
228
+ class InstanceError < Safer::ClassProtocol::Error
229
+ ##
230
+ # Create a Safer::ClassProtocol::Error::InstanceError object.
231
+ def initialize(error_object, protocol, class_violations, instance_violations)
232
+ super(
233
+ "Instance of #{error_object.class} does not implement protocol" +
234
+ "#{protocol.name}.",
235
+ error_object, protocol, class_violations, instance_violations)
236
+ end
237
+ end # Safer::ClassProtocol::Error::InstanceError
238
+ end # Safer::ClassProtocol::Error
239
+
240
+
241
+ Safer::IVar::instance_variable(self, :name)
242
+ Safer::IVar::instance_variable(self, :class_signature)
243
+ Safer::IVar::instance_variable(self, :instance_signature)
244
+
245
+ ##
246
+ # :attr_reader: name
247
+ # Name of this protocol signature. Typically derived from the class name
248
+ # from which this signature is derived.
249
+ Safer::IVar::export_reader(self, :name)
250
+ ##
251
+ # :attr_reader: class_signature
252
+ # Signatures of required class methods for objects implementing this
253
+ # protocol.
254
+ Safer::IVar::export_reader(self, :class_signature)
255
+ ##
256
+ # :attr_reader: instance_signature
257
+ # Signatures of required instance methods for objects implementing this
258
+ # protocol.
259
+ Safer::IVar::export_reader(self, :instance_signature)
260
+
261
+ ##
262
+ # Create a +ClassProtocol+ object.
263
+ # Used internally.
264
+ # [+name+] Value for +name+ field object.
265
+ # [+class_signature+] Value for +class_signature+ field.
266
+ # [+instance_signature+] Value for +instance_signature+ field.
267
+ 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
272
+
273
+ ##
274
+ # Check that a Class object conforms to this protocol.
275
+ 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))
280
+ end
281
+ true
282
+ end # Safer::ClassProtocol#class_conforms?
283
+ ##
284
+ # Check that an instance object conforms to this protocol.
285
+ 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))
290
+ end
291
+ true
292
+ end # Safer::ClassProtocol#instance_conforms?
293
+
294
+ ##
295
+ # Given a +Class+ object +klass+ and a +Class+ object +base+, create a
296
+ # +ClassProtocol+ object representing the methods implemented in +klass+
297
+ # that are not present in +base+.
298
+ # [+klass+] +Class+ object from which to derive a +ClassProtocol+.
299
+ # [+base+] +Class+ object describing routines that will be implemented by
300
+ # +klass+, but which should not be included in the returned
301
+ # +ClassProtocol+.
302
+ def self.create_from_class(klass, base = Object)
303
+ class_signature = Signature.create(klass, :methods, :method) do
304
+ |method_name|
305
+ ! Class.respond_to?(method_name)
306
+ end
307
+ baseinstance_table = self._array_to_table(base.instance_methods)
308
+ instance_signature = Signature.create(klass, :instance_methods, :instance_method) do
309
+ |method_name|
310
+ ! baseinstance_table.has_key?(method_name)
311
+ end
312
+
313
+ self.new(klass.to_s, class_signature, instance_signature)
314
+ end # Safer::ClassProtocol.create_from_class
315
+
316
+ ##
317
+ # Given an instance object +instance+ and a +Class+ object +baseclass+,
318
+ # create a +ClassProtocol+ object representing the methods implemented in
319
+ # +klass+ that are not present in +base+.
320
+ def self.create_from_instance(instance, baseclass = Object)
321
+ class_signature = Signature.create(instance.class, :methods, :method) do
322
+ |method_name|
323
+ ! Class.method_defined?(method_name)
324
+ end
325
+ baseinstance_table = self._array_to_table(base.instance_methods)
326
+ instance_signature = Signature.create(instance, :methods, :method) do
327
+ |method_name|
328
+ ! baseinstance_table.has_key?(method_name)
329
+ end
330
+
331
+ self.new(instance.class.to_s, class_signature, instance_signature)
332
+ end # Safer::ClassProtocol.create_from_instance
333
+
334
+ end # Safer::ClassProtocol
335
+ end # Safer
data/lib/safer.rb ADDED
@@ -0,0 +1,8 @@
1
+ ##
2
+ # Safer is an umbrella library, whose components are designed to improve
3
+ # the safety of your code.
4
+ module Safer
5
+ ##
6
+ # Current release of Safer.
7
+ VERSION = "0.1.0"
8
+ end
@@ -0,0 +1,85 @@
1
+ require 'safer/ivar'
2
+ require 'test/unit'
3
+
4
+ module Root
5
+ class Test
6
+ class Instance
7
+ Safer::IVar::instance_variable(self, :instance)
8
+ end
9
+ class Reader
10
+ Safer::IVar::instance_variable(self, :instance)
11
+ Safer::IVar::export_reader(self, :instance)
12
+ end
13
+ class Writer
14
+ Safer::IVar::instance_variable(self, :instance)
15
+ Safer::IVar::export_writer(self, :instance)
16
+ end
17
+ class Accessor
18
+ Safer::IVar::instance_variable(self, :instance)
19
+ Safer::IVar::export_accessor(self, :instance)
20
+ end
21
+ end
22
+ end
23
+
24
+ class TC_SaferIVar < Test::Unit::TestCase
25
+ def setup
26
+ end
27
+ def teardown
28
+ end
29
+
30
+ def assert_no_respond_to(object, method, message="")
31
+ _wrap_assertion do
32
+ full_message =
33
+ build_message(
34
+ nil,
35
+ "<?>\ngiven as the method name argument to #assert_no_respond_to " +
36
+ "must be a Symbol or #respond_to\\?(:to_str).",
37
+ method
38
+ )
39
+
40
+ assert_block(full_message) do
41
+ method.kind_of?(Symbol) || method.respond_to?(:to_str)
42
+ end
43
+ full_message =
44
+ build_message(
45
+ message, "<?>\nof type <?>\nnot expected to respond_to\\\\?<?>.\n",
46
+ object, object.class, method
47
+ )
48
+ assert_block(full_message) { not object.respond_to?(method) }
49
+ end
50
+ end
51
+
52
+ def test_class_symbol_prefix
53
+ assert_equal("root", Safer::IVar::class_symbol_prefix(Root))
54
+ assert_equal("root_test", Safer::IVar::class_symbol_prefix(Root::Test))
55
+ assert_equal("root_test_instance", Safer::IVar::class_symbol_prefix(Root::Test::Instance))
56
+ k = Root.class_eval("$k = Class::new(Root::Test::Instance)")
57
+ assert_same(nil, Safer::IVar::class_symbol_prefix(k))
58
+ b = k.class_eval("class Blah ; end ; Blah")
59
+ assert_equal("blah", Safer::IVar::class_symbol_prefix(b))
60
+ end
61
+
62
+ def test_instance
63
+ test_accessors = proc do |obj, expectRead, expectWrite|
64
+ if expectRead
65
+ assert_respond_to(obj, :instance)
66
+ else
67
+ assert_no_respond_to(obj, :instance)
68
+ end
69
+ if expectWrite
70
+ assert_respond_to(obj, :instance=)
71
+ else
72
+ assert_no_respond_to(obj, :instance=)
73
+ end
74
+ end
75
+ ti = Root::Test::Instance::new
76
+ assert_respond_to(ti, :root_test_instance__instance)
77
+ test_accessors.call(ti, false, false)
78
+ tr = Root::Test::Reader::new
79
+ test_accessors.call(tr, true, false)
80
+ tw = Root::Test::Writer::new
81
+ test_accessors.call(tw, false, true)
82
+ ta = Root::Test::Accessor::new
83
+ test_accessors.call(ta, true, true)
84
+ end
85
+ end
@@ -0,0 +1,144 @@
1
+ require 'safer/protocol'
2
+ require 'test/unit'
3
+
4
+ module ArityModule
5
+ def arity_empty
6
+ end
7
+ def arity_1(one)
8
+ end
9
+ def arity_2(one, two)
10
+ end
11
+ def arity_any(*args)
12
+ end
13
+ def arity_2_any(one, two, *args)
14
+ end
15
+ METHODS = %w(empty 1 2 any 2_any).map do |suffix| "arity_#{suffix}".to_sym end
16
+ end
17
+
18
+ class TestArityClass
19
+ extend ArityModule
20
+ end
21
+ class TestArityInstance
22
+ include ArityModule
23
+ end
24
+ class TestArityBoth
25
+ extend ArityModule
26
+ include ArityModule
27
+ end
28
+
29
+ class TestArityError
30
+ include ArityModule
31
+ def arity_empty(blah)
32
+ end
33
+ end
34
+
35
+ class TC_SaferProtocol < Test::Unit::TestCase
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)
40
+
41
+ @class_instance = TestArityClass::new
42
+ @instance_instance = TestArityInstance::new
43
+ @both_instance = TestArityBoth::new
44
+ @error_instance = TestArityError::new
45
+ end
46
+ def teardown
47
+ @class_protocol = nil
48
+ @instance_protocol = nil
49
+ @both_protocol = nil
50
+
51
+ @class_instance = nil
52
+ @instance_instance = nil
53
+ @both_instance = nil
54
+ @error_instance = nil
55
+ end
56
+
57
+ def get_methods(useinstance)
58
+ if useinstance
59
+ [ :instance_conforms?, proc do |obj| obj ; end, Safer::ClassProtocol::Error::InstanceError ]
60
+ else
61
+ [ :class_conforms?, proc do |obj| obj.class ; end, Safer::ClassProtocol::Error::ClassError ]
62
+ end
63
+ end
64
+
65
+ def test_signaturesCorrect
66
+ assert_equal(@both_protocol.class_signature, @class_protocol.class_signature)
67
+ assert_equal(@both_protocol.instance_signature, @instance_protocol.instance_signature)
68
+ assert_equal(@class_protocol.class_signature, @instance_protocol.instance_signature)
69
+ arity_methods = ArityModule::METHODS.map do |meth| meth.to_s end.sort
70
+ assert_equal(@class_protocol.class_signature.table.keys.sort, arity_methods)
71
+ end
72
+
73
+ def run_shouldConform(useinstance)
74
+ conforms_method, object_get, except_type = get_methods(useinstance)
75
+ 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))
81
+ end
82
+ end
83
+ def test_shouldConform
84
+ run_shouldConform(false)
85
+ run_shouldConform(true)
86
+ end
87
+
88
+ def run_arity(useinstance)
89
+ conforms_method, object_get, except_type = get_methods(useinstance)
90
+ begin
91
+ @instance_protocol.send(conforms_method, object_get.call(@error_instance))
92
+ 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)
99
+ end
100
+ end
101
+ def test_arity
102
+ run_arity(false)
103
+ run_arity(true)
104
+ end
105
+
106
+ def run_classInstanceError(useinstance)
107
+ conforms_method, object_get, except_type = get_methods(useinstance)
108
+
109
+ class_instance_error = nil
110
+ begin
111
+ @instance_protocol.send(conforms_method, object_get.call(@class_instance))
112
+ 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
124
+ end
125
+
126
+ begin
127
+ @both_protocol.send(conforms_method, object_get.call(@class_instance))
128
+ 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)
138
+ end
139
+ end
140
+ def test_classInstanceError
141
+ run_classInstanceError(false)
142
+ run_classInstanceError(true)
143
+ end
144
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: safer
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Aidan Cully
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-23 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rubyforge
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 2
32
+ - 0
33
+ - 4
34
+ version: 2.0.4
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: hoe
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 19
46
+ segments:
47
+ - 2
48
+ - 6
49
+ - 2
50
+ version: 2.6.2
51
+ type: :development
52
+ version_requirements: *id002
53
+ description: |-
54
+ Safer is an umbrella library, with components designed to make it simple to
55
+ verify and improve the safety of your ruby code. There are at present two
56
+ modules under the safer umbrella:
57
+
58
+ [<tt>Safer::IVar</tt>] generates specially-named accessor functions
59
+ for class instance variables.
60
+ [<tt>Safer::ClassProtocol</tt>] is used to provide a ruby analogue to
61
+ Objective-C Protocols (which are similar to
62
+ Java interfaces, but do not require
63
+ inheritance).
64
+ email:
65
+ - aidan@panix.com
66
+ executables: []
67
+
68
+ extensions: []
69
+
70
+ extra_rdoc_files:
71
+ - History.txt
72
+ - Manifest.txt
73
+ - README.txt
74
+ files:
75
+ - .autotest
76
+ - History.txt
77
+ - Manifest.txt
78
+ - README.txt
79
+ - Rakefile
80
+ - lib/safer.rb
81
+ - lib/safer/ivar.rb
82
+ - lib/safer/protocol.rb
83
+ - test/test_safer_ivar.rb
84
+ - test/test_safer_protocol.rb
85
+ has_rdoc: true
86
+ homepage: http://safer.rubyforge.org/
87
+ licenses: []
88
+
89
+ post_install_message:
90
+ rdoc_options:
91
+ - --main
92
+ - README.txt
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ hash: 3
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ requirements: []
114
+
115
+ rubyforge_project: aidancully
116
+ rubygems_version: 1.3.7
117
+ signing_key:
118
+ specification_version: 3
119
+ summary: Safer is an umbrella library, with components designed to make it simple to verify and improve the safety of your ruby code
120
+ test_files:
121
+ - test/test_safer_ivar.rb
122
+ - test/test_safer_protocol.rb