safer 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/safer/ivar.rb CHANGED
@@ -1,222 +1,7 @@
1
+ require 'safer/ivarfactory'
2
+
1
3
  class Safer
2
4
  ##
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
- #
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