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/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