safer 0.1.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/.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