doodle 0.2.2 → 0.2.3
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/History.txt +24 -0
- data/Manifest.txt +26 -1
- data/README.txt +9 -8
- data/lib/doodle.rb +43 -1496
- data/lib/doodle/app.rb +6 -0
- data/lib/doodle/attribute.rb +165 -0
- data/lib/doodle/base.rb +180 -0
- data/lib/doodle/collector-1.9.rb +72 -0
- data/lib/doodle/collector.rb +191 -0
- data/lib/doodle/comparable.rb +8 -0
- data/lib/doodle/conversion.rb +80 -0
- data/lib/doodle/core.rb +42 -0
- data/lib/doodle/datatype-holder.rb +39 -0
- data/lib/doodle/debug.rb +20 -0
- data/lib/doodle/deferred.rb +13 -0
- data/lib/doodle/equality.rb +21 -0
- data/lib/doodle/exceptions.rb +29 -0
- data/lib/doodle/factory.rb +91 -0
- data/lib/doodle/getter-setter.rb +154 -0
- data/lib/doodle/info.rb +298 -0
- data/lib/doodle/inherit.rb +40 -0
- data/lib/doodle/json.rb +38 -0
- data/lib/doodle/marshal.rb +16 -0
- data/lib/doodle/normalized_array.rb +512 -0
- data/lib/doodle/normalized_hash.rb +356 -0
- data/lib/doodle/ordered-hash.rb +8 -0
- data/lib/doodle/singleton.rb +23 -0
- data/lib/doodle/smoke-and-mirrors.rb +23 -0
- data/lib/doodle/to_hash.rb +17 -0
- data/lib/doodle/utils.rb +173 -11
- data/lib/doodle/validation.rb +122 -0
- data/lib/doodle/version.rb +1 -1
- data/lib/molic_orderedhash.rb +24 -10
- data/spec/assigned_spec.rb +45 -0
- data/spec/attributes_spec.rb +7 -7
- data/spec/collector_spec.rb +100 -13
- data/spec/doodle_context_spec.rb +5 -5
- data/spec/from_spec.rb +43 -3
- data/spec/json_spec.rb +232 -0
- data/spec/member_init_spec.rb +11 -11
- data/spec/modules_spec.rb +4 -4
- data/spec/multi_collector_spec.rb +91 -0
- data/spec/must_spec.rb +32 -0
- data/spec/spec_helper.rb +14 -4
- data/spec/specialized_attribute_class_spec.rb +2 -2
- data/spec/typed_collector_spec.rb +57 -0
- data/spec/xml_spec.rb +8 -8
- metadata +33 -3
data/spec/doodle_context_spec.rb
CHANGED
@@ -11,15 +11,15 @@ describe Doodle, "doodle_context" do
|
|
11
11
|
# - a proc/lamba is treated as a literal argument, i.e. the
|
12
12
|
# - value is set to a Proc
|
13
13
|
# - a block argument, on the other hand, is instance
|
14
|
-
#
|
14
|
+
# evaluated during initialization
|
15
15
|
# - consequences
|
16
16
|
# - can only be done in init block
|
17
17
|
# - somewhat subtle difference (from programmer's point of
|
18
|
-
#
|
18
|
+
# view) between a proc and a block
|
19
19
|
# Also note re: Doodle.parent - its value is only valid
|
20
20
|
# during initialization - this is a way to capture that
|
21
|
-
# value for
|
22
|
-
|
21
|
+
# value for use later
|
22
|
+
|
23
23
|
init { doodle.parent }
|
24
24
|
end
|
25
25
|
end
|
@@ -29,7 +29,7 @@ describe Doodle, "doodle_context" do
|
|
29
29
|
end
|
30
30
|
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
it 'should provide a means to find out the current parent of an item in initialization block' do
|
34
34
|
dad = Parent 'Conn' do
|
35
35
|
child 'Sean'
|
data/spec/from_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
|
3
|
-
describe 'Doodle', '
|
3
|
+
describe 'Doodle', 'from' do
|
4
4
|
temporary_constant :Foo, :Name do
|
5
5
|
before :each do
|
6
6
|
class Name < String
|
@@ -25,7 +25,7 @@ describe 'Doodle', 'applying Doodle type conversions' do
|
|
25
25
|
it 'should convert a value based on conversions in doodle class' do
|
26
26
|
proc { foo = Foo 'Arthur' }.should_not raise_error
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
it 'should convert a value based on conversions in doodle class to the correct class' do
|
30
30
|
foo = Foo 'Arthur'
|
31
31
|
foo.name.class.should_be Name
|
@@ -38,6 +38,46 @@ describe 'Doodle', 'applying Doodle type conversions' do
|
|
38
38
|
it 'should apply validations from doodle type' do
|
39
39
|
proc { Foo 'Art' }.should raise_error(Doodle::ConversionError)
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'Doodle', 'from' do
|
46
|
+
temporary_constant :Answer do
|
47
|
+
before :each do
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should allow specifying from in has params' do
|
51
|
+
class Answer < Doodle
|
52
|
+
has :value, :from => { Integer => proc {|i| i.to_s }}
|
53
|
+
end
|
54
|
+
name = Answer.new(42)
|
55
|
+
name.value.should_be "42"
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should allow specifying from in has params with kind specified' do
|
59
|
+
class Answer < Doodle
|
60
|
+
has :value, :kind => String, :from => { Integer => proc {|i| i.to_s }}
|
61
|
+
end
|
62
|
+
name = Answer.new(42)
|
63
|
+
name.value.should_be "42"
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should override from clause in has params with one defined in block' do
|
67
|
+
class Answer < Doodle
|
68
|
+
has :value, :kind => String, :from => { Integer => proc {|i| i.to_s }} do
|
69
|
+
# this should override :from clause in has params
|
70
|
+
from Float do |i|
|
71
|
+
(Integer(i + 20)).to_s
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
name = Answer.new(22.0)
|
77
|
+
name.value.should_be "42"
|
78
|
+
|
79
|
+
name = Answer.new(42)
|
80
|
+
name.value.should_be "42"
|
81
|
+
end
|
42
82
|
end
|
43
83
|
end
|
data/spec/json_spec.rb
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require 'doodle/json'
|
3
|
+
|
4
|
+
def compare_json(data, json)
|
5
|
+
JSON.parse(data.to_json, :create_additions => false).should_be JSON.parse(json, :create_additions => false)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Doodle, 'JSON serialization within a module' do
|
9
|
+
temporary_constants :Container, :Base, :Slideshow, :Layout do
|
10
|
+
before(:each) do
|
11
|
+
@json_source = <<EOT
|
12
|
+
{
|
13
|
+
"json_class":"Container::Slideshow",
|
14
|
+
"data":{
|
15
|
+
"name":"test",
|
16
|
+
"layout":{
|
17
|
+
"json_class":"Container::Layout",
|
18
|
+
"data":{
|
19
|
+
"template":"generic"
|
20
|
+
}
|
21
|
+
},
|
22
|
+
"id":1
|
23
|
+
}
|
24
|
+
}
|
25
|
+
EOT
|
26
|
+
@json_data = JSON.parse(@json_source, :create_additions => false)
|
27
|
+
|
28
|
+
module ::Container
|
29
|
+
class Base < Doodle
|
30
|
+
include Doodle::JSON
|
31
|
+
end
|
32
|
+
class Layout < Base
|
33
|
+
has :template
|
34
|
+
end
|
35
|
+
class Slideshow < Base
|
36
|
+
has :id, :kind => Integer do
|
37
|
+
from String do |s|
|
38
|
+
s.to_i
|
39
|
+
end
|
40
|
+
end
|
41
|
+
has :name, :kind => String
|
42
|
+
has Layout
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
it 'should serialize to json' do
|
47
|
+
slideshow = Container::Slideshow.new do
|
48
|
+
id 1
|
49
|
+
name "test"
|
50
|
+
layout "generic"
|
51
|
+
end
|
52
|
+
compare_json(slideshow, @json_source)
|
53
|
+
end
|
54
|
+
it 'should serialize from json' do
|
55
|
+
slideshow = Container::Slideshow.new do
|
56
|
+
id 1
|
57
|
+
name "test"
|
58
|
+
layout "generic"
|
59
|
+
end
|
60
|
+
ss2 = Doodle.from_json(@json_source)
|
61
|
+
ss2.should_be slideshow
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe Doodle, 'JSON serialization at top level' do
|
67
|
+
temporary_constants :Base, :Slideshow, :Layout do
|
68
|
+
before(:each) do
|
69
|
+
@json_source = <<EOT
|
70
|
+
{
|
71
|
+
"json_class":"Slideshow",
|
72
|
+
"data":{
|
73
|
+
"name":"test",
|
74
|
+
"layout":{
|
75
|
+
"json_class":"Layout",
|
76
|
+
"data":{
|
77
|
+
"template":"generic"
|
78
|
+
}
|
79
|
+
},
|
80
|
+
"id":1
|
81
|
+
}
|
82
|
+
}
|
83
|
+
EOT
|
84
|
+
class ::Base < Doodle
|
85
|
+
include Doodle::JSON
|
86
|
+
end
|
87
|
+
class ::Layout < Base
|
88
|
+
has :template
|
89
|
+
end
|
90
|
+
class ::Slideshow < Base
|
91
|
+
has :id, :kind => Integer do
|
92
|
+
from String do |s|
|
93
|
+
s.to_i
|
94
|
+
end
|
95
|
+
end
|
96
|
+
has :name, :kind => String
|
97
|
+
has Layout
|
98
|
+
end
|
99
|
+
end
|
100
|
+
it 'should serialize to json' do
|
101
|
+
slideshow = ::Slideshow.new do
|
102
|
+
id 1
|
103
|
+
name "test"
|
104
|
+
layout "generic"
|
105
|
+
end
|
106
|
+
compare_json(slideshow, @json_source)
|
107
|
+
end
|
108
|
+
it 'should serialize from json' do
|
109
|
+
slideshow = ::Slideshow.new do
|
110
|
+
id 1
|
111
|
+
name "test"
|
112
|
+
layout "generic"
|
113
|
+
end
|
114
|
+
ss2 = Doodle.from_json(@json_source)
|
115
|
+
ss2.should_be slideshow
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe Doodle, 'JSON' do
|
121
|
+
temporary_constant :Address do
|
122
|
+
before :each do
|
123
|
+
class ::Address < Doodle
|
124
|
+
include Doodle::JSON
|
125
|
+
has :where, :default => "home"
|
126
|
+
has :city
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should not raise an error when supplying attribute values' do
|
131
|
+
proc {
|
132
|
+
Address do
|
133
|
+
city "London"
|
134
|
+
end
|
135
|
+
}.should_not raise_error
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'should accept attributes defined in block' do
|
139
|
+
a = Address do
|
140
|
+
city "London"
|
141
|
+
end
|
142
|
+
a.city.should_be "London"
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should output non-doodle attributes as JSON attributes' do
|
146
|
+
a = Address do
|
147
|
+
city "London"
|
148
|
+
end
|
149
|
+
compare_json(a, <<EOT)
|
150
|
+
{
|
151
|
+
"json_class":"Address",
|
152
|
+
"data":{
|
153
|
+
"city":"London"
|
154
|
+
}
|
155
|
+
}
|
156
|
+
EOT
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe Doodle, 'JSON' do
|
162
|
+
temporary_constant :Base, :City, :Address do
|
163
|
+
before :each do
|
164
|
+
class ::Base < Doodle
|
165
|
+
include Doodle::JSON
|
166
|
+
end
|
167
|
+
class ::City < Base
|
168
|
+
has :_text_
|
169
|
+
has :country, :default => "UK"
|
170
|
+
end
|
171
|
+
class ::Address < Base
|
172
|
+
has :where, :default => "home"
|
173
|
+
has City
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should output required tags in JSON' do
|
178
|
+
a = Address do
|
179
|
+
city "London"
|
180
|
+
end
|
181
|
+
compare_json(a, <<EOT)
|
182
|
+
{
|
183
|
+
"json_class":"Address",
|
184
|
+
"data":{
|
185
|
+
"city":{
|
186
|
+
"json_class":"City",
|
187
|
+
"data":{"_text_":"London"}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
EOT
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'should output specified optional attributes as json attributes if kind not a doodle class' do
|
195
|
+
a = Address :where => 'home' do
|
196
|
+
city "London"
|
197
|
+
end
|
198
|
+
compare_json(a, <<EOT)
|
199
|
+
{
|
200
|
+
"json_class":"Address",
|
201
|
+
"data":{
|
202
|
+
"city":{
|
203
|
+
"json_class":"City",
|
204
|
+
"data":{"_text_":"London"}
|
205
|
+
},
|
206
|
+
"where":"home"
|
207
|
+
}
|
208
|
+
}
|
209
|
+
EOT
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'should output specified optional attributes' do
|
213
|
+
a = Address :where => 'home' do
|
214
|
+
city "London", :country => "England" do
|
215
|
+
country "UK"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
compare_json(a, <<EOT)
|
219
|
+
{
|
220
|
+
"json_class":"Address",
|
221
|
+
"data":{
|
222
|
+
"city":{"json_class":"City", "data":{"_text_":"London", "country":"UK"}},
|
223
|
+
"where":"home"
|
224
|
+
}
|
225
|
+
}
|
226
|
+
EOT
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
data/spec/member_init_spec.rb
CHANGED
@@ -13,16 +13,16 @@ describe 'Doodle', 'initialization of scalar attributes from hash' do
|
|
13
13
|
has :v2, :kind => String, :default => "bar"
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
it 'should initialize an attribute from a hash' do
|
18
18
|
foo = Foo do
|
19
|
-
bar :name => "hello", :count => 1
|
19
|
+
bar :name => "hello", :count => 1
|
20
20
|
end
|
21
21
|
foo.bar.name.should_be "hello"
|
22
22
|
foo.bar.count.should_be 1
|
23
23
|
end
|
24
24
|
it 'should fail trying to initialize with incorrect keyword values' do
|
25
|
-
proc {
|
25
|
+
proc {
|
26
26
|
foo = Foo do
|
27
27
|
bar :name => 1, :count => "hello"
|
28
28
|
end
|
@@ -30,7 +30,7 @@ describe 'Doodle', 'initialization of scalar attributes from hash' do
|
|
30
30
|
end
|
31
31
|
it 'should work with positional args' do
|
32
32
|
foo = nil
|
33
|
-
proc {
|
33
|
+
proc {
|
34
34
|
foo = Foo do
|
35
35
|
bar "hello", 1
|
36
36
|
end
|
@@ -40,7 +40,7 @@ describe 'Doodle', 'initialization of scalar attributes from hash' do
|
|
40
40
|
end
|
41
41
|
it 'should work with block initialization' do
|
42
42
|
foo = nil
|
43
|
-
proc {
|
43
|
+
proc {
|
44
44
|
foo = Foo do
|
45
45
|
bar do
|
46
46
|
name "hello"
|
@@ -53,7 +53,7 @@ describe 'Doodle', 'initialization of scalar attributes from hash' do
|
|
53
53
|
end
|
54
54
|
it 'should work with arg and block initialization' do
|
55
55
|
foo = nil
|
56
|
-
proc {
|
56
|
+
proc {
|
57
57
|
foo = Foo do
|
58
58
|
bar "hello" do
|
59
59
|
count 1
|
@@ -65,7 +65,7 @@ describe 'Doodle', 'initialization of scalar attributes from hash' do
|
|
65
65
|
end
|
66
66
|
it 'should work with keyword and block initialization' do
|
67
67
|
foo = nil
|
68
|
-
proc {
|
68
|
+
proc {
|
69
69
|
foo = Foo do
|
70
70
|
bar :name => "hello" do
|
71
71
|
count 1
|
@@ -77,7 +77,7 @@ describe 'Doodle', 'initialization of scalar attributes from hash' do
|
|
77
77
|
end
|
78
78
|
it 'should raise error with invalid keyword and block initialization' do
|
79
79
|
foo = nil
|
80
|
-
proc {
|
80
|
+
proc {
|
81
81
|
foo = Foo do
|
82
82
|
bar :name => 1 do
|
83
83
|
count "hello"
|
@@ -87,7 +87,7 @@ describe 'Doodle', 'initialization of scalar attributes from hash' do
|
|
87
87
|
end
|
88
88
|
it 'should raise error with keyword and invalid block initialization' do
|
89
89
|
foo = nil
|
90
|
-
proc {
|
90
|
+
proc {
|
91
91
|
foo = Foo do
|
92
92
|
bar :name => "hello" do
|
93
93
|
count "hello"
|
@@ -97,7 +97,7 @@ describe 'Doodle', 'initialization of scalar attributes from hash' do
|
|
97
97
|
end
|
98
98
|
it 'should initialize non-Doodle or Proc with simple value' do
|
99
99
|
foo = nil
|
100
|
-
proc {
|
100
|
+
proc {
|
101
101
|
foo = Foo do
|
102
102
|
bar :name => "hello", :count => 1
|
103
103
|
v2 "Hello"
|
@@ -108,7 +108,7 @@ describe 'Doodle', 'initialization of scalar attributes from hash' do
|
|
108
108
|
foo.v2.should_be "Hello"
|
109
109
|
end
|
110
110
|
it 'should fail trying to initialize an inappropriate attribute (not a Doodle or Proc) from a block' do
|
111
|
-
proc {
|
111
|
+
proc {
|
112
112
|
foo = Foo do
|
113
113
|
bar :name => "hello", :count => 1
|
114
114
|
v2 do
|
data/spec/modules_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
|
3
|
-
describe Doodle, "
|
3
|
+
describe Doodle, "definitions within rspec" do
|
4
4
|
temporary_constant :Foo1 do
|
5
5
|
before :each do
|
6
6
|
class Foo1 < Doodle
|
@@ -13,7 +13,7 @@ describe Doodle, "Definitions within rspec" do
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
describe Doodle, "
|
16
|
+
describe Doodle, "definitions at top level" do
|
17
17
|
temporary_constant :Foo2 do
|
18
18
|
before :each do
|
19
19
|
class ::Object
|
@@ -28,7 +28,7 @@ describe Doodle, "Definitions at top level" do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
describe Doodle, "
|
31
|
+
describe Doodle, "definitions in modules" do
|
32
32
|
temporary_constant :Foo3, :Bar3 do
|
33
33
|
before :each do
|
34
34
|
module ::Bar3
|
@@ -43,7 +43,7 @@ describe Doodle, "Definitions in modules" do
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
describe Doodle, "
|
46
|
+
describe Doodle, "definitions in module_eval" do
|
47
47
|
temporary_constant :Foo4, :Bar4 do
|
48
48
|
before :each do
|
49
49
|
module Bar4
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe Doodle, 'multiple collector' do
|
4
|
+
temporary_constants :Text, :Item, :List do
|
5
|
+
before :each do
|
6
|
+
#: definition
|
7
|
+
class ::Item < Doodle
|
8
|
+
has :name, :kind => String
|
9
|
+
end
|
10
|
+
class ::Text < Doodle
|
11
|
+
has :body, :kind => String
|
12
|
+
end
|
13
|
+
class ::List < Doodle
|
14
|
+
has :items, :collect => [Item, Text]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should accept convertible values in collector' do
|
19
|
+
list = nil
|
20
|
+
no_error {
|
21
|
+
list = List do
|
22
|
+
item "Hello"
|
23
|
+
text "World"
|
24
|
+
end
|
25
|
+
}
|
26
|
+
list.items.size.should_be 2
|
27
|
+
list.items[0].should_be Item("Hello")
|
28
|
+
list.items[1].should_be Text("World")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe Doodle, 'multiple collector' do
|
34
|
+
temporary_constants :Text, :Item, :List do
|
35
|
+
before :each do
|
36
|
+
#: definition
|
37
|
+
class ::Item < Doodle
|
38
|
+
has :name, :kind => String
|
39
|
+
end
|
40
|
+
class ::Text < Doodle
|
41
|
+
has :body, :kind => String
|
42
|
+
end
|
43
|
+
class ::List < Doodle
|
44
|
+
has :items, :collect => [ { :foo => Item }, { :bar => Text } ]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should accept convertible values in collector using specified collector methods' do
|
49
|
+
list = nil
|
50
|
+
no_error {
|
51
|
+
list = List do
|
52
|
+
foo "Hello"
|
53
|
+
bar "World"
|
54
|
+
end
|
55
|
+
}
|
56
|
+
list.items.size.should_be 2
|
57
|
+
list.items[0].should_be Item("Hello")
|
58
|
+
list.items[1].should_be Text("World")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe Doodle, 'multiple collector' do
|
64
|
+
temporary_constants :Text, :Item, :List do
|
65
|
+
before :each do
|
66
|
+
#: definition
|
67
|
+
class ::Item < Doodle
|
68
|
+
has :name, :kind => String
|
69
|
+
end
|
70
|
+
class ::Text < Doodle
|
71
|
+
has :body, :kind => String
|
72
|
+
end
|
73
|
+
class ::List < Doodle
|
74
|
+
has :items, :collect => { :foo => Item, :bar => Text }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should accept convertible values in collector using specified collector methods' do
|
79
|
+
list = nil
|
80
|
+
no_error {
|
81
|
+
list = List do
|
82
|
+
foo "Hello"
|
83
|
+
bar "World"
|
84
|
+
end
|
85
|
+
}
|
86
|
+
list.items.size.should_be 2
|
87
|
+
list.items[0].should_be Item("Hello")
|
88
|
+
list.items[1].should_be Text("World")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|