og 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. data/CHANGELOG +796 -664
  2. data/INSTALL +24 -24
  3. data/README +39 -32
  4. data/Rakefile +41 -42
  5. data/benchmark/bench.rb +36 -36
  6. data/doc/AUTHORS +15 -12
  7. data/doc/LICENSE +3 -3
  8. data/doc/RELEASES +311 -243
  9. data/doc/config.txt +1 -1
  10. data/examples/mysql_to_psql.rb +15 -15
  11. data/examples/run.rb +92 -92
  12. data/install.rb +7 -17
  13. data/lib/og.rb +76 -75
  14. data/lib/og/collection.rb +203 -160
  15. data/lib/og/entity.rb +168 -169
  16. data/lib/og/errors.rb +5 -5
  17. data/lib/og/manager.rb +179 -178
  18. data/lib/og/mixin/hierarchical.rb +107 -107
  19. data/lib/og/mixin/optimistic_locking.rb +36 -36
  20. data/lib/og/mixin/orderable.rb +148 -148
  21. data/lib/og/mixin/timestamped.rb +8 -8
  22. data/lib/og/mixin/tree.rb +124 -124
  23. data/lib/og/relation.rb +237 -213
  24. data/lib/og/relation/belongs_to.rb +5 -5
  25. data/lib/og/relation/has_many.rb +60 -58
  26. data/lib/og/relation/joins_many.rb +93 -47
  27. data/lib/og/relation/refers_to.rb +25 -21
  28. data/lib/og/store.rb +210 -207
  29. data/lib/og/store/filesys.rb +79 -79
  30. data/lib/og/store/kirby.rb +263 -258
  31. data/lib/og/store/memory.rb +261 -261
  32. data/lib/og/store/mysql.rb +288 -284
  33. data/lib/og/store/psql.rb +261 -244
  34. data/lib/og/store/sql.rb +873 -720
  35. data/lib/og/store/sqlite.rb +177 -175
  36. data/lib/og/store/sqlserver.rb +204 -214
  37. data/lib/og/types.rb +1 -1
  38. data/lib/og/validation.rb +57 -57
  39. data/lib/vendor/mysql.rb +376 -376
  40. data/lib/vendor/mysql411.rb +10 -10
  41. data/test/og/mixin/tc_hierarchical.rb +59 -59
  42. data/test/og/mixin/tc_optimistic_locking.rb +40 -40
  43. data/test/og/mixin/tc_orderable.rb +67 -67
  44. data/test/og/mixin/tc_timestamped.rb +19 -19
  45. data/test/og/store/tc_filesys.rb +46 -46
  46. data/test/og/tc_inheritance.rb +81 -81
  47. data/test/og/tc_join.rb +67 -0
  48. data/test/og/tc_polymorphic.rb +49 -49
  49. data/test/og/tc_relation.rb +57 -30
  50. data/test/og/tc_select.rb +49 -0
  51. data/test/og/tc_store.rb +345 -337
  52. data/test/og/tc_types.rb +11 -11
  53. metadata +11 -18
@@ -16,42 +16,42 @@ end
16
16
  # http://en.wikipedia.org/wiki/Optimistic_concurrency_control
17
17
 
18
18
  module Locking
19
- property :lock_version, Fixnum, :default => 0
20
- pre "@lock_version = 0", :on => :og_insert
21
-
22
- def self.append_features(base) #:nodoc:
23
- PropertyUtils.copy_features(self, base)
24
-
25
- super
26
-
27
- base.module_eval do
28
- def self.enchant
29
- self.send :alias_method, :update_without_lock, :update
30
- self.send :alias_method, :update, :update_with_lock
31
- self.send :alias_method, :save_without_lock, :save
32
- self.send :alias_method, :save, :save_with_lock
33
- end
34
- end
35
- end
36
-
37
- def update_with_lock
38
- lock = @lock_version
39
- @lock_version += 1
40
-
41
- unless update_without_lock(:condition => "lock_version=#{lock}") == 1
42
- raise(StaleObjectError, 'Attempted to update a stale object')
43
- end
44
- end
45
-
46
- def save_with_lock
47
- lock = @lock_version
48
- @lock_version += 1
49
-
50
- unless save_without_lock(:condition => "lock_version=#{lock}") == 1
51
- raise(StaleObjectError, 'Attempted to update a stale object')
52
- end
53
- end
54
-
19
+ property :lock_version, Fixnum, :default => 0
20
+ pre "@lock_version = 0", :on => :og_insert
21
+
22
+ def self.append_features(base) #:nodoc:
23
+ PropertyUtils.copy_features(self, base)
24
+
25
+ super
26
+
27
+ base.module_eval do
28
+ def self.enchant
29
+ self.send :alias_method, :update_without_lock, :update
30
+ self.send :alias_method, :update, :update_with_lock
31
+ self.send :alias_method, :save_without_lock, :save
32
+ self.send :alias_method, :save, :save_with_lock
33
+ end
34
+ end
35
+ end
36
+
37
+ def update_with_lock
38
+ lock = @lock_version
39
+ @lock_version += 1
40
+
41
+ unless update_without_lock(:condition => "lock_version=#{lock}") == 1
42
+ raise(StaleObjectError, 'Attempted to update a stale object')
43
+ end
44
+ end
45
+
46
+ def save_with_lock
47
+ lock = @lock_version
48
+ @lock_version += 1
49
+
50
+ unless save_without_lock(:condition => "lock_version=#{lock}") == 1
51
+ raise(StaleObjectError, 'Attempted to update a stale object')
52
+ end
53
+ end
54
+
55
55
  end
56
56
 
57
57
  end
@@ -6,154 +6,154 @@ module Og
6
6
 
7
7
  module Orderable
8
8
 
9
- def self.append_dynamic_features(base, options)
10
- o = {
11
- :position => 'position',
12
- :type => Fixnum,
13
- }
14
- o.update(options) if options
15
-
16
- if o[:scope].is_a?(Symbol) && o[:scope].to_s !~ /_oid$/
17
- o[:scope] = "#{o[:scope]}_oid".intern
18
- end
19
-
20
- position = o[:position]
21
- scope = o[:scope]
22
-
23
- if scope
24
- if scope.is_a?(Symbol)
25
- scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
26
- end
27
-
28
- cond = 'condition => ' + scope
29
- cond_and = ':condition => ' + scope + ' + " AND " +'
30
- else
31
- cond = ':condition => nil'
32
- cond_and = ':condition => '
33
- end
34
-
35
- code = %{
36
- property :#{position}, #{o[:type]}
37
-
38
- pre "add_to_bottom", :on => :og_insert
39
- pre "decrement_position_of_lower_items", :on => :og_delete
40
-
41
- def move_higher
42
- if higher = higher_item
43
- #{base}.transaction do
44
- higher.increment_position
45
- decrement_position
46
- end
47
- end
48
- end
49
-
50
- def move_lower
51
- if lower = lower_item
52
- #{base}.transaction do
53
- lower.decrement_position
54
- increment_position
55
- end
56
- end
57
- end
58
-
59
- def move_to_top
60
- #{base}.transaction do
61
- increment_position_of_higher_items
62
- set_top_position
63
- end
64
- end
65
-
66
- def move_to_bottom
67
- #{base}.transaction do
68
- decrement_position_of_lower_items
69
- set_bottom_position
70
- end
71
- end
72
-
73
- def move_to
74
- end
75
-
76
- def add_to_top
77
- increment_position_of_all_items
78
- end
79
-
80
- def add_to_bottom
81
- @#{position} = bottom_position + 1
82
- end
83
-
84
- def add_to
85
- end
86
-
87
- def higher_item
88
- #{base}.one(#{cond_and}"#{position}=\#\{@#{position} - 1\}")
89
- end
90
- alias_method :previous_item, :higher_item
91
-
92
- def lower_item
93
- #{base}.one(#{cond_and}"#{position}=\#\{@#{position} + 1\}")
94
- end
95
- alias_method :next_item, :lower_item
96
-
97
- def top_item
98
- end
99
- alias_method :first_item, :top_item
100
-
101
- def bottom_item
102
- #{base}.one(#{cond}, :order => "#{position} DESC", :limit => 1)
103
- end
104
- alias_method :last_item, :last_item
105
-
106
- def top?
107
- @#{position} == 1
108
- end
109
- alias_method :first?, :top?
110
-
111
- def bottom?
112
- @#{position} == bottom_position
113
- end
114
- alias_method :last?, :bottom?
115
-
116
- def increment_position
117
- @#{position} += 1
118
- update(:#{position})
119
- end
120
-
121
- def decrement_position
122
- @#{position} -= 1
123
- update(:#{position})
124
- end
125
-
126
- def bottom_position
127
- item = bottom_item
128
- item ? item.#{position} : 0
129
- end
130
-
131
- def set_top_position
132
- @#{position} = 1
133
- update(:#{position})
134
- end
135
-
136
- def set_bottom_position
137
- @#{position} = bottom_position + 1
138
- update(:#{position})
139
- end
140
-
141
- def increment_position_of_higher_items
142
- #{base}.update_property("#{position}=(#{position} + 1)", #{cond_and}"#{position} < \#\{@#{position}\}")
143
- end
144
-
145
- def increment_position_of_all_items
146
- #{base}.update_property("#{position}=(#{position} + 1)", #{cond})
147
- end
148
-
149
- def decrement_position_of_lower_items
150
- #{base}.update_property("#{position}=(#{position} - 1)", #{cond_and}"#{position} > \#\{@#{position}\}")
151
- end
152
- }
153
-
154
- base.module_eval(code)
155
- end
156
-
9
+ def self.append_dynamic_features(base, options)
10
+ o = {
11
+ :position => 'position',
12
+ :type => Fixnum,
13
+ }
14
+ o.update(options) if options
15
+
16
+ if o[:scope].is_a?(Symbol) && o[:scope].to_s !~ /_oid$/
17
+ o[:scope] = "#{o[:scope]}_oid".intern
18
+ end
19
+
20
+ position = o[:position]
21
+ scope = o[:scope]
22
+
23
+ if scope
24
+ if scope.is_a?(Symbol)
25
+ scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
26
+ end
27
+
28
+ cond = 'condition => ' + scope
29
+ cond_and = ':condition => ' + scope + ' + " AND " +'
30
+ else
31
+ cond = ':condition => nil'
32
+ cond_and = ':condition => '
33
+ end
34
+
35
+ code = %{
36
+ property :#{position}, #{o[:type]}
37
+
38
+ pre "add_to_bottom", :on => :og_insert
39
+ pre "decrement_position_of_lower_items", :on => :og_delete
40
+
41
+ def move_higher
42
+ if higher = higher_item
43
+ #{base}.transaction do
44
+ higher.increment_position
45
+ decrement_position
46
+ end
47
+ end
48
+ end
49
+
50
+ def move_lower
51
+ if lower = lower_item
52
+ #{base}.transaction do
53
+ lower.decrement_position
54
+ increment_position
55
+ end
56
+ end
57
+ end
58
+
59
+ def move_to_top
60
+ #{base}.transaction do
61
+ increment_position_of_higher_items
62
+ set_top_position
63
+ end
64
+ end
65
+
66
+ def move_to_bottom
67
+ #{base}.transaction do
68
+ decrement_position_of_lower_items
69
+ set_bottom_position
70
+ end
71
+ end
72
+
73
+ def move_to
74
+ end
75
+
76
+ def add_to_top
77
+ increment_position_of_all_items
78
+ end
79
+
80
+ def add_to_bottom
81
+ @#{position} = bottom_position + 1
82
+ end
83
+
84
+ def add_to
85
+ end
86
+
87
+ def higher_item
88
+ #{base}.one(#{cond_and}"#{position}=\#\{@#{position} - 1\}")
89
+ end
90
+ alias_method :previous_item, :higher_item
91
+
92
+ def lower_item
93
+ #{base}.one(#{cond_and}"#{position}=\#\{@#{position} + 1\}")
94
+ end
95
+ alias_method :next_item, :lower_item
96
+
97
+ def top_item
98
+ end
99
+ alias_method :first_item, :top_item
100
+
101
+ def bottom_item
102
+ #{base}.one(#{cond}, :order => "#{position} DESC", :limit => 1)
103
+ end
104
+ alias_method :last_item, :last_item
105
+
106
+ def top?
107
+ @#{position} == 1
108
+ end
109
+ alias_method :first?, :top?
110
+
111
+ def bottom?
112
+ @#{position} == bottom_position
113
+ end
114
+ alias_method :last?, :bottom?
115
+
116
+ def increment_position
117
+ @#{position} += 1
118
+ update(:only=>[:#{position}])
119
+ end
120
+
121
+ def decrement_position
122
+ @#{position} -= 1
123
+ update(:only=>[:#{position}])
124
+ end
125
+
126
+ def bottom_position
127
+ item = bottom_item
128
+ item ? item.#{position} : 0
129
+ end
130
+
131
+ def set_top_position
132
+ @#{position} = 1
133
+ update(:only=>[:#{position}])
134
+ end
135
+
136
+ def set_bottom_position
137
+ @#{position} = bottom_position + 1
138
+ update(:only=>[:#{position}])
139
+ end
140
+
141
+ def increment_position_of_higher_items
142
+ #{base}.update_property("#{position}=(#{position} + 1)", #{cond_and}"#{position} < \#\{@#{position}\}")
143
+ end
144
+
145
+ def increment_position_of_all_items
146
+ #{base}.update_property("#{position}=(#{position} + 1)", #{cond})
147
+ end
148
+
149
+ def decrement_position_of_lower_items
150
+ #{base}.update_property("#{position}=(#{position} - 1)", #{cond_and}"#{position} > \#\{@#{position}\}")
151
+ end
152
+ }
153
+
154
+ base.module_eval(code)
155
+ end
156
+
157
157
  end
158
158
 
159
159
  end
@@ -3,16 +3,16 @@ module Og
3
3
  # Adds timestamping functionality.
4
4
 
5
5
  module Timestamped
6
- property :create_time, Time
7
- property :update_time, Time
8
- property :access_time, Time
6
+ property :create_time, Time
7
+ property :update_time, Time
8
+ property :access_time, Time
9
9
 
10
- pre "@create_time = @update_time = Time.now", :on => :og_insert
11
- pre "@update_time = Time.now", :on => :og_update
10
+ pre "@create_time = @update_time = Time.now", :on => :og_insert
11
+ pre "@update_time = Time.now", :on => :og_update
12
12
 
13
- def touch!
14
- @access_time = Time.now
15
- end
13
+ def touch!
14
+ @access_time = Time.now
15
+ end
16
16
  end
17
17
 
18
18
  end
@@ -11,16 +11,16 @@ module Og
11
11
  # article (http://www.dbazine.com/tropashko4.shtml)
12
12
 
13
13
  module TreeTraversal
14
-
15
- # The default prefix for the tree traversal helpers.
16
-
17
- cattr_accessor :prefix, 'tree'
14
+
15
+ # The default prefix for the tree traversal helpers.
16
+
17
+ cattr_accessor :prefix, 'tree'
18
18
 
19
- def self.child(sum, n)
20
- power = 2 ** n
19
+ def self.child(sum, n)
20
+ power = 2 ** n
21
21
 
22
- return sum * power
23
- end
22
+ return sum * power
23
+ end
24
24
 
25
25
  end
26
26
 
@@ -30,15 +30,15 @@ end
30
30
  __END__
31
31
 
32
32
  def xcoord(numer, denom)
33
- num = numer + 1
34
- den = denom * 2
33
+ num = numer + 1
34
+ den = denom * 2
35
35
 
36
- while (num / 2).floor == (num / 2)
37
- num /= 2
38
- den /= 2
39
- end
36
+ while (num / 2).floor == (num / 2)
37
+ num /= 2
38
+ den /= 2
39
+ end
40
40
 
41
- return num, den
41
+ return num, den
42
42
  end
43
43
 
44
44
  #--
@@ -46,111 +46,111 @@ end
46
46
  #++
47
47
 
48
48
  def ycoord(numer, denom)
49
- num, den = xcoord(numer, denom)
49
+ num, den = xcoord(numer, denom)
50
50
 
51
- while den < denom
52
- num *= 2
53
- den *= 2
54
- end
51
+ while den < denom
52
+ num *= 2
53
+ den *= 2
54
+ end
55
55
 
56
- num = numer - num
56
+ num = numer - num
57
57
 
58
- while (num / 2).floor == (num / 2)
59
- num /= 2
60
- den /= 2
61
- end
58
+ while (num / 2).floor == (num / 2)
59
+ num /= 2
60
+ den /= 2
61
+ end
62
62
 
63
- return num, den
63
+ return num, den
64
64
  end
65
65
 
66
66
  def parent(numer, denom)
67
- return nil if numer == 3
67
+ return nil if numer == 3
68
68
 
69
- num = (numer - 1) / 2
70
- den = denom / 2
69
+ num = (numer - 1) / 2
70
+ den = denom / 2
71
71
 
72
- while ((num-1)/4).floor == ((num-1)/4)
73
- num = (num + 1) / 2
74
- den = den / 2
75
- end
76
-
77
- return num, den
72
+ while ((num-1)/4).floor == ((num-1)/4)
73
+ num = (num + 1) / 2
74
+ den = den / 2
75
+ end
76
+
77
+ return num, den
78
78
  end
79
79
 
80
80
  def sibling(numer, denom)
81
- return nil if numer == 3
82
-
83
- num = (numer - 1) / 2
84
- den = denom / 2
85
- sib = 1
86
-
87
- while ((num-1)/4).floor == ((num-1)/4)
88
- return sib if num == 1 and den == 1
89
- num = (num + 1) / 2
90
- den /= 2
91
- sib += 1
92
- end
93
-
94
- return sib
81
+ return nil if numer == 3
82
+
83
+ num = (numer - 1) / 2
84
+ den = denom / 2
85
+ sib = 1
86
+
87
+ while ((num-1)/4).floor == ((num-1)/4)
88
+ return sib if num == 1 and den == 1
89
+ num = (num + 1) / 2
90
+ den /= 2
91
+ sib += 1
92
+ end
93
+
94
+ return sib
95
95
  end
96
96
 
97
97
  def child(numer, denom, n)
98
- power = 2 ** n
98
+ power = 2 ** n
99
99
 
100
- num = (numer * power) + 3 - power
101
- den = denom * power
100
+ num = (numer * power) + 3 - power
101
+ den = denom * power
102
102
 
103
- return num, den
103
+ return num, den
104
104
  end
105
105
 
106
106
  def path(numer, denom)
107
- return '' if numer == nil
108
- n, d = parent(numer, denom)
109
- return "#{path(n, d)}.#{sibling(numer, denom)}"
107
+ return '' if numer == nil
108
+ n, d = parent(numer, denom)
109
+ return "#{path(n, d)}.#{sibling(numer, denom)}"
110
110
  end
111
111
 
112
112
  def encode(path)
113
- num = den = 1
114
- postfix = ".#{path}."
113
+ num = den = 1
114
+ postfix = ".#{path}."
115
115
 
116
- while postfix.length > 1
117
- sibling, postfix = postfix.split('.', 2)
118
- num, den = child(num, den, sibling.to_i)
119
- end
116
+ while postfix.length > 1
117
+ sibling, postfix = postfix.split('.', 2)
118
+ num, den = child(num, den, sibling.to_i)
119
+ end
120
120
 
121
- return num, den
121
+ return num, den
122
122
  end
123
123
 
124
124
 
125
125
  require 'og'
126
126
 
127
127
  class Comment
128
- property :path, String
129
- property :x, :y, Float
128
+ property :path, String
129
+ property :x, :y, Float
130
130
 
131
- def initialize(path = nil, x = nil, y = nil)
132
- @path, @x, @y = path, x, y
133
- end
131
+ def initialize(path = nil, x = nil, y = nil)
132
+ @path, @x, @y = path, x, y
133
+ end
134
134
  end
135
135
 
136
136
  Og::Database.new(
137
- :database => 'test',
138
- :adapter => 'psql',
139
- :user => 'postgres',
140
- :password => 'navelrulez',
141
- :connection_count => 1,
142
- :drop => true
137
+ :database => 'test',
138
+ :adapter => 'psql',
139
+ :user => 'postgres',
140
+ :password => 'navelrulez',
141
+ :connection_count => 1,
142
+ :drop => true
143
143
  )
144
144
 
145
145
  def dp(path)
146
- n, d = encode(path)
147
- n, d = Float(n), Float(d)
148
- # puts "#{path} -> n: #{n} d: #{d} c: #{n/d}"
149
- p = path(n, d)
150
- # puts "=== #{p}"
151
- xn, xd = xcoord(n, d)
152
- yn, yd = ycoord(n, d)
153
- Comment.create(path, xn/xd, yn/yd)
146
+ n, d = encode(path)
147
+ n, d = Float(n), Float(d)
148
+ # puts "#{path} -> n: #{n} d: #{d} c: #{n/d}"
149
+ p = path(n, d)
150
+ # puts "=== #{p}"
151
+ xn, xd = xcoord(n, d)
152
+ yn, yd = ycoord(n, d)
153
+ Comment.create(path, xn/xd, yn/yd)
154
154
  end
155
155
 
156
156
  dp '1.1'
@@ -168,20 +168,20 @@ dp '1.5'
168
168
  dp '1.5.1'
169
169
 
170
170
  for c in Comment.all('ORDER BY x DESC, y ASC')
171
- puts "#{c.path.ljust(16)}#{c.inspect}"
171
+ puts "#{c.path.ljust(16)}#{c.inspect}"
172
172
  end
173
173
 
174
174
  class Article
175
- property :title, :body, String
176
- has_many: :comments, Comment, :tree => true
175
+ property :title, :body, String
176
+ has_many: :comments, Comment, :tree => true
177
177
  end
178
178
 
179
179
  class Comment
180
- property :body, String
181
- belongs_to :article, Article
182
- belongs_to :parent, Comment
183
- has_many :children, Comment, :tree => true
184
- has_many :roles, Role, :list => true
180
+ property :body, String
181
+ belongs_to :article, Article
182
+ belongs_to :parent, Comment
183
+ has_many :children, Comment, :tree => true
184
+ has_many :roles, Role, :list => true
185
185
  end
186
186
 
187
187
 
@@ -190,37 +190,37 @@ comment.add_child(Comment.new('hello'))
190
190
  comment.children_tree
191
191
  comment.children
192
192
 
193
- if options[:tree]
194
- code << %{
195
- property :#{prefix}_x, Fixnum
196
- property :#{prefix}_y, Fixnum
197
- sql_index '#{prefix}_x, #{prefix}_y'
198
- }
199
- end
200
-
201
- if options[:tree]
202
-
203
- code << %{
204
- def #{name}_tree(extrasql = nil)
205
- Og.db.select("SELECT * FROM #{Og::Adapter.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\} ORDER BY #{prefix}_x DESC, #{prefix}_y ASC", #{klass})
206
- end
207
- }
208
-
209
- elsif options[:list]
210
-
211
- else
212
-
213
- end
214
-
215
- }
216
-
217
- if options[:tree]
218
- code << %{
219
- n = Og.db.count("#{linkback}=\#\@oid", #{klass})
220
- ptx = @#{prefix}_x || 0
221
- pty = @#{prefix}_y || 0
222
- obj.#{prefix}_x, obj.#{prefix}_y = TreeTraversal.child(ptx + pty, n)
223
- }
224
- end
225
-
226
- code << %{
193
+ if options[:tree]
194
+ code << %{
195
+ property :#{prefix}_x, Fixnum
196
+ property :#{prefix}_y, Fixnum
197
+ sql_index '#{prefix}_x, #{prefix}_y'
198
+ }
199
+ end
200
+
201
+ if options[:tree]
202
+
203
+ code << %{
204
+ def #{name}_tree(extrasql = nil)
205
+ Og.db.select("SELECT * FROM #{Og::Adapter.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\} ORDER BY #{prefix}_x DESC, #{prefix}_y ASC", #{klass})
206
+ end
207
+ }
208
+
209
+ elsif options[:list]
210
+
211
+ else
212
+
213
+ end
214
+
215
+ }
216
+
217
+ if options[:tree]
218
+ code << %{
219
+ n = Og.db.count("#{linkback}=\#\@oid", #{klass})
220
+ ptx = @#{prefix}_x || 0
221
+ pty = @#{prefix}_y || 0
222
+ obj.#{prefix}_x, obj.#{prefix}_y = TreeTraversal.child(ptx + pty, n)
223
+ }
224
+ end
225
+
226
+ code << %{