blankslate 2.1.2.4 → 3.1.2
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.
- data/CHANGES +101 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +225 -0
- data/Rakefile +184 -8
- data/doc/releases/builder-1.2.4.rdoc +31 -0
- data/doc/releases/builder-2.0.0.rdoc +46 -0
- data/doc/releases/builder-2.1.1.rdoc +58 -0
- data/lib/blankslate.rb +29 -5
- data/test/test_blankslate.rb +217 -0
- data/test/test_eventbuilder.rb +150 -0
- data/test/test_markupbuilder.rb +573 -0
- data/test/test_method_caching.rb +62 -0
- data/test/test_namecollision.rb +39 -0
- data/test/test_xchar.rb +77 -0
- metadata +59 -46
- data/README +0 -31
- data/VERSION +0 -1
- data/blankslate.gemspec +0 -22
- data/spec/blankslate_spec.rb +0 -39
@@ -0,0 +1,31 @@
|
|
1
|
+
= Builder 1.2.4 Released.
|
2
|
+
|
3
|
+
Added a "CDATA" method to the XML Markup builder (from Josh Knowles).
|
4
|
+
|
5
|
+
== What is Builder?
|
6
|
+
|
7
|
+
Builder::XmlMarkup allows easy programmatic creation of XML markup.
|
8
|
+
For example:
|
9
|
+
|
10
|
+
builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)
|
11
|
+
builder.person { |b| b.name("Jim"); b.phone("555-1234") }
|
12
|
+
puts builder.target!
|
13
|
+
|
14
|
+
will generate:
|
15
|
+
|
16
|
+
<person>
|
17
|
+
<name>Jim</name>
|
18
|
+
<phone>555-1234</phone>
|
19
|
+
</person>
|
20
|
+
|
21
|
+
== Availability
|
22
|
+
|
23
|
+
The easiest way to get and install builder is via RubyGems ...
|
24
|
+
|
25
|
+
gem install builder (you may need root/admin privileges)
|
26
|
+
|
27
|
+
== Thanks
|
28
|
+
|
29
|
+
* Josh Knowles for the cdata! patch.
|
30
|
+
|
31
|
+
-- Jim Weirich
|
@@ -0,0 +1,46 @@
|
|
1
|
+
= Builder 2.0.0 Released.
|
2
|
+
|
3
|
+
== Changes in 2.0.0
|
4
|
+
|
5
|
+
* UTF-8 characters in data are now correctly translated to their XML
|
6
|
+
equivalents. (Thanks to Sam Ruby)
|
7
|
+
|
8
|
+
* Attribute values are now escaped by default. See the README
|
9
|
+
file for details.
|
10
|
+
|
11
|
+
<b>NOTE:</b> The escaping attribute values by default is different
|
12
|
+
than in previous releases of Builder. This makes version 2.0.0
|
13
|
+
somewhat incompatible with the 1.x series of Builder. If you use "&",
|
14
|
+
"<", or ">" in attributes values, you may have to change your
|
15
|
+
code. (Essentially you remove the manual escaping. The new way is
|
16
|
+
easier, believe me).
|
17
|
+
|
18
|
+
== What is Builder?
|
19
|
+
|
20
|
+
Builder::XmlMarkup is a library that allows easy programmatic creation
|
21
|
+
of XML markup. For example:
|
22
|
+
|
23
|
+
builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)
|
24
|
+
builder.person { |b| b.name("Jim"); b.phone("555-1234") }
|
25
|
+
|
26
|
+
will generate:
|
27
|
+
|
28
|
+
<person>
|
29
|
+
<name>Jim</name>
|
30
|
+
<phone>555-1234</phone>
|
31
|
+
</person>
|
32
|
+
|
33
|
+
== Availability
|
34
|
+
|
35
|
+
The easiest way to get and install builder is via RubyGems ...
|
36
|
+
|
37
|
+
gem install builder (you may need root/admin privileges)
|
38
|
+
|
39
|
+
== Thanks
|
40
|
+
|
41
|
+
* Sam Ruby for the XChar module and the related UTF-8 translation
|
42
|
+
tools.
|
43
|
+
* Also to Sam Ruby for gently persuading me to start quoting attribute
|
44
|
+
values.
|
45
|
+
|
46
|
+
-- Jim Weirich
|
@@ -0,0 +1,58 @@
|
|
1
|
+
= Builder 2.1.1 Released.
|
2
|
+
|
3
|
+
Release 2.1.1 of Builder is mainly a bug fix release.
|
4
|
+
|
5
|
+
== Changes in 2.1.1
|
6
|
+
|
7
|
+
* Added <tt>reveal</tt> capability to BlankSlate.
|
8
|
+
|
9
|
+
* Fixed a bug in BlankSlate where including a module into Object could
|
10
|
+
cause methods to leak into BlankSlate.
|
11
|
+
|
12
|
+
* Fixed typo in XmlMarkup class docs (from Martin Fowler).
|
13
|
+
|
14
|
+
* Fixed test on private methods to differentiate between targetted and
|
15
|
+
untargetted private methods.
|
16
|
+
|
17
|
+
* Removed legacy capture of @self in XmlBase (@self was used back when
|
18
|
+
we used instance eval).
|
19
|
+
|
20
|
+
* Added additional tests for global functions (both direct and
|
21
|
+
included).
|
22
|
+
|
23
|
+
* Several misc internal cleanups, including rearranging the source
|
24
|
+
code tree.
|
25
|
+
|
26
|
+
<b>NOTE:</b> The escaping attribute values by default is different
|
27
|
+
than in previous releases of Builder. This makes version 2.0.x
|
28
|
+
somewhat incompatible with the 1.x series of Builder. If you use "&",
|
29
|
+
"<", or ">" in attributes values, you may have to change your
|
30
|
+
code. (Essentially you remove the manual escaping. The new way is
|
31
|
+
easier, believe me).
|
32
|
+
|
33
|
+
== What is Builder?
|
34
|
+
|
35
|
+
Builder::XmlMarkup is a library that allows easy programmatic creation
|
36
|
+
of XML markup. For example:
|
37
|
+
|
38
|
+
builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)
|
39
|
+
builder.person { |b| b.name("Jim"); b.phone("555-1234") }
|
40
|
+
|
41
|
+
will generate:
|
42
|
+
|
43
|
+
<person>
|
44
|
+
<name>Jim</name>
|
45
|
+
<phone>555-1234</phone>
|
46
|
+
</person>
|
47
|
+
|
48
|
+
== Availability
|
49
|
+
|
50
|
+
The easiest way to get and install builder is via RubyGems ...
|
51
|
+
|
52
|
+
gem install builder (you may need root/admin privileges)
|
53
|
+
|
54
|
+
== Thanks
|
55
|
+
|
56
|
+
* Martin Fowler for spotting some typos in the documentation.
|
57
|
+
|
58
|
+
-- Jim Weirich
|
data/lib/blankslate.rb
CHANGED
@@ -8,6 +8,30 @@
|
|
8
8
|
# above copyright notice is included.
|
9
9
|
#++
|
10
10
|
|
11
|
+
class String
|
12
|
+
if instance_methods.first.is_a?(Symbol)
|
13
|
+
def _blankslate_as_name
|
14
|
+
to_sym
|
15
|
+
end
|
16
|
+
else
|
17
|
+
def _blankslate_as_name
|
18
|
+
self
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Symbol
|
24
|
+
if instance_methods.first.is_a?(Symbol)
|
25
|
+
def _blankslate_as_name
|
26
|
+
self
|
27
|
+
end
|
28
|
+
else
|
29
|
+
def _blankslate_as_name
|
30
|
+
to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
11
35
|
######################################################################
|
12
36
|
# BlankSlate provides an abstract base class with no predefined
|
13
37
|
# methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
|
@@ -16,12 +40,12 @@
|
|
16
40
|
#
|
17
41
|
class BlankSlate
|
18
42
|
class << self
|
19
|
-
|
43
|
+
|
20
44
|
# Hide the method named +name+ in the BlankSlate class. Don't
|
21
45
|
# hide +instance_eval+ or any method beginning with "__".
|
22
46
|
def hide(name)
|
23
|
-
if instance_methods.
|
24
|
-
name !~ /^(__|instance_eval
|
47
|
+
if instance_methods.include?(name._blankslate_as_name) and
|
48
|
+
name !~ /^(__|instance_eval$)/
|
25
49
|
@hidden_methods ||= {}
|
26
50
|
@hidden_methods[name.to_sym] = instance_method(name)
|
27
51
|
undef_method name
|
@@ -41,7 +65,7 @@ class BlankSlate
|
|
41
65
|
define_method(name, hidden_method)
|
42
66
|
end
|
43
67
|
end
|
44
|
-
|
68
|
+
|
45
69
|
instance_methods.each { |m| hide(m) }
|
46
70
|
end
|
47
71
|
|
@@ -106,4 +130,4 @@ class Module
|
|
106
130
|
end
|
107
131
|
result
|
108
132
|
end
|
109
|
-
end
|
133
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Portions copyright 2004 by Jim Weirich (jim@weirichhouse.org).
|
5
|
+
# Portions copyright 2005 by Sam Ruby (rubys@intertwingly.net).
|
6
|
+
# All rights reserved.
|
7
|
+
|
8
|
+
# Permission is granted for use, copying, modification, distribution,
|
9
|
+
# and distribution of modified versions of this work as long as the
|
10
|
+
# above copyright notice is included.
|
11
|
+
#++
|
12
|
+
|
13
|
+
require 'test/unit'
|
14
|
+
require 'test/preload'
|
15
|
+
require 'blankslate'
|
16
|
+
require 'stringio'
|
17
|
+
|
18
|
+
# Methods to be introduced into the Object class late.
|
19
|
+
module LateObject
|
20
|
+
def late_object
|
21
|
+
33
|
22
|
+
end
|
23
|
+
def LateObject.included(mod)
|
24
|
+
# Modules defining an included method should not prevent blank
|
25
|
+
# slate erasure!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Methods to be introduced into the Kernel module late.
|
30
|
+
module LateKernel
|
31
|
+
def late_kernel
|
32
|
+
44
|
33
|
+
end
|
34
|
+
def LateKernel.included(mod)
|
35
|
+
# Modules defining an included method should not prevent blank
|
36
|
+
# slate erasure!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Introduce some late methods (both module and direct) into the Kernel
|
41
|
+
# module.
|
42
|
+
module Kernel
|
43
|
+
include LateKernel
|
44
|
+
|
45
|
+
def late_addition
|
46
|
+
1234
|
47
|
+
end
|
48
|
+
|
49
|
+
def double_late_addition
|
50
|
+
11
|
51
|
+
end
|
52
|
+
|
53
|
+
def double_late_addition
|
54
|
+
22
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# Introduce some late methods (both module and direct) into the Object
|
60
|
+
# class.
|
61
|
+
class Object
|
62
|
+
include LateObject
|
63
|
+
def another_late_addition
|
64
|
+
4321
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Introduce some late methods by inclusion.
|
69
|
+
module GlobalModule
|
70
|
+
def global_inclusion
|
71
|
+
42
|
72
|
+
end
|
73
|
+
end
|
74
|
+
include GlobalModule
|
75
|
+
|
76
|
+
def direct_global
|
77
|
+
43
|
78
|
+
end
|
79
|
+
|
80
|
+
######################################################################
|
81
|
+
# Test case for blank slate.
|
82
|
+
#
|
83
|
+
class TestBlankSlate < Test::Unit::TestCase
|
84
|
+
def setup
|
85
|
+
@bs = BlankSlate.new
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_undefined_methods_remain_undefined
|
89
|
+
assert_raise(NoMethodError) { @bs.no_such_method }
|
90
|
+
assert_raise(NoMethodError) { @bs.nil? }
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# NOTE: NameError is acceptable because the lack of a '.' means that
|
95
|
+
# Ruby can't tell if it is a method or a local variable.
|
96
|
+
def test_undefined_methods_remain_undefined_during_instance_eval
|
97
|
+
assert_raise(NoMethodError, NameError) do
|
98
|
+
@bs.instance_eval do nil? end
|
99
|
+
end
|
100
|
+
assert_raise(NoMethodError, NameError) do
|
101
|
+
@bs.instance_eval do no_such_method end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_private_methods_are_undefined
|
106
|
+
assert_raise(NoMethodError) do
|
107
|
+
@bs.puts "HI"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_targetted_private_methods_are_undefined_during_instance_eval
|
112
|
+
assert_raise(NoMethodError, NameError) do
|
113
|
+
@bs.instance_eval do self.puts "HI" end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_untargetted_private_methods_are_defined_during_instance_eval
|
118
|
+
oldstdout = $stdout
|
119
|
+
$stdout = StringIO.new
|
120
|
+
@bs.instance_eval do
|
121
|
+
puts "HI"
|
122
|
+
end
|
123
|
+
ensure
|
124
|
+
$stdout = oldstdout
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_methods_added_late_to_kernel_remain_undefined
|
128
|
+
assert_equal 1234, nil.late_addition
|
129
|
+
assert_raise(NoMethodError) { @bs.late_addition }
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_methods_added_late_to_object_remain_undefined
|
133
|
+
assert_equal 4321, nil.another_late_addition
|
134
|
+
assert_raise(NoMethodError) { @bs.another_late_addition }
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_methods_added_late_to_global_remain_undefined
|
138
|
+
assert_equal 42, global_inclusion
|
139
|
+
assert_raise(NoMethodError) { @bs.global_inclusion }
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_preload_method_added
|
143
|
+
assert Kernel.k_added_names.include?(:late_addition)
|
144
|
+
assert Object.o_added_names.include?(:another_late_addition)
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_method_defined_late_multiple_times_remain_undefined
|
148
|
+
assert_equal 22, nil.double_late_addition
|
149
|
+
assert_raise(NoMethodError) { @bs.double_late_addition }
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_late_included_module_in_object_is_ok
|
153
|
+
assert_equal 33, 1.late_object
|
154
|
+
assert_raise(NoMethodError) { @bs.late_object }
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_late_included_module_in_kernel_is_ok
|
158
|
+
assert_raise(NoMethodError) { @bs.late_kernel }
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_revealing_previously_hidden_methods_are_callable
|
162
|
+
with_to_s = Class.new(BlankSlate) do
|
163
|
+
reveal :to_s
|
164
|
+
end
|
165
|
+
assert_match(/^#<.*>$/, with_to_s.new.to_s)
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_revealing_previously_hidden_methods_are_callable_with_block
|
169
|
+
Object.class_eval <<-EOS
|
170
|
+
def given_block(&block)
|
171
|
+
block
|
172
|
+
end
|
173
|
+
EOS
|
174
|
+
|
175
|
+
with_given_block = Class.new(BlankSlate) do
|
176
|
+
reveal :given_block
|
177
|
+
end
|
178
|
+
assert_not_nil with_given_block.new.given_block {}
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_revealing_a_hidden_method_twice_is_ok
|
182
|
+
with_to_s = Class.new(BlankSlate) do
|
183
|
+
reveal :to_s
|
184
|
+
reveal :to_s
|
185
|
+
end
|
186
|
+
assert_match(/^#<.*>$/, with_to_s.new.to_s)
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_revealing_unknown_hidden_method_is_an_error
|
190
|
+
assert_raises(RuntimeError) do
|
191
|
+
Class.new(BlankSlate) do
|
192
|
+
reveal :xyz
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_global_includes_still_work
|
198
|
+
assert_nothing_raised do
|
199
|
+
assert_equal 42, global_inclusion
|
200
|
+
assert_equal 42, Object.new.global_inclusion
|
201
|
+
assert_equal 42, "magic number".global_inclusion
|
202
|
+
assert_equal 43, direct_global
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_reveal_should_not_bind_to_an_instance
|
207
|
+
with_object_id = Class.new(BlankSlate) do
|
208
|
+
reveal(:object_id)
|
209
|
+
end
|
210
|
+
|
211
|
+
obj1 = with_object_id.new
|
212
|
+
obj2 = with_object_id.new
|
213
|
+
|
214
|
+
assert obj1.object_id != obj2.object_id,
|
215
|
+
"Revealed methods should not be bound to a particular instance"
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Portions copyright 2004 by Jim Weirich (jim@weirichhouse.org).
|
5
|
+
# Portions copyright 2005 by Sam Ruby (rubys@intertwingly.net).
|
6
|
+
# All rights reserved.
|
7
|
+
|
8
|
+
# Permission is granted for use, copying, modification, distribution,
|
9
|
+
# and distribution of modified versions of this work as long as the
|
10
|
+
# above copyright notice is included.
|
11
|
+
#++
|
12
|
+
|
13
|
+
require 'test/unit'
|
14
|
+
require 'test/preload'
|
15
|
+
require 'builder'
|
16
|
+
require 'builder/xmlevents'
|
17
|
+
|
18
|
+
class TestEvents < Test::Unit::TestCase
|
19
|
+
|
20
|
+
class Target
|
21
|
+
attr_reader :events
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@events = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def start_tag(tag, attrs)
|
28
|
+
@events << [:start_tag, tag, attrs]
|
29
|
+
end
|
30
|
+
|
31
|
+
def end_tag(tag)
|
32
|
+
@events << [:end_tag, tag]
|
33
|
+
end
|
34
|
+
|
35
|
+
def text(string)
|
36
|
+
@events << [:text, string]
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def setup
|
43
|
+
@target = Target.new
|
44
|
+
@xml = Builder::XmlEvents.new(:target=>@target)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_simple
|
48
|
+
@xml.one
|
49
|
+
expect [:start_tag, :one, nil]
|
50
|
+
expect [:end_tag, :one]
|
51
|
+
expect_done
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_nested
|
55
|
+
@xml.one { @xml.two }
|
56
|
+
expect [:start_tag, :one, nil]
|
57
|
+
expect [:start_tag, :two, nil]
|
58
|
+
expect [:end_tag, :two]
|
59
|
+
expect [:end_tag, :one]
|
60
|
+
expect_done
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_text
|
64
|
+
@xml.one("a")
|
65
|
+
expect [:start_tag, :one, nil]
|
66
|
+
expect [:text, "a"]
|
67
|
+
expect [:end_tag, :one]
|
68
|
+
expect_done
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_special_text
|
72
|
+
@xml.one("H&R")
|
73
|
+
expect [:start_tag, :one, nil]
|
74
|
+
expect [:text, "H&R"]
|
75
|
+
expect [:end_tag, :one]
|
76
|
+
expect_done
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_text_with_entity
|
80
|
+
@xml.one("H&R")
|
81
|
+
expect [:start_tag, :one, nil]
|
82
|
+
expect [:text, "H&R"]
|
83
|
+
expect [:end_tag, :one]
|
84
|
+
expect_done
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_attributes
|
88
|
+
@xml.a(:b=>"c", :x=>"y")
|
89
|
+
expect [:start_tag, :a, {:x => "y", :b => "c"}]
|
90
|
+
expect [:end_tag, :a]
|
91
|
+
expect_done
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_moderately_complex
|
95
|
+
@xml.tag! "address-book" do |x|
|
96
|
+
x.entry :id=>"1" do
|
97
|
+
x.name {
|
98
|
+
x.first "Bill"
|
99
|
+
x.last "Smith"
|
100
|
+
}
|
101
|
+
x.address "Cincinnati"
|
102
|
+
end
|
103
|
+
x.entry :id=>"2" do
|
104
|
+
x.name {
|
105
|
+
x.first "John"
|
106
|
+
x.last "Doe"
|
107
|
+
}
|
108
|
+
x.address "Columbus"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
expect [:start_tag, "address-book".intern, nil]
|
112
|
+
expect [:start_tag, :entry, {:id => "1"}]
|
113
|
+
expect [:start_tag, :name, nil]
|
114
|
+
expect [:start_tag, :first, nil]
|
115
|
+
expect [:text, "Bill"]
|
116
|
+
expect [:end_tag, :first]
|
117
|
+
expect [:start_tag, :last, nil]
|
118
|
+
expect [:text, "Smith"]
|
119
|
+
expect [:end_tag, :last]
|
120
|
+
expect [:end_tag, :name]
|
121
|
+
expect [:start_tag, :address, nil]
|
122
|
+
expect [:text, "Cincinnati"]
|
123
|
+
expect [:end_tag, :address]
|
124
|
+
expect [:end_tag, :entry]
|
125
|
+
expect [:start_tag, :entry, {:id => "2"}]
|
126
|
+
expect [:start_tag, :name, nil]
|
127
|
+
expect [:start_tag, :first, nil]
|
128
|
+
expect [:text, "John"]
|
129
|
+
expect [:end_tag, :first]
|
130
|
+
expect [:start_tag, :last, nil]
|
131
|
+
expect [:text, "Doe"]
|
132
|
+
expect [:end_tag, :last]
|
133
|
+
expect [:end_tag, :name]
|
134
|
+
expect [:start_tag, :address, nil]
|
135
|
+
expect [:text, "Columbus"]
|
136
|
+
expect [:end_tag, :address]
|
137
|
+
expect [:end_tag, :entry]
|
138
|
+
expect [:end_tag, "address-book".intern]
|
139
|
+
expect_done
|
140
|
+
end
|
141
|
+
|
142
|
+
def expect(value)
|
143
|
+
assert_equal value, @target.events.shift
|
144
|
+
end
|
145
|
+
|
146
|
+
def expect_done
|
147
|
+
assert_nil @target.events.shift
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|