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
@colorinstance 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 endThis 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 from klass and each
element of nmlist.
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 the prefix, '__', and the corresponding
element of nmlist
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 from klass and each
element of nmlist.
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 |