Class: Safer::IVar
- Inherits:
-
Object
- Object
- Safer::IVar
- Defined in:
- lib/safer/ivar.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.
Class Method Summary collapse
-
._symbol_names(klass, 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 nicer way.
-
.export_reader(klass, *nmlist) ⇒ Object
export the reader routines for instance variables in a nicer way.
-
.export_writer(klass, *nmlist) ⇒ Object
export the writer routines for instance variables in a nicer way.
-
.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.
-
.symbol_names(klass, *nmlist) ⇒ Object
compute accessor routine symbol names, so that the names include the entire class namespace.
Class Method Details
._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
.
115 116 117 118 119 120 121 |
# File 'lib/safer/ivar.rb', line 115 def self._symbol_names(klass, nmlist) kname = self.class_symbol_prefix(klass) nmlist.map do |nm| (kname + '__' + 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.
96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/safer/ivar.rb', line 96 def self.class_symbol_prefix(klass) klass.to_s.split('::').inject(nil) do |memo, el| # reset the symbol name when an anonymous class is encountered. if /^\#<.*>$/.match(el) nil elsif memo memo + '_' + el.downcase else el.downcase end end end |
.export_accessor(klass, *nmlist) ⇒ Object
export both reader and writer routines for instance variables in a nicer 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
).
216 217 218 219 |
# File 'lib/safer/ivar.rb', line 216 def self.export_accessor(klass, *nmlist) self.export_reader(klass, *nmlist) self.export_writer(klass, *nmlist) end |
.export_reader(klass, *nmlist) ⇒ Object
export the reader routines for instance variables in a nicer 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
).
181 182 183 184 185 186 187 188 |
# File 'lib/safer/ivar.rb', line 181 def self.export_reader(klass, *nmlist) symlist = self.symbol_names(klass, *nmlist) nmlist.size.times do |index| thisnm = nmlist[index] thissym = symlist[index] klass.class_eval("def #{thisnm} ; self.#{thissym} ; end") end end |
.export_writer(klass, *nmlist) ⇒ Object
export the writer routines for instance variables in a nicer 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
).
197 198 199 200 201 202 203 204 205 206 |
# File 'lib/safer/ivar.rb', line 197 def self.export_writer(klass, *nmlist) symlist = self.symbol_names(klass, *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(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
.
166 167 168 169 170 171 172 |
# File 'lib/safer/ivar.rb', line 166 def self.instance_variable(klass, *nmlist) self.symbol_names(klass, *nmlist).each do |symnm| klass.class_eval do attr_accessor symnm end end end |
.symbol_names(klass, *nmlist) ⇒ Object
compute accessor routine symbol names, so that the names include the entire class namespace.
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
139 140 141 |
# File 'lib/safer/ivar.rb', line 139 def self.symbol_names(klass, *nmlist) self._symbol_names(klass, nmlist) end |