Class: Safer::IVarFactory
- Inherits:
-
Object
- Object
- Safer::IVarFactory
- Defined in:
- lib/safer/ivarfactory.rb
Overview
Create accessor functions for instance variables, in which the accessor function is prefixed with the name of the class in which the instance variable is defined.
Usage
Create one or more instance variables using instance_variable . For example, the following code:
class Outer
Safer::IVar.instance_variable(self, :variable)
class Inner < Outer
Safer::IVar.instance_variable(self, :variable)
end
end
puts(Outer.instance_methods.grep(/__variable/).inspect)
puts(Outer::Inner.instance_methods.grep(/__variable/).inspect)
produces the following output:
["outer__variable", "outer__variable="]
["outer_inner__variable", "outer_inner__variable=", "outer__variable", "outer__variable="]
Accessors for Safer::IVar-defined instance variables can be created using export_reader, export_writer, and export_accessor .
Rationale
Safer::IVar is intended to improve the clarity and consistency of ruby code in at least the following (related) ways:
-
Reducing the probability of instance variable usage errors.
-
Documenting the instance variables attached to a class.
Error Reduction
Safer::IVar should help in reducing errors in at least the following ways:
-
Encapsulation errors. Traditional ruby instance variables defined by one class are transparently accessible to all subclasses. They are not, however, part of the public interface to a class (unless an accessor is defined), which means they are not generally documented. These two factors create a situation in which it is quite possible that a subclass inadvertantly re-define an instance variable in such a way as to render the subclass unusable. Bugs of this sort can be very difficult to resolve.
Because instance variables generated by Safer::IVar are prefixed with a string derived from the class in which the variable is defined, it is much less likely that developers inadvertantly re-define instance variables in sub-classes, dramatically reducing the likelihood of this type of error.
-
Misspelling errors. For example, suppose you typically write software using the en-us dialect, and have an object with a @color instance variable. A en-uk speaker then submits a patch that sets the default color to green:
--- ex1.rb 2010-09-28 06:24:52.000000000 -0400 +++ ex2.rb 2010-09-28 06:25:00.000000000 -0400 @@ -1,6 +1,7 @@ class Foo def initialize @size = 3 + @colour = "green" end attr_reader :color end
This code will not raise any exceptions, but its behavior does not match developer intent. On the other hand, using Safer::IVar
--- ex1-safer.rb 2010-09-28 06:31:51.000000000 -0400 +++ ex2-safer.rb 2010-09-28 06:32:08.000000000 -0400 @@ -1,7 +1,8 @@ class Foo Safer::IVar.instance_variable(self, :size, :color) Safer::IVar.export_reader(self, :color) def initialize self.foo__size = 3 + self.foo__colour = "green" end end
The new code will raise an exception at the call to Foo.new, making it much less likely that the error will go undetected.
Documentation
Traditional ruby instance variables are defined and used in an ad hoc manner. As such, there is no natural location in which the instance variables defined by a class can be documented, and no obvious way to determine the set of instance variables used in a class. Safer::IVar instance variables will all be associated with a single call to Safer::IVar.instance_variable. This provides both a natural location for documenting an instance variable’s interpretation, as well as a string to search for when determining the set of instance variables defined by a class.
Defined Under Namespace
Instance Method Summary collapse
-
#_export_accessor_internal(klass, prefix, nmlist) ⇒ Object
Used internally.
-
#_export_reader_internal(klass, prefix, nmlist) ⇒ Object
Used internally.
-
#_export_writer_internal(klass, prefix, nmlist) ⇒ Object
Used internally.
-
#_instance_variable_internal(klass, prefix, nmlist) ⇒ Object
Used internally.
-
#_symbol_names(klass, nmlist) ⇒ Object
Used internally.
-
#_symbol_names_internal(prefix, nmlist) ⇒ Object
Used internally.
-
#class_symbol_prefix(klass) ⇒ Object
Given a Class object, derive the prefix string for the accessor functions that instance_variable will create.
-
#export_accessor(klass, *nmlist) ⇒ Object
export both reader and writer routines for instance variables in a safer way.
-
#export_reader(klass, *nmlist) ⇒ Object
export the reader routines for instance variables in a safer way.
-
#export_writer(klass, *nmlist) ⇒ Object
export the writer routines for instance variables in a safer way.
-
#initialize(prefix) ⇒ IVarFactory
constructor
A new instance of IVarFactory.
-
#instance_variable(klass, *nmlist) ⇒ Object
create accessor routines for an instance variable, with variable name determined by the class in which the instance variable was declared.
-
#run(klass) {|Dsl.new(self, klass)| ... } ⇒ Object
Yield DSL interface for instance variable creation to caller.
-
#symbol_names(klass, *nmlist) ⇒ Object
compute accessor routine symbol names, where the symbol name prefix is derived from the class name.
Constructor Details
#initialize(prefix) ⇒ IVarFactory
Returns a new instance of IVarFactory.
93 94 95 |
# File 'lib/safer/ivarfactory.rb', line 93 def initialize(prefix) @prefix = prefix end |
Instance Method Details
#_export_accessor_internal(klass, prefix, nmlist) ⇒ Object
Used internally. See Safer::IVarFactory#export_accessor, and Safer::IVarFactory::Dsl#accessor .
240 241 242 243 |
# File 'lib/safer/ivarfactory.rb', line 240 def _export_accessor_internal(klass, prefix, nmlist) self._export_reader_internal(klass, prefix, nmlist) self._export_writer_internal(klass, prefix, nmlist) end |
#_export_reader_internal(klass, prefix, nmlist) ⇒ Object
Used internally. See Safer::IVarFactory#export_reader, and Safer::IVarFactory::Dsl#reader .
190 191 192 193 194 195 196 197 |
# File 'lib/safer/ivarfactory.rb', line 190 def _export_reader_internal(klass, prefix, nmlist) symlist = self._symbol_names_internal(prefix, nmlist) nmlist.size.times do |index| thisnm = nmlist[index] thissym = symlist[index] klass.class_eval("def #{thisnm} ; self.#{thissym} ; end") end end |
#_export_writer_internal(klass, prefix, nmlist) ⇒ Object
Used internally. See Safer::IVarFactory#export_writer, and Safer::IVarFactory::Dsl#writer .
214 215 216 217 218 219 220 221 222 223 |
# File 'lib/safer/ivarfactory.rb', line 214 def _export_writer_internal(klass, prefix, nmlist) symlist = self._symbol_names_internal(prefix, nmlist) nmlist.size.times do |index| thisnm = nmlist[index] thissym = symlist[index] klass.class_eval( "def #{thisnm}=(value) ; self.#{thissym} = value ; end" ) end end |
#_instance_variable_internal(klass, prefix, nmlist) ⇒ Object
Used internally. See Safer::IVarFactory#instance_variable, and Safer::IVarFactory::Dsl#ivar .
151 152 153 154 155 156 157 |
# File 'lib/safer/ivarfactory.rb', line 151 def _instance_variable_internal(klass, prefix, nmlist) self._symbol_names_internal(prefix, nmlist).each do |symnm| klass.class_eval do attr_accessor symnm end end end |
#_symbol_names(klass, nmlist) ⇒ Object
Used internally.
klass
-
Class object for which to generate a symbol name.
nmlist
-
Array of
Symbol
objects. - return
-
Array of
Symbol
objects generated fromklass
and each element ofnmlist
.
123 124 125 126 |
# File 'lib/safer/ivarfactory.rb', line 123 def _symbol_names(klass, nmlist) kname = self.class_symbol_prefix(klass) self._symbol_names_internal(kname, nmlist) end |
#_symbol_names_internal(prefix, nmlist) ⇒ Object
Used internally.
prefix
-
Prefix of generated symbol names.
nmlist
-
Array of
Symbol
objects. - return
-
Array of
Symbol
objects. Each element will be the concatenation of theprefix
, ‘__’, and the corresponding element ofnmlist
111 112 113 114 115 |
# File 'lib/safer/ivarfactory.rb', line 111 def _symbol_names_internal(prefix, nmlist) nmlist.map do |nm| (prefix + '__' + nm.to_s).to_sym end end |
#class_symbol_prefix(klass) ⇒ Object
Given a Class object, derive the prefix string for the accessor functions that instance_variable will create.
100 101 102 |
# File 'lib/safer/ivarfactory.rb', line 100 def class_symbol_prefix(klass) @prefix.class_symbol_prefix(klass) end |
#export_accessor(klass, *nmlist) ⇒ Object
export both reader and writer routines for instance variables in a safer way.
klass
-
Class object for which to define accessors for instance variables defined by Safer::IVar.instance_variable.
nmlist
-
List of symbols for which to define accessors. Each symbol in
nmlist
should have previously been given as an argument to Safer::IVar.instance_variable(klass
).
253 254 255 256 |
# File 'lib/safer/ivarfactory.rb', line 253 def export_accessor(klass, *nmlist) self._export_accessor_internal( klass, self.class_symbol_prefix(klass), nmlist) end |
#export_reader(klass, *nmlist) ⇒ Object
export the reader routines for instance variables in a safer way.
klass
-
Class object for which to define reader accessors for instance variables defined by Safer::IVar.instance_variable.
nmlist
-
List of symbols for which to define reader accessors. Each symbol in
nmlist
should have previously been given as an argument to Safer::IVar.instance_variable(klass
).
206 207 208 209 |
# File 'lib/safer/ivarfactory.rb', line 206 def export_reader(klass, *nmlist) self._export_reader_internal( klass, self.class_symbol_prefix(klass), nmlist) end |
#export_writer(klass, *nmlist) ⇒ Object
export the writer routines for instance variables in a safer way.
klass
-
Class object for which to define writer accessors for instance variables defined by Safer::IVar.instance_variable.
nmlist
-
List of symbols for which to define writer accessors. Each symbol in
nmlist
should have previously been given as an argument to Safer::IVar.instance_variable(klass
).
232 233 234 235 |
# File 'lib/safer/ivarfactory.rb', line 232 def export_writer(klass, *nmlist) self._export_writer_internal( klass, self.class_symbol_prefix(klass), nmlist) end |
#instance_variable(klass, *nmlist) ⇒ Object
create accessor routines for an instance variable, with variable name determined by the class in which the instance variable was declared.
klass
-
Class object into which to generate the variable accessor functions.
nmlist
-
List of symbols for which to create accessor routines.
Uses Safer::IVar.symbol_names to determine the symbol names of the accessor routines. For example, the listing:
class MyClass
class SubClass
Safer::IVar.instance_variable(self, :foo, :bar)
end
end
is equivalent to the following code:
class MyClass
class SubClass
attr_accessor :myclass_subclass__foo
attr_accessor :myclass_subclass__bar
end
end
The name-mangling signals that these instance variables are, for all intents and purposes, private to klass
.
182 183 184 185 |
# File 'lib/safer/ivarfactory.rb', line 182 def instance_variable(klass, *nmlist) self._instance_variable_internal( klass, self.class_symbol_prefix(klass), nmlist) end |
#run(klass) {|Dsl.new(self, klass)| ... } ⇒ Object
Yield DSL interface for instance variable creation to caller. See Safer::IVarFactory::Dsl for the DSL API.
klass
-
Class object used by DSL interface.
262 263 264 |
# File 'lib/safer/ivarfactory.rb', line 262 def run(klass) yield(Dsl.new(self, klass)) end |
#symbol_names(klass, *nmlist) ⇒ Object
compute accessor routine symbol names, where the symbol name prefix is derived from the class name.
klass
-
Class object for which to generate a symbol name.
nmlist
-
Array of
Symbol
objects. - return
-
Array of
Symbol
objects generated fromklass
and each element ofnmlist
.
For example, given the following listing:
class OuterClass
class InnerClass
SYMNM = Safer::IVar.symbol_names(self, :foo)
puts(SYMNM.to_s)
end
end
the following output would be produced:
outerclass_innerclass__foo
144 145 146 |
# File 'lib/safer/ivarfactory.rb', line 144 def symbol_names(klass, *nmlist) self._symbol_names(klass, nmlist) end |