safer 0.4.0 → 0.4.1
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 +4 -0
- data/Manifest.txt +5 -0
- data/lib/safer.rb +1 -1
- data/lib/safer/ivarfactory.rb +269 -0
- data/lib/safer/ivarfactory/dsl.rb +49 -0
- data/lib/safer/ivarfactory/prefix.rb +29 -0
- metadata +10 -12
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -5,8 +5,13 @@ README.rdoc
|
|
5
5
|
Rakefile
|
6
6
|
lib/safer.rb
|
7
7
|
lib/safer/ivar.rb
|
8
|
+
lib/safer/ivarfactory.rb
|
9
|
+
lib/safer/ivarfactory/dsl.rb
|
10
|
+
lib/safer/ivarfactory/prefix.rb
|
8
11
|
lib/safer/protocol.rb
|
9
12
|
lib/safer/hashprotocol.rb
|
10
13
|
test/test_safer_ivar.rb
|
14
|
+
test/test_safer_ivar_run.rb
|
15
|
+
test/test_safer_ivarfactory.rb
|
11
16
|
test/test_safer_protocol.rb
|
12
17
|
test/test_safer_hashprotocol.rb
|
data/lib/safer.rb
CHANGED
@@ -0,0 +1,269 @@
|
|
1
|
+
require 'safer'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Create accessor functions for instance variables, in which the accessor
|
5
|
+
# function is prefixed with the name of the class in which the instance
|
6
|
+
# variable is defined.
|
7
|
+
#
|
8
|
+
# ==Usage
|
9
|
+
# Create one or more instance variables using instance_variable .
|
10
|
+
# For example, the following code:
|
11
|
+
# class Outer
|
12
|
+
# Safer::IVar.instance_variable(self, :variable)
|
13
|
+
# class Inner < Outer
|
14
|
+
# Safer::IVar.instance_variable(self, :variable)
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# puts(Outer.instance_methods.grep(/__variable/).inspect)
|
18
|
+
# puts(Outer::Inner.instance_methods.grep(/__variable/).inspect)
|
19
|
+
# produces the following output:
|
20
|
+
# ["outer__variable", "outer__variable="]
|
21
|
+
# ["outer_inner__variable", "outer_inner__variable=", "outer__variable", "outer__variable="]
|
22
|
+
#
|
23
|
+
# Accessors for Safer::IVar-defined instance variables can be created using
|
24
|
+
# export_reader, export_writer, and export_accessor .
|
25
|
+
#
|
26
|
+
# ==Rationale
|
27
|
+
# Safer::IVar is intended to improve the clarity and consistency of ruby
|
28
|
+
# code in at least the following (related) ways:
|
29
|
+
#
|
30
|
+
# 1. Reducing the probability of instance variable usage errors.
|
31
|
+
# 2. Documenting the instance variables attached to a class.
|
32
|
+
#
|
33
|
+
# ===Error Reduction
|
34
|
+
# Safer::IVar should help in reducing errors in at least the following ways:
|
35
|
+
#
|
36
|
+
# * Encapsulation errors. Traditional ruby instance variables defined by one
|
37
|
+
# class are transparently accessible to all subclasses. They are not,
|
38
|
+
# however, part of the public interface to a class (unless an accessor is
|
39
|
+
# defined), which means they are not generally documented. These two
|
40
|
+
# factors create a situation in which it is quite possible that a subclass
|
41
|
+
# inadvertantly re-define an instance variable in such a way as to render
|
42
|
+
# the subclass unusable. Bugs of this sort can be very difficult to
|
43
|
+
# resolve.
|
44
|
+
#
|
45
|
+
# Because instance variables generated by Safer::IVar are prefixed with a
|
46
|
+
# string derived from the class in which the variable is defined, it is much
|
47
|
+
# less likely that developers _inadvertantly_ re-define instance variables
|
48
|
+
# in sub-classes, dramatically reducing the likelihood of this type of
|
49
|
+
# error.
|
50
|
+
#
|
51
|
+
# * Misspelling errors. For example, suppose you typically write software
|
52
|
+
# using the en-us dialect, and have an object with a +@color+ instance
|
53
|
+
# variable. A en-uk speaker then submits a patch that sets the default
|
54
|
+
# color to green:
|
55
|
+
# --- ex1.rb 2010-09-28 06:24:52.000000000 -0400
|
56
|
+
# +++ ex2.rb 2010-09-28 06:25:00.000000000 -0400
|
57
|
+
# @@ -1,6 +1,7 @@
|
58
|
+
# class Foo
|
59
|
+
# def initialize
|
60
|
+
# @size = 3
|
61
|
+
# + @colour = "green"
|
62
|
+
# end
|
63
|
+
# attr_reader :color
|
64
|
+
# end
|
65
|
+
# This code will not raise any exceptions, but its behavior does not match
|
66
|
+
# developer intent. On the other hand, using Safer::IVar
|
67
|
+
# --- ex1-safer.rb 2010-09-28 06:31:51.000000000 -0400
|
68
|
+
# +++ ex2-safer.rb 2010-09-28 06:32:08.000000000 -0400
|
69
|
+
# @@ -1,7 +1,8 @@
|
70
|
+
# class Foo
|
71
|
+
# Safer::IVar.instance_variable(self, :size, :color)
|
72
|
+
# Safer::IVar.export_reader(self, :color)
|
73
|
+
# def initialize
|
74
|
+
# self.foo__size = 3
|
75
|
+
# + self.foo__colour = "green"
|
76
|
+
# end
|
77
|
+
# end
|
78
|
+
# The new code will raise an exception at the call to Foo.new, making it
|
79
|
+
# much less likely that the error will go undetected.
|
80
|
+
#
|
81
|
+
# ===Documentation
|
82
|
+
# Traditional ruby instance variables are defined and used in an <i>ad hoc</i>
|
83
|
+
# manner. As such, there is no natural location in which the instance
|
84
|
+
# variables defined by a class can be documented, and no obvious way to
|
85
|
+
# determine the set of instance variables used in a class. Safer::IVar
|
86
|
+
# instance variables will all be associated with a single call to
|
87
|
+
# Safer::IVar.instance_variable. This provides both a natural location for
|
88
|
+
# documenting an instance variable's interpretation, as well as a string to
|
89
|
+
# search for when determining the set of instance variables defined by a
|
90
|
+
# class.
|
91
|
+
#
|
92
|
+
class Safer::IVarFactory
|
93
|
+
def initialize(prefix)
|
94
|
+
@prefix = prefix
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Given a Class object, derive the prefix string for the accessor functions
|
99
|
+
# that instance_variable will create.
|
100
|
+
def class_symbol_prefix(klass)
|
101
|
+
@prefix.class_symbol_prefix(klass)
|
102
|
+
end # Safer::IVarFactory#class_symbol_prefix
|
103
|
+
|
104
|
+
##
|
105
|
+
# Used internally.
|
106
|
+
# [+prefix+] Prefix of generated symbol names.
|
107
|
+
# [+nmlist+] Array of +Symbol+ objects.
|
108
|
+
# [_return_] Array of +Symbol+ objects. Each element will be the
|
109
|
+
# concatenation of the +prefix+, '__', and the corresponding
|
110
|
+
# element of +nmlist+
|
111
|
+
def _symbol_names_internal(prefix, nmlist)
|
112
|
+
nmlist.map do |nm|
|
113
|
+
(prefix + '__' + nm.to_s).to_sym
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Used internally.
|
119
|
+
# [+klass+] Class object for which to generate a symbol name.
|
120
|
+
# [+nmlist+] Array of +Symbol+ objects.
|
121
|
+
# [_return_] Array of +Symbol+ objects generated from +klass+ and each
|
122
|
+
# element of +nmlist+.
|
123
|
+
def _symbol_names(klass, nmlist)
|
124
|
+
kname = self.class_symbol_prefix(klass)
|
125
|
+
self._symbol_names_internal(kname, nmlist)
|
126
|
+
end # Safer::IVarFactory#_symbol_names
|
127
|
+
|
128
|
+
##
|
129
|
+
# compute accessor routine symbol names, where the symbol name prefix is
|
130
|
+
# derived from the class name.
|
131
|
+
# [+klass+] Class object for which to generate a symbol name.
|
132
|
+
# [+nmlist+] Array of +Symbol+ objects.
|
133
|
+
# [_return_] Array of +Symbol+ objects generated from +klass+ and each
|
134
|
+
# element of +nmlist+.
|
135
|
+
# For example, given the following listing:
|
136
|
+
# class OuterClass
|
137
|
+
# class InnerClass
|
138
|
+
# SYMNM = Safer::IVar.symbol_names(self, :foo)
|
139
|
+
# puts(SYMNM.to_s)
|
140
|
+
# end
|
141
|
+
# end
|
142
|
+
# the following output would be produced:
|
143
|
+
# outerclass_innerclass__foo
|
144
|
+
def symbol_names(klass, *nmlist)
|
145
|
+
self._symbol_names(klass, nmlist)
|
146
|
+
end # Safer::IVarFactory#symbol_names
|
147
|
+
|
148
|
+
##
|
149
|
+
# Used internally. See Safer::IVarFactory#instance_variable, and
|
150
|
+
# Safer::IVarFactory::Dsl#ivar .
|
151
|
+
def _instance_variable_internal(klass, prefix, nmlist)
|
152
|
+
self._symbol_names_internal(prefix, nmlist).each do |symnm|
|
153
|
+
klass.class_eval do
|
154
|
+
attr_accessor symnm
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# create accessor routines for an instance variable, with variable name
|
161
|
+
# determined by the class in which the instance variable was declared.
|
162
|
+
# [+klass+] Class object into which to generate the variable accessor
|
163
|
+
# functions.
|
164
|
+
# [+nmlist+] List of symbols for which to create accessor routines.
|
165
|
+
# Uses Safer::IVar.symbol_names to determine the symbol names of the
|
166
|
+
# accessor routines.
|
167
|
+
# For example, the listing:
|
168
|
+
# class MyClass
|
169
|
+
# class SubClass
|
170
|
+
# Safer::IVar.instance_variable(self, :foo, :bar)
|
171
|
+
# end
|
172
|
+
# end
|
173
|
+
# is equivalent to the following code:
|
174
|
+
# class MyClass
|
175
|
+
# class SubClass
|
176
|
+
# attr_accessor :myclass_subclass__foo
|
177
|
+
# attr_accessor :myclass_subclass__bar
|
178
|
+
# end
|
179
|
+
# end
|
180
|
+
# The name-mangling signals that these instance variables are, for all
|
181
|
+
# intents and purposes, private to +klass+.
|
182
|
+
def instance_variable(klass, *nmlist)
|
183
|
+
self._instance_variable_internal(
|
184
|
+
klass, self.class_symbol_prefix(klass), nmlist)
|
185
|
+
end # Safer::IVarFactory#instance_variable
|
186
|
+
|
187
|
+
##
|
188
|
+
# Used internally. See Safer::IVarFactory#export_reader, and
|
189
|
+
# Safer::IVarFactory::Dsl#reader .
|
190
|
+
def _export_reader_internal(klass, prefix, nmlist)
|
191
|
+
symlist = self._symbol_names_internal(prefix, nmlist)
|
192
|
+
nmlist.size.times do |index|
|
193
|
+
thisnm = nmlist[index]
|
194
|
+
thissym = symlist[index]
|
195
|
+
klass.class_eval("def #{thisnm} ; self.#{thissym} ; end")
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
##
|
200
|
+
# export the reader routines for instance variables in a safer way.
|
201
|
+
# [+klass+] Class object for which to define reader accessors for
|
202
|
+
# instance variables defined by Safer::IVar.instance_variable.
|
203
|
+
# [+nmlist+] List of symbols for which to define reader accessors.
|
204
|
+
# Each symbol in +nmlist+ should have previously been given as
|
205
|
+
# an argument to Safer::IVar.instance_variable(+klass+).
|
206
|
+
def export_reader(klass, *nmlist)
|
207
|
+
self._export_reader_internal(
|
208
|
+
klass, self.class_symbol_prefix(klass), nmlist)
|
209
|
+
end # Safer::IVarFactory#export_reader
|
210
|
+
|
211
|
+
##
|
212
|
+
# Used internally. See Safer::IVarFactory#export_writer, and
|
213
|
+
# Safer::IVarFactory::Dsl#writer .
|
214
|
+
def _export_writer_internal(klass, prefix, nmlist)
|
215
|
+
symlist = self._symbol_names_internal(prefix, nmlist)
|
216
|
+
nmlist.size.times do |index|
|
217
|
+
thisnm = nmlist[index]
|
218
|
+
thissym = symlist[index]
|
219
|
+
klass.class_eval(
|
220
|
+
"def #{thisnm}=(value) ; self.#{thissym} = value ; end"
|
221
|
+
)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
##
|
226
|
+
# export the writer routines for instance variables in a safer way.
|
227
|
+
# [+klass+] Class object for which to define writer accessors for
|
228
|
+
# instance variables defined by Safer::IVar.instance_variable.
|
229
|
+
# [+nmlist+] List of symbols for which to define writer accessors.
|
230
|
+
# Each symbol in +nmlist+ should have previously been given as
|
231
|
+
# an argument to Safer::IVar.instance_variable(+klass+).
|
232
|
+
def export_writer(klass, *nmlist)
|
233
|
+
self._export_writer_internal(
|
234
|
+
klass, self.class_symbol_prefix(klass), nmlist)
|
235
|
+
end # Safer::IVarFactory#export_writer
|
236
|
+
|
237
|
+
##
|
238
|
+
# Used internally. See Safer::IVarFactory#export_accessor, and
|
239
|
+
# Safer::IVarFactory::Dsl#accessor .
|
240
|
+
def _export_accessor_internal(klass, prefix, nmlist)
|
241
|
+
self._export_reader_internal(klass, prefix, nmlist)
|
242
|
+
self._export_writer_internal(klass, prefix, nmlist)
|
243
|
+
end
|
244
|
+
|
245
|
+
##
|
246
|
+
# export both reader and writer routines for instance variables in a
|
247
|
+
# safer way.
|
248
|
+
# [+klass+] Class object for which to define accessors for
|
249
|
+
# instance variables defined by Safer::IVar.instance_variable.
|
250
|
+
# [+nmlist+] List of symbols for which to define accessors.
|
251
|
+
# Each symbol in +nmlist+ should have previously been given as
|
252
|
+
# an argument to Safer::IVar.instance_variable(+klass+).
|
253
|
+
def export_accessor(klass, *nmlist)
|
254
|
+
self._export_accessor_internal(
|
255
|
+
klass, self.class_symbol_prefix(klass), nmlist)
|
256
|
+
end # Safer::IVarFactory#export_accessor
|
257
|
+
|
258
|
+
##
|
259
|
+
# Yield DSL interface for instance variable creation to caller. See
|
260
|
+
# Safer::IVarFactory::Dsl for the DSL API.
|
261
|
+
# [+klass+] Class object used by DSL interface.
|
262
|
+
def run(klass)
|
263
|
+
yield(Dsl.new(self, klass))
|
264
|
+
end # Safer::IVarFactory#run
|
265
|
+
|
266
|
+
end # Safer::IVarFactory
|
267
|
+
|
268
|
+
require 'safer/ivarfactory/dsl.rb'
|
269
|
+
require 'safer/ivarfactory/prefix.rb'
|
@@ -0,0 +1,49 @@
|
|
1
|
+
##
|
2
|
+
# Present a lightweight interface to instance variable creation. An
|
3
|
+
# instance of this class is given to the user as an argument to the yield
|
4
|
+
# block passed to Safer::IVarFactory#run. For example:
|
5
|
+
# class Foo
|
6
|
+
# Safer::IVar.run(self) do |ivar|
|
7
|
+
# # ivar is an instance of Safer::IVarFactory::Dsl
|
8
|
+
# end
|
9
|
+
# end
|
10
|
+
class Safer::IVarFactory::Dsl
|
11
|
+
##
|
12
|
+
# Initialize a DSL object.
|
13
|
+
# [+factory+] IVarFactory to use for creating instance variables.
|
14
|
+
# [+klass+] +klass argument to Safer::IVarFactory#run; create instance
|
15
|
+
# variables in this class.
|
16
|
+
def initialize(factory, klass)
|
17
|
+
@factory = factory
|
18
|
+
@klass = klass
|
19
|
+
@symbol_prefix = factory.class_symbol_prefix(klass)
|
20
|
+
end # Safer::IVarFactory::Dsl#initialize
|
21
|
+
|
22
|
+
##
|
23
|
+
# Define Safer::IVarFactory instance variables for the class attached to
|
24
|
+
# this DSL object. See Safer::IVarFactory#instance_variable
|
25
|
+
def ivar(*args)
|
26
|
+
@factory._instance_variable_internal(@klass, @symbol_prefix, args)
|
27
|
+
end # Safer::IVarFactory::Dsl#ivar
|
28
|
+
|
29
|
+
##
|
30
|
+
# Export readers for Safer::IVarFactory instance variables in the class
|
31
|
+
# attached to this DSL object. See Safer::IVarFactory#export_reader
|
32
|
+
def reader(*args)
|
33
|
+
@factory._export_reader_internal(@klass, @symbol_prefix, args)
|
34
|
+
end # Safer::IVarFactory::Dsl#reader
|
35
|
+
|
36
|
+
##
|
37
|
+
# Export writers for Safer::IVarFactory instance variables in the class
|
38
|
+
# attached to this DSL object. See Safer::IVarFactory#export_reader
|
39
|
+
def writer(*args)
|
40
|
+
@factory._export_writer_internal(@klass, @symbol_prefix, args)
|
41
|
+
end # Safer::IVarFactory::Dsl#writer
|
42
|
+
|
43
|
+
##
|
44
|
+
# Export accessors for Safer::IVarFactory instance variables in the class
|
45
|
+
# attached to this DSL object. See Safer::IVarFactory#export_reader
|
46
|
+
def accessor(*args)
|
47
|
+
@factory._export_accessor_internal(@klass, @symbol_prefix, args)
|
48
|
+
end # Safer::IVarFactory::Dsl#accessor
|
49
|
+
end # Safer::IVarFactory::Dsl
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Safer::IVarFactory::Prefix
|
2
|
+
def self.is_anon(component)
|
3
|
+
/^\#<.*>$/.match(component)
|
4
|
+
end
|
5
|
+
|
6
|
+
class Full < Safer::IVarFactory::Prefix
|
7
|
+
def self.class_symbol_prefix(klass)
|
8
|
+
klass.to_s.split('::').inject(nil) do |memo, el|
|
9
|
+
# reset the symbol name when an anonymous class is encountered.
|
10
|
+
if self.is_anon(el)
|
11
|
+
nil
|
12
|
+
elsif memo
|
13
|
+
memo + '_' + el.downcase
|
14
|
+
else
|
15
|
+
el.downcase
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end # Safer::IVarFactory::Prefix::Full#class_symbol_prefix
|
19
|
+
end # Safer::IVarFactory::Prefix::Full
|
20
|
+
|
21
|
+
class Last < Safer::IVarFactory::Prefix
|
22
|
+
def self.class_symbol_prefix(klass)
|
23
|
+
el = klass.to_s.split('::').reverse.find do |obj|
|
24
|
+
! self.is_anon(obj)
|
25
|
+
end
|
26
|
+
el.downcase
|
27
|
+
end
|
28
|
+
end # Safer::IVarFactory::Prefix::Last
|
29
|
+
end # Safer::IVarFactory::Prefix
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 15
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 0
|
8
7
|
- 4
|
9
|
-
-
|
10
|
-
version: 0.4.
|
8
|
+
- 1
|
9
|
+
version: 0.4.1
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Aidan Cully
|
@@ -26,7 +25,6 @@ dependencies:
|
|
26
25
|
requirements:
|
27
26
|
- - ">="
|
28
27
|
- !ruby/object:Gem::Version
|
29
|
-
hash: 7
|
30
28
|
segments:
|
31
29
|
- 2
|
32
30
|
- 0
|
@@ -42,12 +40,11 @@ dependencies:
|
|
42
40
|
requirements:
|
43
41
|
- - ">="
|
44
42
|
- !ruby/object:Gem::Version
|
45
|
-
hash: 19
|
46
43
|
segments:
|
47
44
|
- 2
|
48
|
-
-
|
49
|
-
-
|
50
|
-
version: 2.
|
45
|
+
- 7
|
46
|
+
- 0
|
47
|
+
version: 2.7.0
|
51
48
|
type: :development
|
52
49
|
version_requirements: *id002
|
53
50
|
description: |-
|
@@ -81,13 +78,16 @@ files:
|
|
81
78
|
- Rakefile
|
82
79
|
- lib/safer.rb
|
83
80
|
- lib/safer/ivar.rb
|
81
|
+
- lib/safer/ivarfactory.rb
|
82
|
+
- lib/safer/ivarfactory/dsl.rb
|
83
|
+
- lib/safer/ivarfactory/prefix.rb
|
84
84
|
- lib/safer/protocol.rb
|
85
85
|
- lib/safer/hashprotocol.rb
|
86
86
|
- test/test_safer_ivar.rb
|
87
|
-
- test/test_safer_protocol.rb
|
88
|
-
- test/test_safer_hashprotocol.rb
|
89
87
|
- test/test_safer_ivar_run.rb
|
90
88
|
- test/test_safer_ivarfactory.rb
|
89
|
+
- test/test_safer_protocol.rb
|
90
|
+
- test/test_safer_hashprotocol.rb
|
91
91
|
has_rdoc: true
|
92
92
|
homepage: http://safer.rubyforge.org
|
93
93
|
licenses: []
|
@@ -103,7 +103,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
103
|
requirements:
|
104
104
|
- - ">="
|
105
105
|
- !ruby/object:Gem::Version
|
106
|
-
hash: 3
|
107
106
|
segments:
|
108
107
|
- 0
|
109
108
|
version: "0"
|
@@ -112,7 +111,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
111
|
requirements:
|
113
112
|
- - ">="
|
114
113
|
- !ruby/object:Gem::Version
|
115
|
-
hash: 3
|
116
114
|
segments:
|
117
115
|
- 0
|
118
116
|
version: "0"
|