safer 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ === 0.4.1 / 2011-10-29
2
+
3
+ * Fixed Manifest.txt.
4
+
1
5
  === 0.4.0 / 2011-10-29
2
6
 
3
7
  * Safer::IVar functionality moved into Safer::IVarFactory class.
@@ -5,8 +5,13 @@ README.rdoc
5
5
  Rakefile
6
6
  lib/safer.rb
7
7
  lib/safer/ivar.rb
8
+ lib/safer/ivarfactory.rb
9
+ lib/safer/ivarfactory/dsl.rb
10
+ lib/safer/ivarfactory/prefix.rb
8
11
  lib/safer/protocol.rb
9
12
  lib/safer/hashprotocol.rb
10
13
  test/test_safer_ivar.rb
14
+ test/test_safer_ivar_run.rb
15
+ test/test_safer_ivarfactory.rb
11
16
  test/test_safer_protocol.rb
12
17
  test/test_safer_hashprotocol.rb
@@ -15,5 +15,5 @@
15
15
  class Safer
16
16
  ##
17
17
  # Current release of Safer.
18
- VERSION = "0.4.0"
18
+ VERSION = "0.4.1"
19
19
  end
@@ -0,0 +1,269 @@
1
+ require 'safer'
2
+
3
+ ##
4
+ # Create accessor functions for instance variables, in which the accessor
5
+ # function is prefixed with the name of the class in which the instance
6
+ # variable is defined.
7
+ #
8
+ # ==Usage
9
+ # Create one or more instance variables using instance_variable .
10
+ # For example, the following code:
11
+ # class Outer
12
+ # Safer::IVar.instance_variable(self, :variable)
13
+ # class Inner < Outer
14
+ # Safer::IVar.instance_variable(self, :variable)
15
+ # end
16
+ # end
17
+ # puts(Outer.instance_methods.grep(/__variable/).inspect)
18
+ # puts(Outer::Inner.instance_methods.grep(/__variable/).inspect)
19
+ # produces the following output:
20
+ # ["outer__variable", "outer__variable="]
21
+ # ["outer_inner__variable", "outer_inner__variable=", "outer__variable", "outer__variable="]
22
+ #
23
+ # Accessors for Safer::IVar-defined instance variables can be created using
24
+ # export_reader, export_writer, and export_accessor .
25
+ #
26
+ # ==Rationale
27
+ # Safer::IVar is intended to improve the clarity and consistency of ruby
28
+ # code in at least the following (related) ways:
29
+ #
30
+ # 1. Reducing the probability of instance variable usage errors.
31
+ # 2. Documenting the instance variables attached to a class.
32
+ #
33
+ # ===Error Reduction
34
+ # Safer::IVar should help in reducing errors in at least the following ways:
35
+ #
36
+ # * Encapsulation errors. Traditional ruby instance variables defined by one
37
+ # class are transparently accessible to all subclasses. They are not,
38
+ # however, part of the public interface to a class (unless an accessor is
39
+ # defined), which means they are not generally documented. These two
40
+ # factors create a situation in which it is quite possible that a subclass
41
+ # inadvertantly re-define an instance variable in such a way as to render
42
+ # the subclass unusable. Bugs of this sort can be very difficult to
43
+ # resolve.
44
+ #
45
+ # Because instance variables generated by Safer::IVar are prefixed with a
46
+ # string derived from the class in which the variable is defined, it is much
47
+ # less likely that developers _inadvertantly_ re-define instance variables
48
+ # in sub-classes, dramatically reducing the likelihood of this type of
49
+ # error.
50
+ #
51
+ # * Misspelling errors. For example, suppose you typically write software
52
+ # using the en-us dialect, and have an object with a +@color+ instance
53
+ # variable. A en-uk speaker then submits a patch that sets the default
54
+ # color to green:
55
+ # --- ex1.rb 2010-09-28 06:24:52.000000000 -0400
56
+ # +++ ex2.rb 2010-09-28 06:25:00.000000000 -0400
57
+ # @@ -1,6 +1,7 @@
58
+ # class Foo
59
+ # def initialize
60
+ # @size = 3
61
+ # + @colour = "green"
62
+ # end
63
+ # attr_reader :color
64
+ # end
65
+ # This code will not raise any exceptions, but its behavior does not match
66
+ # developer intent. On the other hand, using Safer::IVar
67
+ # --- ex1-safer.rb 2010-09-28 06:31:51.000000000 -0400
68
+ # +++ ex2-safer.rb 2010-09-28 06:32:08.000000000 -0400
69
+ # @@ -1,7 +1,8 @@
70
+ # class Foo
71
+ # Safer::IVar.instance_variable(self, :size, :color)
72
+ # Safer::IVar.export_reader(self, :color)
73
+ # def initialize
74
+ # self.foo__size = 3
75
+ # + self.foo__colour = "green"
76
+ # end
77
+ # end
78
+ # The new code will raise an exception at the call to Foo.new, making it
79
+ # much less likely that the error will go undetected.
80
+ #
81
+ # ===Documentation
82
+ # Traditional ruby instance variables are defined and used in an <i>ad hoc</i>
83
+ # manner. As such, there is no natural location in which the instance
84
+ # variables defined by a class can be documented, and no obvious way to
85
+ # determine the set of instance variables used in a class. Safer::IVar
86
+ # instance variables will all be associated with a single call to
87
+ # Safer::IVar.instance_variable. This provides both a natural location for
88
+ # documenting an instance variable's interpretation, as well as a string to
89
+ # search for when determining the set of instance variables defined by a
90
+ # class.
91
+ #
92
+ class Safer::IVarFactory
93
+ def initialize(prefix)
94
+ @prefix = prefix
95
+ end
96
+
97
+ ##
98
+ # Given a Class object, derive the prefix string for the accessor functions
99
+ # that instance_variable will create.
100
+ def class_symbol_prefix(klass)
101
+ @prefix.class_symbol_prefix(klass)
102
+ end # Safer::IVarFactory#class_symbol_prefix
103
+
104
+ ##
105
+ # Used internally.
106
+ # [+prefix+] Prefix of generated symbol names.
107
+ # [+nmlist+] Array of +Symbol+ objects.
108
+ # [_return_] Array of +Symbol+ objects. Each element will be the
109
+ # concatenation of the +prefix+, '__', and the corresponding
110
+ # element of +nmlist+
111
+ def _symbol_names_internal(prefix, nmlist)
112
+ nmlist.map do |nm|
113
+ (prefix + '__' + nm.to_s).to_sym
114
+ end
115
+ end
116
+
117
+ ##
118
+ # Used internally.
119
+ # [+klass+] Class object for which to generate a symbol name.
120
+ # [+nmlist+] Array of +Symbol+ objects.
121
+ # [_return_] Array of +Symbol+ objects generated from +klass+ and each
122
+ # element of +nmlist+.
123
+ def _symbol_names(klass, nmlist)
124
+ kname = self.class_symbol_prefix(klass)
125
+ self._symbol_names_internal(kname, nmlist)
126
+ end # Safer::IVarFactory#_symbol_names
127
+
128
+ ##
129
+ # compute accessor routine symbol names, where the symbol name prefix is
130
+ # derived from the class name.
131
+ # [+klass+] Class object for which to generate a symbol name.
132
+ # [+nmlist+] Array of +Symbol+ objects.
133
+ # [_return_] Array of +Symbol+ objects generated from +klass+ and each
134
+ # element of +nmlist+.
135
+ # For example, given the following listing:
136
+ # class OuterClass
137
+ # class InnerClass
138
+ # SYMNM = Safer::IVar.symbol_names(self, :foo)
139
+ # puts(SYMNM.to_s)
140
+ # end
141
+ # end
142
+ # the following output would be produced:
143
+ # outerclass_innerclass__foo
144
+ def symbol_names(klass, *nmlist)
145
+ self._symbol_names(klass, nmlist)
146
+ end # Safer::IVarFactory#symbol_names
147
+
148
+ ##
149
+ # Used internally. See Safer::IVarFactory#instance_variable, and
150
+ # Safer::IVarFactory::Dsl#ivar .
151
+ def _instance_variable_internal(klass, prefix, nmlist)
152
+ self._symbol_names_internal(prefix, nmlist).each do |symnm|
153
+ klass.class_eval do
154
+ attr_accessor symnm
155
+ end
156
+ end
157
+ end
158
+
159
+ ##
160
+ # create accessor routines for an instance variable, with variable name
161
+ # determined by the class in which the instance variable was declared.
162
+ # [+klass+] Class object into which to generate the variable accessor
163
+ # functions.
164
+ # [+nmlist+] List of symbols for which to create accessor routines.
165
+ # Uses Safer::IVar.symbol_names to determine the symbol names of the
166
+ # accessor routines.
167
+ # For example, the listing:
168
+ # class MyClass
169
+ # class SubClass
170
+ # Safer::IVar.instance_variable(self, :foo, :bar)
171
+ # end
172
+ # end
173
+ # is equivalent to the following code:
174
+ # class MyClass
175
+ # class SubClass
176
+ # attr_accessor :myclass_subclass__foo
177
+ # attr_accessor :myclass_subclass__bar
178
+ # end
179
+ # end
180
+ # The name-mangling signals that these instance variables are, for all
181
+ # intents and purposes, private to +klass+.
182
+ def instance_variable(klass, *nmlist)
183
+ self._instance_variable_internal(
184
+ klass, self.class_symbol_prefix(klass), nmlist)
185
+ end # Safer::IVarFactory#instance_variable
186
+
187
+ ##
188
+ # Used internally. See Safer::IVarFactory#export_reader, and
189
+ # Safer::IVarFactory::Dsl#reader .
190
+ def _export_reader_internal(klass, prefix, nmlist)
191
+ symlist = self._symbol_names_internal(prefix, nmlist)
192
+ nmlist.size.times do |index|
193
+ thisnm = nmlist[index]
194
+ thissym = symlist[index]
195
+ klass.class_eval("def #{thisnm} ; self.#{thissym} ; end")
196
+ end
197
+ end
198
+
199
+ ##
200
+ # export the reader routines for instance variables in a safer way.
201
+ # [+klass+] Class object for which to define reader accessors for
202
+ # instance variables defined by Safer::IVar.instance_variable.
203
+ # [+nmlist+] List of symbols for which to define reader accessors.
204
+ # Each symbol in +nmlist+ should have previously been given as
205
+ # an argument to Safer::IVar.instance_variable(+klass+).
206
+ def export_reader(klass, *nmlist)
207
+ self._export_reader_internal(
208
+ klass, self.class_symbol_prefix(klass), nmlist)
209
+ end # Safer::IVarFactory#export_reader
210
+
211
+ ##
212
+ # Used internally. See Safer::IVarFactory#export_writer, and
213
+ # Safer::IVarFactory::Dsl#writer .
214
+ def _export_writer_internal(klass, prefix, nmlist)
215
+ symlist = self._symbol_names_internal(prefix, nmlist)
216
+ nmlist.size.times do |index|
217
+ thisnm = nmlist[index]
218
+ thissym = symlist[index]
219
+ klass.class_eval(
220
+ "def #{thisnm}=(value) ; self.#{thissym} = value ; end"
221
+ )
222
+ end
223
+ end
224
+
225
+ ##
226
+ # export the writer routines for instance variables in a safer way.
227
+ # [+klass+] Class object for which to define writer accessors for
228
+ # instance variables defined by Safer::IVar.instance_variable.
229
+ # [+nmlist+] List of symbols for which to define writer accessors.
230
+ # Each symbol in +nmlist+ should have previously been given as
231
+ # an argument to Safer::IVar.instance_variable(+klass+).
232
+ def export_writer(klass, *nmlist)
233
+ self._export_writer_internal(
234
+ klass, self.class_symbol_prefix(klass), nmlist)
235
+ end # Safer::IVarFactory#export_writer
236
+
237
+ ##
238
+ # Used internally. See Safer::IVarFactory#export_accessor, and
239
+ # Safer::IVarFactory::Dsl#accessor .
240
+ def _export_accessor_internal(klass, prefix, nmlist)
241
+ self._export_reader_internal(klass, prefix, nmlist)
242
+ self._export_writer_internal(klass, prefix, nmlist)
243
+ end
244
+
245
+ ##
246
+ # export both reader and writer routines for instance variables in a
247
+ # safer way.
248
+ # [+klass+] Class object for which to define accessors for
249
+ # instance variables defined by Safer::IVar.instance_variable.
250
+ # [+nmlist+] List of symbols for which to define accessors.
251
+ # Each symbol in +nmlist+ should have previously been given as
252
+ # an argument to Safer::IVar.instance_variable(+klass+).
253
+ def export_accessor(klass, *nmlist)
254
+ self._export_accessor_internal(
255
+ klass, self.class_symbol_prefix(klass), nmlist)
256
+ end # Safer::IVarFactory#export_accessor
257
+
258
+ ##
259
+ # Yield DSL interface for instance variable creation to caller. See
260
+ # Safer::IVarFactory::Dsl for the DSL API.
261
+ # [+klass+] Class object used by DSL interface.
262
+ def run(klass)
263
+ yield(Dsl.new(self, klass))
264
+ end # Safer::IVarFactory#run
265
+
266
+ end # Safer::IVarFactory
267
+
268
+ require 'safer/ivarfactory/dsl.rb'
269
+ require 'safer/ivarfactory/prefix.rb'
@@ -0,0 +1,49 @@
1
+ ##
2
+ # Present a lightweight interface to instance variable creation. An
3
+ # instance of this class is given to the user as an argument to the yield
4
+ # block passed to Safer::IVarFactory#run. For example:
5
+ # class Foo
6
+ # Safer::IVar.run(self) do |ivar|
7
+ # # ivar is an instance of Safer::IVarFactory::Dsl
8
+ # end
9
+ # end
10
+ class Safer::IVarFactory::Dsl
11
+ ##
12
+ # Initialize a DSL object.
13
+ # [+factory+] IVarFactory to use for creating instance variables.
14
+ # [+klass+] +klass argument to Safer::IVarFactory#run; create instance
15
+ # variables in this class.
16
+ def initialize(factory, klass)
17
+ @factory = factory
18
+ @klass = klass
19
+ @symbol_prefix = factory.class_symbol_prefix(klass)
20
+ end # Safer::IVarFactory::Dsl#initialize
21
+
22
+ ##
23
+ # Define Safer::IVarFactory instance variables for the class attached to
24
+ # this DSL object. See Safer::IVarFactory#instance_variable
25
+ def ivar(*args)
26
+ @factory._instance_variable_internal(@klass, @symbol_prefix, args)
27
+ end # Safer::IVarFactory::Dsl#ivar
28
+
29
+ ##
30
+ # Export readers for Safer::IVarFactory instance variables in the class
31
+ # attached to this DSL object. See Safer::IVarFactory#export_reader
32
+ def reader(*args)
33
+ @factory._export_reader_internal(@klass, @symbol_prefix, args)
34
+ end # Safer::IVarFactory::Dsl#reader
35
+
36
+ ##
37
+ # Export writers for Safer::IVarFactory instance variables in the class
38
+ # attached to this DSL object. See Safer::IVarFactory#export_reader
39
+ def writer(*args)
40
+ @factory._export_writer_internal(@klass, @symbol_prefix, args)
41
+ end # Safer::IVarFactory::Dsl#writer
42
+
43
+ ##
44
+ # Export accessors for Safer::IVarFactory instance variables in the class
45
+ # attached to this DSL object. See Safer::IVarFactory#export_reader
46
+ def accessor(*args)
47
+ @factory._export_accessor_internal(@klass, @symbol_prefix, args)
48
+ end # Safer::IVarFactory::Dsl#accessor
49
+ end # Safer::IVarFactory::Dsl
@@ -0,0 +1,29 @@
1
+ class Safer::IVarFactory::Prefix
2
+ def self.is_anon(component)
3
+ /^\#<.*>$/.match(component)
4
+ end
5
+
6
+ class Full < Safer::IVarFactory::Prefix
7
+ def self.class_symbol_prefix(klass)
8
+ klass.to_s.split('::').inject(nil) do |memo, el|
9
+ # reset the symbol name when an anonymous class is encountered.
10
+ if self.is_anon(el)
11
+ nil
12
+ elsif memo
13
+ memo + '_' + el.downcase
14
+ else
15
+ el.downcase
16
+ end
17
+ end
18
+ end # Safer::IVarFactory::Prefix::Full#class_symbol_prefix
19
+ end # Safer::IVarFactory::Prefix::Full
20
+
21
+ class Last < Safer::IVarFactory::Prefix
22
+ def self.class_symbol_prefix(klass)
23
+ el = klass.to_s.split('::').reverse.find do |obj|
24
+ ! self.is_anon(obj)
25
+ end
26
+ el.downcase
27
+ end
28
+ end # Safer::IVarFactory::Prefix::Last
29
+ end # Safer::IVarFactory::Prefix
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safer
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
7
  - 4
9
- - 0
10
- version: 0.4.0
8
+ - 1
9
+ version: 0.4.1
11
10
  platform: ruby
12
11
  authors:
13
12
  - Aidan Cully
@@ -26,7 +25,6 @@ dependencies:
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
29
- hash: 7
30
28
  segments:
31
29
  - 2
32
30
  - 0
@@ -42,12 +40,11 @@ dependencies:
42
40
  requirements:
43
41
  - - ">="
44
42
  - !ruby/object:Gem::Version
45
- hash: 19
46
43
  segments:
47
44
  - 2
48
- - 6
49
- - 2
50
- version: 2.6.2
45
+ - 7
46
+ - 0
47
+ version: 2.7.0
51
48
  type: :development
52
49
  version_requirements: *id002
53
50
  description: |-
@@ -81,13 +78,16 @@ files:
81
78
  - Rakefile
82
79
  - lib/safer.rb
83
80
  - lib/safer/ivar.rb
81
+ - lib/safer/ivarfactory.rb
82
+ - lib/safer/ivarfactory/dsl.rb
83
+ - lib/safer/ivarfactory/prefix.rb
84
84
  - lib/safer/protocol.rb
85
85
  - lib/safer/hashprotocol.rb
86
86
  - test/test_safer_ivar.rb
87
- - test/test_safer_protocol.rb
88
- - test/test_safer_hashprotocol.rb
89
87
  - test/test_safer_ivar_run.rb
90
88
  - test/test_safer_ivarfactory.rb
89
+ - test/test_safer_protocol.rb
90
+ - test/test_safer_hashprotocol.rb
91
91
  has_rdoc: true
92
92
  homepage: http://safer.rubyforge.org
93
93
  licenses: []
@@ -103,7 +103,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
103
103
  requirements:
104
104
  - - ">="
105
105
  - !ruby/object:Gem::Version
106
- hash: 3
107
106
  segments:
108
107
  - 0
109
108
  version: "0"
@@ -112,7 +111,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
111
  requirements:
113
112
  - - ">="
114
113
  - !ruby/object:Gem::Version
115
- hash: 3
116
114
  segments:
117
115
  - 0
118
116
  version: "0"