og 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/AUTHORS +14 -4
  2. data/ChangeLog +192 -1
  3. data/README.og +2 -1
  4. data/RELEASES.og +35 -0
  5. data/Rakefile +1 -1
  6. data/examples/og/mock_example.rb +6 -9
  7. data/examples/og/mysql_to_psql.rb +100 -0
  8. data/examples/og/run.rb +8 -17
  9. data/lib/glue/array.rb +1 -1
  10. data/lib/glue/attribute.rb +86 -0
  11. data/lib/glue/cache.rb +1 -1
  12. data/lib/glue/hash.rb +1 -1
  13. data/lib/glue/inflector.rb +1 -1
  14. data/lib/glue/logger.rb +118 -18
  15. data/lib/glue/mixins.rb +1 -1
  16. data/lib/glue/number.rb +1 -1
  17. data/lib/glue/pool.rb +1 -1
  18. data/lib/glue/property.rb +48 -31
  19. data/lib/glue/string.rb +1 -1
  20. data/lib/glue/time.rb +2 -2
  21. data/lib/glue/validation.rb +400 -0
  22. data/lib/glue.rb +7 -8
  23. data/lib/og/backend.rb +47 -46
  24. data/lib/og/backends/mysql.rb +64 -63
  25. data/lib/og/backends/psql.rb +73 -72
  26. data/lib/og/connection.rb +7 -8
  27. data/lib/og/enchant.rb +80 -0
  28. data/lib/og/meta.rb +21 -21
  29. data/lib/og/mock.rb +31 -88
  30. data/lib/og/version.rb +6 -5
  31. data/lib/og.rb +95 -129
  32. data/test/tc_og.rb +3 -3
  33. data/vendor/extensions/_base.rb +153 -0
  34. data/vendor/extensions/_template.rb +36 -0
  35. data/vendor/extensions/all.rb +21 -0
  36. data/vendor/extensions/array.rb +68 -0
  37. data/vendor/extensions/binding.rb +224 -0
  38. data/vendor/extensions/class.rb +50 -0
  39. data/vendor/extensions/continuation.rb +71 -0
  40. data/vendor/extensions/enumerable.rb +250 -0
  41. data/vendor/extensions/hash.rb +23 -0
  42. data/vendor/extensions/io.rb +58 -0
  43. data/vendor/extensions/kernel.rb +42 -0
  44. data/vendor/extensions/module.rb +114 -0
  45. data/vendor/extensions/numeric.rb +230 -0
  46. data/vendor/extensions/object.rb +164 -0
  47. data/vendor/extensions/ostruct.rb +41 -0
  48. data/vendor/extensions/string.rb +316 -0
  49. data/vendor/extensions/symbol.rb +28 -0
  50. metadata +24 -4
  51. data/lib/glue/property.rb.old +0 -307
@@ -0,0 +1,316 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ #
4
+ # == extensions/string.rb
5
+ #
6
+ # Adds methods to the builtin String class.
7
+ #
8
+
9
+ require "extensions/_base"
10
+
11
+
12
+ ExtensionsProject.implement(String, :leftmost_indent) do
13
+ class String
14
+ #
15
+ # Returns the size of the smallest indent of any line in the string.
16
+ # Emits a warning if tabs are found, and if <tt>$VERBOSE</tt> is on.
17
+ # You can use #expand_tabs to avoid this. This method is primarily intended
18
+ # for use by #tabto and is not likely to be all that useful in its own
19
+ # right.
20
+ #
21
+ def leftmost_indent
22
+ tabs_found = false
23
+ scan(/^([ \t]*)\S/).flatten.map { |ws|
24
+ tabs_found = true if ws =~ /\t/
25
+ ws.size
26
+ }.compact.min
27
+ ensure
28
+ if tabs_found and $VERBOSE
29
+ $stderr.puts %{
30
+ String#leftmost_indent: warning: tabs treated as spaces
31
+ (value: #{self.inspect[0..30]}...")
32
+ }.strip
33
+ end
34
+ end
35
+ protected :leftmost_indent
36
+ end
37
+ end
38
+
39
+
40
+ ExtensionsProject.implement(String, :expand_tabs) do
41
+ class String
42
+ #
43
+ # Expands tabs to +n+ spaces. Non-destructive. If +n+ is 0, then tabs are
44
+ # simply removed. Raises an exception if +n+ is negative.
45
+ #
46
+ #--
47
+ # Thanks to GGaramuno for a more efficient algorithm. Very nice.
48
+ def expand_tabs(n=8)
49
+ n = n.to_int
50
+ raise ArgumentError, "n must be >= 0" if n < 0
51
+ return gsub(/\t/, "") if n == 0
52
+ return gsub(/\t/, " ") if n == 1
53
+ str = self.dup
54
+ while
55
+ str.gsub!(/^([^\t\n]*)(\t+)/) { |f|
56
+ val = ( n * $2.size - ($1.size % n) )
57
+ $1 << (' ' * val)
58
+ }
59
+ end
60
+ str
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+ ExtensionsProject.implement(String, :indent) do
67
+ class String
68
+ #
69
+ # Indents the string +n+ spaces.
70
+ #
71
+ def indent(n)
72
+ n = n.to_int
73
+ return outdent(-n) if n < 0
74
+ gsub(/^/, " "*n)
75
+ end
76
+ end
77
+ end
78
+
79
+
80
+ ExtensionsProject.implement(String, :outdent) do
81
+ class String
82
+ #
83
+ # Outdents the string +n+ spaces. Initial tabs will cause problems and
84
+ # cause a warning to be emitted (if warnings are on). Relative indendation
85
+ # is always preserved. Once the block hits the beginning of the line,
86
+ # that's it. In the following example, <tt>.</tt> represents space from the
87
+ # beginning of the line.
88
+ #
89
+ # str = %{
90
+ # ..One
91
+ # ....Two
92
+ # }.outdent(4)
93
+ #
94
+ # is
95
+ #
96
+ # One
97
+ # ..Two
98
+ #
99
+ def outdent(n)
100
+ n = n.to_int
101
+ return indent(-n) if n < 0
102
+ tabto(leftmost_indent - n)
103
+ end
104
+ end
105
+ end
106
+
107
+
108
+ ExtensionsProject.implement(String, :tabto) do
109
+ class String
110
+ #
111
+ # Move the string to the <tt>n</tt>th column. Relative indentation is preserved.
112
+ # Column indices begin at 0, so the result is that the leftmost character of
113
+ # the string has +n+ spaces before it.
114
+ #
115
+ # Examples:
116
+ # "xyz".tabto(0) # -> "xyz"
117
+ # "xyz".tabto(1) # -> " xyz"
118
+ # "xyz".tabto(2) # -> " xyz"
119
+ # " xyz".tabto(1) # -> " xyz"
120
+ #
121
+ # str = <<EOF
122
+ # Hello, my name
123
+ # is Gerald.
124
+ # EOF
125
+ # str.tabto(5) == <<EOF # -> true
126
+ # Hello, my name
127
+ # is Gerald.
128
+ # EOF
129
+ #
130
+ def tabto(n)
131
+ n = n.to_int
132
+ n = 0 if n < 0
133
+ find = " " * leftmost_indent()
134
+ replace = " " * (n)
135
+ gsub(/^#{find}/, replace)
136
+ end
137
+ end
138
+ end
139
+
140
+
141
+ ExtensionsProject.implement(String, :taballto) do
142
+ class String
143
+ #
144
+ # Tabs all lines in the string to column +n+. That is, relative indentation
145
+ # is _not_ preserved.
146
+ #
147
+ def taballto(n)
148
+ n = n.to_int
149
+ n = 0 if n < 0
150
+ gsub(/^[ \t]*/, " "*n)
151
+ end
152
+ end
153
+ end
154
+
155
+
156
+ ExtensionsProject.implement(String, :trim) do
157
+ class String
158
+ #
159
+ # Trims a string:
160
+ # - removes one initial blank line
161
+ # - removes trailing spaces on each line
162
+ # - if +margin+ is given, removes initial spaces up to and including
163
+ # the margin on each line, plus one space
164
+ #
165
+ # This is designed specifically for working with inline documents.
166
+ # Here-documents are great, except they tend to go against the indentation
167
+ # of your code. This method allows a convenient way of using %{}-style
168
+ # documents. For instance:
169
+ #
170
+ # USAGE = %{
171
+ # | usage: prog [-o dir] -h file...
172
+ # | where
173
+ # | -o dir outputs to DIR
174
+ # | -h prints this message
175
+ # }.trim("|")
176
+ #
177
+ # # USAGE == "usage: prog [-o dir] -h file...\n where"...
178
+ # # (note single space to right of margin is deleted)
179
+ #
180
+ # Note carefully that if no margin string is given, then there is no
181
+ # clipping at the beginning of each line and your string will remain
182
+ # indented. You can use <tt>tabto(0)</tt> to align it with the left of
183
+ # screen (while preserving relative indentation).
184
+ #
185
+ # USAGE = %{
186
+ # usage: prog [-o dir] -h file...
187
+ # where
188
+ # -o dir outputs to DIR
189
+ # -h prints this message
190
+ # }.trim.tabto(0)
191
+ #
192
+ # # USAGE == (same as last example)
193
+ #
194
+ def trim(margin=nil)
195
+ s = self.dup
196
+ # Remove initial blank line.
197
+ s.sub!(/\A[ \t]*\n/, "")
198
+ # Get rid of the margin, if it's specified.
199
+ unless margin.nil?
200
+ margin_re = Regexp.escape(margin || "")
201
+ margin_re = /^[ \t]*#{margin_re} ?/
202
+ s.gsub!(margin_re, "")
203
+ end
204
+ # Remove trailing whitespace on each line
205
+ s.gsub!(/[ \t]+$/, "")
206
+ s
207
+ end
208
+ end
209
+ end
210
+
211
+
212
+ ExtensionsProject.implement(String, :starts_with?) do
213
+ class String
214
+ #
215
+ # Returns true iff this string starts with +str+.
216
+ # "Hello, world".starts_with?("He") # -> true
217
+ # "Hello, world".starts_with?("Green") # -> false
218
+ #
219
+ def starts_with?(str)
220
+ str = str.to_str
221
+ head = self[0, str.length]
222
+ head == str
223
+ end
224
+ end
225
+ end
226
+
227
+
228
+ ExtensionsProject.implement(String, :ends_with?) do
229
+ class String
230
+ #
231
+ # Returns true iff this string ends with +str+.
232
+ # "Hello, world".ends_with?(", world") # -> true
233
+ # "Hello, world".ends_with?("Green") # -> false
234
+ #
235
+ def ends_with?(str)
236
+ str = str.to_str
237
+ tail = self[-str.length, str.length]
238
+ tail == str
239
+ end
240
+ end
241
+ end
242
+
243
+
244
+ ExtensionsProject.implement(String, :line) do
245
+ class String
246
+ #
247
+ # Returns a line or lines from the string. +args+ can be a single integer,
248
+ # two integers or a range, as per <tt>Array#slice</tt>. The return value is
249
+ # a single String (a single line), an array of Strings (multiple lines) or
250
+ # +nil+ (out of bounds). Note that lines themselves do not contain a
251
+ # trailing newline character; that is metadata. Indexes out of bounds are
252
+ # ignored.
253
+ #
254
+ # data = " one \n two \n three \n four \n five \n"
255
+ # data.line(1) # -> " two "
256
+ # data.line(0,1) # -> [" one "]
257
+ # data.line(3..9) # -> [" four ", " five "]
258
+ # data.line(9) # -> nil
259
+ #
260
+ def line(*args)
261
+ self.split(/\n/).slice(*args)
262
+ rescue TypeError
263
+ raise TypeError,
264
+ "String#line(*args): args must be one Integer, two Integers or a Range"
265
+ rescue ArgumentError
266
+ raise ArgumentError,
267
+ "String#line(*args): args must be one Integer, two Integers or a Range"
268
+ end
269
+ end
270
+ end
271
+
272
+
273
+ ExtensionsProject.implement(String, :cmp) do
274
+ class String
275
+ #
276
+ # Compare this string to +other+, returning the first index at which they
277
+ # differ, or +nil+ if they are equal.
278
+ #
279
+ # "practise".cmp("practice") # -> 6
280
+ # "noun".cmp("nouns") # -> 5 (and vice versa)
281
+ # "fly".cmp("fly") # -> nil
282
+ #
283
+ def cmp(other)
284
+ other = other.to_str
285
+ if self == other
286
+ return nil
287
+ else
288
+ n = [self.size, other.size].min
289
+ (0..n).each do |i|
290
+ return i unless self[i] == other[i]
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end
296
+
297
+
298
+ ExtensionsProject.implement(String, :join) do
299
+ class String
300
+ #
301
+ # Join all the lines of the string together, and compress spaces. The resulting string
302
+ # will have no surrounding whitespace.
303
+ #
304
+ # text = %{
305
+ # Once upon a time,
306
+ # Little Red Riding Hood ...
307
+ #
308
+ # }
309
+ #
310
+ # text.join # -> "Once upon a time, Little Red Riding Hood ..."
311
+ #
312
+ def join
313
+ gsub(/([ \t]*\n[ \t]*)+/, ' ').strip
314
+ end
315
+ end
316
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/local/bin/ruby -w
2
+ #
3
+ # == extensions/symbol.rb
4
+ #
5
+ # Adds methods to the builtin Symbol class.
6
+ #
7
+
8
+ require "extensions/_base"
9
+
10
+
11
+ #
12
+ # * Symbol#to_proc
13
+ #
14
+ ExtensionsProject.implement(Symbol, :to_proc) do
15
+ class Symbol
16
+ #
17
+ # Allows a Symbol to be implicitly converted to a Proc.
18
+ #
19
+ # This allows such conveniences as:
20
+ # %{john terry fiona}.map(&:capitalize) # -> %{John Terry Fiona}
21
+ # sum = numbers.inject(&:+)
22
+ #
23
+ def to_proc
24
+ proc { |obj, *args| obj.send(self, *args) }
25
+ end
26
+ end
27
+ end
28
+
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.3
2
+ rubygems_version: 0.8.4
3
3
  specification_version: 1
4
4
  name: og
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.7.0
7
- date: 2004-12-27
6
+ version: 0.8.0
7
+ date: 2005-01-12
8
8
  summary: Og (ObjectGraph)
9
9
  require_paths:
10
10
  - lib
@@ -34,6 +34,7 @@ files:
34
34
  - Rakefile
35
35
  - ChangeLog.1
36
36
  - ChangeLog
37
+ - examples/og/mysql_to_psql.rb
37
38
  - examples/og/run.rb
38
39
  - examples/og/mock_example.rb
39
40
  - examples/og/README
@@ -46,13 +47,15 @@ files:
46
47
  - lib/glue/number.rb
47
48
  - lib/glue/time.rb
48
49
  - lib/glue/property.rb
49
- - lib/glue/property.rb.old
50
50
  - lib/glue/macro.rb
51
51
  - lib/glue/cache.rb
52
52
  - lib/glue/string.rb
53
53
  - lib/glue/array.rb
54
+ - lib/glue/validation.rb
55
+ - lib/glue/attribute.rb
54
56
  - lib/og/backends
55
57
  - lib/og/version.rb
58
+ - lib/og/enchant.rb
56
59
  - lib/og/connection.rb
57
60
  - lib/og/mock.rb
58
61
  - lib/og/meta.rb
@@ -62,6 +65,23 @@ files:
62
65
  - lib/og.rb
63
66
  - test/tc_og.rb
64
67
  - test/og/tc_lifecycle.rb
68
+ - vendor/extensions/enumerable.rb
69
+ - vendor/extensions/all.rb
70
+ - vendor/extensions/hash.rb
71
+ - vendor/extensions/continuation.rb
72
+ - vendor/extensions/module.rb
73
+ - vendor/extensions/binding.rb
74
+ - vendor/extensions/ostruct.rb
75
+ - vendor/extensions/kernel.rb
76
+ - vendor/extensions/class.rb
77
+ - vendor/extensions/numeric.rb
78
+ - vendor/extensions/_base.rb
79
+ - vendor/extensions/io.rb
80
+ - vendor/extensions/string.rb
81
+ - vendor/extensions/object.rb
82
+ - vendor/extensions/array.rb
83
+ - vendor/extensions/symbol.rb
84
+ - vendor/extensions/_template.rb
65
85
  test_files: []
66
86
  rdoc_options:
67
87
  - "--main"
@@ -1,307 +0,0 @@
1
- # code:
2
- # * George Moschovitis <gm@navel.gr>
3
- # design:
4
- # * Anastastios Koutoumanos <ak@navel.gr>
5
- # * Elias Karakoulakis <ekarak@ktismata.com>
6
- #
7
- # (c) 2004 Navel, all rights reserved.
8
- # $Id: property.rb 185 2004-12-10 13:29:09Z gmosx $
9
-
10
- require "glue/array"
11
- require "glue/hash"
12
-
13
- module G
14
-
15
- # = Property
16
- #
17
- # Ruby attributes are typeless and generally this is good. Some times
18
- # we need extra metadata though, for example in relational mapping,
19
- # or web form population.
20
- #
21
- # Only Fixnums, Strings, Floats, Times, Booleans are converted.
22
- #
23
- # The default = methods do not force the types. A special
24
- # __force_set method should be used instead.
25
- #
26
- #--
27
- # TODO:
28
- # Inject only the really needd methods into Module.
29
- # Perhaps a sync is needed in evals (!!!!)
30
- #++
31
- #
32
- class Property
33
- # the symbol of the property
34
- attr_accessor :symbol
35
- # the string representation of the symbol
36
- attr_accessor :name
37
- # the class of the property
38
- attr_accessor :klass
39
- # additional metadata (like sql declaratio, sql index, etc)
40
- attr_accessor :meta
41
-
42
- def initialize(symbol, klass, meta = {})
43
- @symbol, @klass = symbol, klass
44
- @meta = meta
45
- @name = @symbol.to_s()
46
- end
47
-
48
- def ==(other)
49
- return @symbol == other.symbol
50
- end
51
-
52
- def to_s
53
- return name
54
- end
55
- end
56
-
57
- end # module
58
-
59
- class Module
60
-
61
- # Define a property (== typed attribute)
62
- # This works like Ruby's standard attr method, ie creates
63
- # only one property.
64
- #
65
- # Use the prop_reader, prop_writer, prop_accessor methods
66
- # for multiple properties.
67
- #
68
- # Examples:
69
- # prop String, :name, :sql => "char(32), :sql_index => "name(32)"
70
- # --> creates only writer.
71
- # prop Fixnum, :oid, writer = true, :sql => "integer PRIMARY KEY"
72
- # --> creates reader and writer.
73
- #
74
- def prop(*params)
75
- meta = {}
76
- klass = Object
77
-
78
- for param in params
79
- if param.is_a?(Class)
80
- klass = param
81
- elsif param.is_a?(Symbol)
82
- symbol = param
83
- elsif param.is_a?(TrueClass) or param.is_a?(TrueClass)
84
- writer = param
85
- elsif param.is_a?(Hash)
86
- # the meta hash.
87
- meta = param
88
- else
89
- raise "Error when defining property!"
90
- end
91
- end
92
-
93
- unless self.methods.include?(:__props)
94
- eval %{
95
- # Properties
96
- # An array is used to enforce order.
97
- def __props
98
- @__props
99
- end
100
-
101
- def __props=(props)
102
- @__props = props
103
- end
104
-
105
- def __meta
106
- @__meta
107
- end
108
-
109
- def __meta=(meta)
110
- @__meta = meta
111
- end
112
- }
113
- end
114
-
115
- @__props = G::SafeArray.new() unless @__props
116
-
117
- property = G::Property.new(symbol, klass, meta)
118
-
119
- reader = meta[:reader] || true
120
- writer = writer || meta[:writer] || false
121
-
122
- __add_prop(property, reader, writer)
123
- end
124
-
125
- # Helper method. Accepts a collection of symbols and generates
126
- # properties. Only generates reader.
127
- #
128
- # Example:
129
- # prop_reader String, :name, :title, :body, :sql => "char(32)"
130
- #
131
- def prop_reader(*params)
132
- meta = {}
133
- klass = Object
134
- symbols = []
135
-
136
- for param in params
137
- if param.is_a?(Class)
138
- klass = param
139
- elsif param.is_a?(Symbol)
140
- symbols << param
141
- elsif param.is_a?(Hash)
142
- # the meta hash.
143
- meta = param
144
- else
145
- raise "Error when defining property!"
146
- end
147
- end
148
-
149
- meta[:reader] = true
150
- meta[:writer] = false
151
-
152
- for symbol in symbols
153
- prop(klass, symbol, meta)
154
- end
155
- end
156
-
157
- # Helper method. Accepts a collection of symbols and generates
158
- # properties. Only generates writer.
159
- #
160
- # Example:
161
- # prop_writer String, :name, :title, :body, :sql => "char(32)"
162
- #
163
- def prop_writer(*params)
164
- meta = {}
165
- klass = Object
166
- symbols = []
167
-
168
- for param in params
169
- if param.is_a?(Class)
170
- klass = param
171
- elsif param.is_a?(Symbol)
172
- symbols << param
173
- elsif param.is_a?(Hash)
174
- # the meta hash.
175
- meta = param
176
- else
177
- raise "Error when defining property!"
178
- end
179
- end
180
-
181
- meta[:reader] = false
182
- meta[:writer] = true
183
-
184
- for symbol in symbols
185
- prop(klass, symbol, meta)
186
- end
187
- end
188
-
189
- # Helper method. Accepts a collection of symbols and generates
190
- # properties. Generates reader and writer.
191
- #
192
- # Example:
193
- # prop_accessor String, :name, :title, :body, :sql => "char(32)"
194
- #
195
- def prop_accessor(*params)
196
- meta = {}
197
- klass = Object
198
- symbols = []
199
-
200
- for param in params
201
- if param.is_a?(Class)
202
- klass = param
203
- elsif param.is_a?(Symbol)
204
- symbols << param
205
- elsif param.is_a?(Hash)
206
- # the meta hash.
207
- meta = param
208
- else
209
- raise "Error when defining property!"
210
- end
211
- end
212
-
213
- meta[:reader] = true
214
- meta[:writer] = true
215
-
216
- for symbol in symbols
217
- prop(klass, symbol, meta)
218
- end
219
- end
220
-
221
- # Add the property
222
- #
223
- def __add_prop(prop, reader = true, writer = true)
224
- if idx = @__props.index(prop)
225
- # override in case of duplicates. Keep the order of the props.
226
- @__props[idx] = prop
227
- else
228
- @__props << prop
229
- end
230
-
231
- # Precompile the property read/write methods
232
-
233
- s, klass = prop.symbol, prop.klass
234
-
235
- if reader
236
- module_eval %{
237
- def #{s}
238
- return @#{s}
239
- end
240
- }
241
- end
242
-
243
- # gmosx: __force_xxx reuses xxx= to allow for easier
244
- # overrides.
245
- if writer
246
- module_eval %{
247
- def #{s}=(val)
248
- @#{s} = val
249
- end
250
-
251
- def __force_#{s}(val)
252
- self.#{s}=(} + case klass.name
253
- when Fixnum.name
254
- "val.to_i()"
255
- when String.name
256
- "val.to_s()"
257
- when Float.name
258
- "val.to_f()"
259
- when Time.name
260
- "Time.parse(val.to_s())"
261
- when TrueClass.name, FalseClass.name
262
- "val.to_i() > 0"
263
- else
264
- "val"
265
- end + %{)
266
- end
267
- }
268
- end
269
- end
270
-
271
- # Attach metadata
272
- #
273
- def meta(key, val)
274
- @__meta = G::SafeHash.new unless @__meta
275
-
276
- @__meta[key] = [] unless @__meta[key]
277
-
278
- # guard against duplicates, no need to keep order.
279
- @__meta[key].delete_if { |v| val == v }
280
- @__meta[key] << val
281
- end
282
-
283
- # This method is typically called before including other
284
- # modules to preserve properties order.
285
- #
286
- def inherit_meta(mod = superclass)
287
- # concat props.
288
- if mod.__props
289
- @__props = G::SafeArray.new unless @__props
290
-
291
- mod.__props.each { |p|
292
- __add_prop(p)
293
- }
294
- end
295
-
296
- # concat metadata
297
- if mod.__meta
298
- mod.__meta.each { |k, val|
299
- val.each { |v|
300
- meta(k, v)
301
- } if val
302
- }
303
- end
304
- end
305
-
306
- end
307
-