safer 0.3.1 → 0.4.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 +14 -0
- data/Manifest.txt +1 -1
- data/{README.txt → README.rdoc} +0 -0
- data/Rakefile +2 -1
- data/lib/safer/hashprotocol.rb +263 -265
- data/lib/safer/ivar.rb +4 -219
- data/lib/safer/protocol.rb +454 -457
- data/lib/safer.rb +1 -1
- data/test/test_safer_hashprotocol.rb +27 -27
- data/test/test_safer_ivar.rb +25 -41
- data/test/test_safer_ivar_run.rb +77 -0
- data/test/test_safer_ivarfactory.rb +36 -0
- data/test/test_safer_protocol.rb +8 -3
- metadata +11 -8
data/lib/safer/ivar.rb
CHANGED
@@ -1,222 +1,7 @@
|
|
1
|
+
require 'safer/ivarfactory'
|
2
|
+
|
1
3
|
class Safer
|
2
4
|
##
|
3
|
-
#
|
4
|
-
|
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
|
-
#
|
91
|
-
class IVar
|
92
|
-
|
93
|
-
##
|
94
|
-
# Given a Class object, derive the prefix string for the accessor functions
|
95
|
-
# that instance_variable will create.
|
96
|
-
def self.class_symbol_prefix(klass)
|
97
|
-
klass.to_s.split('::').inject(nil) do |memo, el|
|
98
|
-
# reset the symbol name when an anonymous class is encountered.
|
99
|
-
if /^\#<.*>$/.match(el)
|
100
|
-
nil
|
101
|
-
elsif memo
|
102
|
-
memo + '_' + el.downcase
|
103
|
-
else
|
104
|
-
el.downcase
|
105
|
-
end
|
106
|
-
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
|
122
|
-
|
123
|
-
##
|
124
|
-
# compute accessor routine symbol names, so that the names include the
|
125
|
-
# entire class namespace.
|
126
|
-
# [+klass+] Class object for which to generate a symbol name.
|
127
|
-
# [+nmlist+] Array of +Symbol+ objects.
|
128
|
-
# [_return_] Array of +Symbol+ objects generated from +klass+ and each
|
129
|
-
# element of +nmlist+.
|
130
|
-
# For example, given the following listing:
|
131
|
-
# class OuterClass
|
132
|
-
# class InnerClass
|
133
|
-
# SYMNM = Safer::IVar.symbol_names(self, :foo)
|
134
|
-
# puts(SYMNM.to_s)
|
135
|
-
# end
|
136
|
-
# end
|
137
|
-
# the following output would be produced:
|
138
|
-
# outerclass_innerclass__foo
|
139
|
-
def self.symbol_names(klass, *nmlist)
|
140
|
-
self._symbol_names(klass, nmlist)
|
141
|
-
end # Safer::IVar.symbol_names
|
142
|
-
|
143
|
-
##
|
144
|
-
# create accessor routines for an instance variable, with variable name
|
145
|
-
# determined by the class in which the instance variable was declared.
|
146
|
-
# [+klass+] Class object into which to generate the variable accessor
|
147
|
-
# functions.
|
148
|
-
# [+nmlist+] List of symbols for which to create accessor routines.
|
149
|
-
# Uses Safer::IVar.symbol_names to determine the symbol names of the
|
150
|
-
# accessor routines.
|
151
|
-
# For example, the listing:
|
152
|
-
# class MyClass
|
153
|
-
# class SubClass
|
154
|
-
# Safer::IVar.instance_variable(self, :foo, :bar)
|
155
|
-
# end
|
156
|
-
# end
|
157
|
-
# is equivalent to the following code:
|
158
|
-
# class MyClass
|
159
|
-
# class SubClass
|
160
|
-
# attr_accessor :myclass_subclass__foo
|
161
|
-
# attr_accessor :myclass_subclass__bar
|
162
|
-
# end
|
163
|
-
# end
|
164
|
-
# The name-mangling signals that these instance variables are, for all
|
165
|
-
# intents and purposes, private to +klass+.
|
166
|
-
def self.instance_variable(klass, *nmlist)
|
167
|
-
self.symbol_names(klass, *nmlist).each do |symnm|
|
168
|
-
klass.class_eval do
|
169
|
-
attr_accessor symnm
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end # Safer::IVar.instance_variable
|
173
|
-
|
174
|
-
##
|
175
|
-
# export the reader routines for instance variables in a nicer way.
|
176
|
-
# [+klass+] Class object for which to define reader accessors for
|
177
|
-
# instance variables defined by Safer::IVar.instance_variable.
|
178
|
-
# [+nmlist+] List of symbols for which to define reader accessors.
|
179
|
-
# Each symbol in +nmlist+ should have previously been given as
|
180
|
-
# an argument to Safer::IVar.instance_variable(+klass+).
|
181
|
-
def self.export_reader(klass, *nmlist)
|
182
|
-
symlist = self.symbol_names(klass, *nmlist)
|
183
|
-
nmlist.size.times do |index|
|
184
|
-
thisnm = nmlist[index]
|
185
|
-
thissym = symlist[index]
|
186
|
-
klass.class_eval("def #{thisnm} ; self.#{thissym} ; end")
|
187
|
-
end
|
188
|
-
end # Safer::IVar.export_reader
|
189
|
-
|
190
|
-
##
|
191
|
-
# export the writer routines for instance variables in a nicer way.
|
192
|
-
# [+klass+] Class object for which to define writer accessors for
|
193
|
-
# instance variables defined by Safer::IVar.instance_variable.
|
194
|
-
# [+nmlist+] List of symbols for which to define writer accessors.
|
195
|
-
# Each symbol in +nmlist+ should have previously been given as
|
196
|
-
# an argument to Safer::IVar.instance_variable(+klass+).
|
197
|
-
def self.export_writer(klass, *nmlist)
|
198
|
-
symlist = self.symbol_names(klass, *nmlist)
|
199
|
-
nmlist.size.times do |index|
|
200
|
-
thisnm = nmlist[index]
|
201
|
-
thissym = symlist[index]
|
202
|
-
klass.class_eval(
|
203
|
-
"def #{thisnm}=(value) ; self.#{thissym} = value ; end"
|
204
|
-
)
|
205
|
-
end
|
206
|
-
end # Safer::IVar.export_writer
|
207
|
-
|
208
|
-
##
|
209
|
-
# export both reader and writer routines for instance variables in a
|
210
|
-
# nicer way.
|
211
|
-
# [+klass+] Class object for which to define accessors for
|
212
|
-
# instance variables defined by Safer::IVar.instance_variable.
|
213
|
-
# [+nmlist+] List of symbols for which to define accessors.
|
214
|
-
# Each symbol in +nmlist+ should have previously been given as
|
215
|
-
# an argument to Safer::IVar.instance_variable(+klass+).
|
216
|
-
def self.export_accessor(klass, *nmlist)
|
217
|
-
self.export_reader(klass, *nmlist)
|
218
|
-
self.export_writer(klass, *nmlist)
|
219
|
-
end # Safer::IVar.export_accessor
|
220
|
-
|
221
|
-
end # Safer::IVar
|
5
|
+
# Default IVarFactory object.
|
6
|
+
IVar = IVarFactory.new(IVarFactory::Prefix::Full)
|
222
7
|
end # Safer
|