serializable_proc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,159 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe 'One arity serializable proc' do
4
+
5
+ extend SerializableProc::Spec::Helpers
6
+
7
+ expected_file = File.expand_path(__FILE__)
8
+ expected_code = "lambda { |lvar_arg| [\"a\", \"b\"].map { |lvar_x| puts(lvar_x) } }"
9
+
10
+ should_handle_proc_variable expected_file, expected_code, {
11
+ # ////////////////////////////////////////////////////////////////////////
12
+ # >> Always newlinling
13
+ # ////////////////////////////////////////////////////////////////////////
14
+ __LINE__ =>
15
+ lambda do |arg|
16
+ %w{a b}.map do |x|
17
+ puts x
18
+ end
19
+ end,
20
+ __LINE__ =>
21
+ lambda { |arg|
22
+ %w{a b}.map{|x|
23
+ puts x
24
+ }
25
+ },
26
+ __LINE__ =>
27
+ proc do |arg|
28
+ %w{a b}.map do |x|
29
+ puts x
30
+ end
31
+ end,
32
+ __LINE__ =>
33
+ lambda { |arg|
34
+ %w{a b}.map{|x|
35
+ puts x
36
+ }
37
+ },
38
+ __LINE__ =>
39
+ Proc.new do |arg|
40
+ %w{a b}.map do |x|
41
+ puts x
42
+ end
43
+ end,
44
+ __LINE__ =>
45
+ Proc.new { |arg|
46
+ %w{a b}.map{|x|
47
+ puts x
48
+ }
49
+ },
50
+ # ////////////////////////////////////////////////////////////////////////
51
+ # >> Partial newlining
52
+ # ////////////////////////////////////////////////////////////////////////
53
+ __LINE__ =>
54
+ lambda do |arg|
55
+ %w{a b}.map do |x| puts x end
56
+ end,
57
+ __LINE__ =>
58
+ lambda { |arg|
59
+ %w{a b}.map{|x| puts x }
60
+ },
61
+ __LINE__ =>
62
+ proc do |arg|
63
+ %w{a b}.map do |x| puts x end
64
+ end,
65
+ __LINE__ =>
66
+ lambda { |arg|
67
+ %w{a b}.map{|x| puts x }
68
+ },
69
+ __LINE__ =>
70
+ Proc.new do |arg|
71
+ %w{a b}.map do |x| puts x end
72
+ end,
73
+ __LINE__ =>
74
+ Proc.new { |arg|
75
+ %w{a b}.map{|x| puts x }
76
+ },
77
+ # ////////////////////////////////////////////////////////////////////////
78
+ # >> No newlining
79
+ # ////////////////////////////////////////////////////////////////////////
80
+ __LINE__ =>
81
+ lambda do |arg| %w{a b}.map do |x| puts x end end,
82
+ __LINE__ =>
83
+ lambda { |arg| %w{a b}.map{|x| puts x } },
84
+ __LINE__ =>
85
+ proc do |arg| %w{a b}.map do |x| puts x end end,
86
+ __LINE__ =>
87
+ lambda { |arg| %w{a b}.map{|x| puts x } },
88
+ __LINE__ =>
89
+ Proc.new do |arg| %w{a b}.map do |x| puts x end end,
90
+ __LINE__ =>
91
+ Proc.new { |arg| %w{a b}.map{|x| puts x } },
92
+ }
93
+
94
+ should "handle block using do ... end [##{__LINE__}]" do
95
+ (
96
+ SerializableProc.new do |arg|
97
+ %w{a b}.map{|x| puts x }
98
+ end
99
+ ).should.be having_expected_proc_attrs(expected_file, __LINE__ - 3, expected_code)
100
+ end
101
+
102
+ should "handle block using do ... end [##{__LINE__}]" do
103
+ (SerializableProc.new do |arg| %w{a b}.map{|x| puts x } end).
104
+ should.be having_expected_proc_attrs(expected_file, __LINE__.pred, expected_code)
105
+ end
106
+
107
+ should "handle block using { ... } [##{__LINE__}]" do
108
+ (
109
+ SerializableProc.new { |arg|
110
+ %w{a b}.map{|x| puts x }
111
+ }
112
+ ).should.be having_expected_proc_attrs(expected_file, __LINE__ - 3, expected_code)
113
+ end
114
+
115
+ should "handle block using { ... } [##{__LINE__}]" do
116
+ (SerializableProc.new { |arg| %w{a b}.map{|x| puts x } }).
117
+ should.be having_expected_proc_attrs(expected_file, __LINE__.pred, expected_code)
118
+ end
119
+
120
+ should "handle fanciful initializing with lambda { ... } [##{__LINE__}]" do
121
+ (SerializableProc.new(&(lambda { |arg| %w{a b}.map{|x| puts x } }))).
122
+ should.be having_expected_proc_attrs(expected_file, __LINE__.pred, expected_code)
123
+ end
124
+
125
+ should "handle fanciful initializing with lambda do ... end [##{__LINE__}]" do
126
+ (
127
+ SerializableProc.new(&(lambda do |arg|
128
+ %w{a b}.map{|x| puts x }
129
+ end))
130
+ ).should.be having_expected_proc_attrs(expected_file, __LINE__ - 3, expected_code)
131
+ end
132
+
133
+ should "handle fanciful initializing with proc { ... } [##{__LINE__}]" do
134
+ (SerializableProc.new(&(proc { |arg| %w{a b}.map{|x| puts x } }))).
135
+ should.be having_expected_proc_attrs(expected_file, __LINE__.pred, expected_code)
136
+ end
137
+
138
+ should "handle fanciful initializing with proc do ... end [##{__LINE__}]" do
139
+ (
140
+ SerializableProc.new(&(proc do |arg|
141
+ %w{a b}.map{|x| puts x }
142
+ end))
143
+ ).should.be having_expected_proc_attrs(expected_file, __LINE__ - 3, expected_code)
144
+ end
145
+
146
+ should "handle fanciful initializing with Proc.new { ... } [##{__LINE__}]" do
147
+ (SerializableProc.new(&(Proc.new { |arg| %w{a b}.map{|x| puts x } }))).
148
+ should.be having_expected_proc_attrs(expected_file, __LINE__.pred, expected_code)
149
+ end
150
+
151
+ should "handle fanciful initializing with Proc.new do ... end [##{__LINE__}]" do
152
+ (
153
+ SerializableProc.new(&(Proc.new do |arg|
154
+ %w{a b}.map{|x| puts x }
155
+ end))
156
+ ).should.be having_expected_proc_attrs(expected_file, __LINE__ - 3, expected_code)
157
+ end
158
+
159
+ end
@@ -0,0 +1,160 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe 'Optional arity serializable proc' do
4
+
5
+ extend SerializableProc::Spec::Helpers
6
+
7
+ expected_file = File.expand_path(__FILE__)
8
+ expected_code = "lambda { |*lvar_args| [\"a\", \"b\"].map { |lvar_x| puts(lvar_x) } }"
9
+
10
+ should_handle_proc_variable expected_file, expected_code, {
11
+ # ////////////////////////////////////////////////////////////////////////
12
+ # >> Always newlinling
13
+ # ////////////////////////////////////////////////////////////////////////
14
+ __LINE__ =>
15
+ lambda do |*args|
16
+ %w{a b}.map do |x|
17
+ puts x
18
+ end
19
+ end,
20
+ __LINE__ =>
21
+ lambda { |*args|
22
+ %w{a b}.map{|x|
23
+ puts x
24
+ }
25
+ },
26
+ __LINE__ =>
27
+ proc do |*args|
28
+ %w{a b}.map do |x|
29
+ puts x
30
+ end
31
+ end,
32
+ __LINE__ =>
33
+ lambda { |*args|
34
+ %w{a b}.map{|x|
35
+ puts x
36
+ }
37
+ },
38
+ __LINE__ =>
39
+ Proc.new do |*args|
40
+ %w{a b}.map do |x|
41
+ puts x
42
+ end
43
+ end,
44
+ __LINE__ =>
45
+ Proc.new { |*args|
46
+ %w{a b}.map{|x|
47
+ puts x
48
+ }
49
+ },
50
+ # ////////////////////////////////////////////////////////////////////////
51
+ # >> Partial newlining
52
+ # ////////////////////////////////////////////////////////////////////////
53
+ __LINE__ =>
54
+ lambda do |*args|
55
+ %w{a b}.map do |x| puts x end
56
+ end,
57
+ __LINE__ =>
58
+ lambda { |*args|
59
+ %w{a b}.map{|x| puts x }
60
+ },
61
+ __LINE__ =>
62
+ proc do |*args|
63
+ %w{a b}.map do |x| puts x end
64
+ end,
65
+ __LINE__ =>
66
+ lambda { |*args|
67
+ %w{a b}.map{|x| puts x }
68
+ },
69
+ __LINE__ =>
70
+ Proc.new do |*args|
71
+ %w{a b}.map do |x| puts x end
72
+ end,
73
+ __LINE__ =>
74
+ Proc.new { |*args|
75
+ %w{a b}.map{|x| puts x }
76
+ },
77
+ # ////////////////////////////////////////////////////////////////////////
78
+ # >> No newlining
79
+ # ////////////////////////////////////////////////////////////////////////
80
+ __LINE__ =>
81
+ lambda do |*args| %w{a b}.map do |x| puts x end end,
82
+ __LINE__ =>
83
+ lambda { |*args| %w{a b}.map{|x| puts x } },
84
+ __LINE__ =>
85
+ proc do |*args| %w{a b}.map do |x| puts x end end,
86
+ __LINE__ =>
87
+ lambda { |*args| %w{a b}.map{|x| puts x } },
88
+ __LINE__ =>
89
+ Proc.new do |*args| %w{a b}.map do |x| puts x end end,
90
+ __LINE__ =>
91
+ Proc.new { |*args| %w{a b}.map{|x| puts x } },
92
+ }
93
+
94
+ should "handle block using do ... end [##{__LINE__}]" do
95
+ (
96
+ SerializableProc.new do |*args|
97
+ %w{a b}.map{|x| puts x }
98
+ end
99
+ ).should.be having_expected_proc_attrs(expected_file, __LINE__ - 3, expected_code)
100
+ end
101
+
102
+ should "handle block using do ... end [##{__LINE__}]" do
103
+ (SerializableProc.new do |*args| %w{a b}.map{|x| puts x } end).
104
+ should.be having_expected_proc_attrs(expected_file, __LINE__.pred, expected_code)
105
+ end
106
+
107
+ should "handle block using { ... } [##{__LINE__}]" do
108
+ (
109
+ SerializableProc.new { |*args|
110
+ %w{a b}.map{|x| puts x }
111
+ }
112
+ ).should.be having_expected_proc_attrs(expected_file, __LINE__ - 3, expected_code)
113
+ end
114
+
115
+ should "handle block using { ... } [##{__LINE__}]" do
116
+ (SerializableProc.new { |*args| %w{a b}.map{|x| puts x } }).
117
+ should.be having_expected_proc_attrs(expected_file, __LINE__.pred, expected_code)
118
+ end
119
+
120
+ should "handle fanciful initializing with lambda { ... } [##{__LINE__}]" do
121
+ (SerializableProc.new(&(lambda { |*args| %w{a b}.map{|x| puts x } }))).
122
+ should.be having_expected_proc_attrs(expected_file, __LINE__.pred, expected_code)
123
+ end
124
+
125
+ should "handle fanciful initializing with lambda do ... end [##{__LINE__}]" do
126
+ (
127
+ SerializableProc.new(&(lambda do |*args|
128
+ %w{a b}.map{|x| puts x }
129
+ end))
130
+ ).should.be having_expected_proc_attrs(expected_file, __LINE__ - 3, expected_code)
131
+ end
132
+
133
+ should "handle fanciful initializing with proc { ... } [##{__LINE__}]" do
134
+ (SerializableProc.new(&(proc { |*args| %w{a b}.map{|x| puts x } }))).
135
+ should.be having_expected_proc_attrs(expected_file, __LINE__.pred, expected_code)
136
+ end
137
+
138
+ should "handle fanciful initializing with proc do ... end [##{__LINE__}]" do
139
+ (
140
+ SerializableProc.new(&(proc do |*args|
141
+ %w{a b}.map{|x| puts x }
142
+ end))
143
+ ).should.be having_expected_proc_attrs(expected_file, __LINE__ - 3, expected_code)
144
+ end
145
+
146
+ should "handle fanciful initializing with Proc.new { ... } [##{__LINE__}]" do
147
+ (SerializableProc.new(&(Proc.new { |*args| %w{a b}.map{|x| puts x } }))).
148
+ should.be having_expected_proc_attrs(expected_file, __LINE__.pred, expected_code)
149
+ end
150
+
151
+ should "handle fanciful initializing with Proc.new do ... end [##{__LINE__}]" do
152
+ (
153
+ SerializableProc.new(&(Proc.new do |*args|
154
+ %w{a b}.map{|x| puts x }
155
+ end))
156
+ ).should.be having_expected_proc_attrs(expected_file, __LINE__ - 3, expected_code)
157
+ end
158
+
159
+
160
+ end
@@ -0,0 +1,191 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe 'Being proc like' do
4
+
5
+ describe '>> ==' do
6
+
7
+ before do
8
+ @proc = SerializableProc.new{ %w{a b}.map{|x| x } }
9
+ end
10
+
11
+ should 'return true if comparing to itself' do
12
+ @proc.should.equal(@proc)
13
+ end
14
+
15
+ should 'return true if another SerializableProc has the same code' do
16
+ SerializableProc.new{ %w{a b}.map{|x| x } }.should.equal(@proc)
17
+ end
18
+
19
+ should 'return false if another SerializableProc does not have the same code' do
20
+ SerializableProc.new{ %w{b c}.map{|x| x } }.should.not.equal(@proc)
21
+ end
22
+
23
+ end
24
+
25
+ describe '>> call (alias [])' do
26
+
27
+ should 'return yield result given no arg' do
28
+ s_proc = SerializableProc.new { %w{b c}.map{|x| x } }
29
+ expected = %w{b c}
30
+ s_proc.call.should.equal(expected)
31
+ s_proc[].should.equal(expected)
32
+ end
33
+
34
+ should 'reflect bound instance variable value (unaffected by outside-scope change)' do
35
+ x, y = 'awe', 'some'
36
+ expected = 'hand' + y
37
+ s_proc = SerializableProc.new { x.sub!('awe','hand'); x + y }
38
+ x, y = 'wonder', 'ful'
39
+ s_proc.call.should.equal(expected)
40
+ s_proc[].should.equal(expected)
41
+ end
42
+
43
+ should 'not affect any outside-scope change to instance variable' do
44
+ x, y = 'awe', 'some'
45
+ s_proc = SerializableProc.new { x.sub!('awe','hand'); x + y }
46
+ x, y = 'wonder', 'ful'
47
+ s_proc.call ; s_proc[]
48
+ x.should.equal('wonder')
49
+ y.should.equal('ful')
50
+ end
51
+
52
+ should 'reflect bound instance variable value (unaffected by outside-scope change)' do
53
+ @x, @y = 'awe', 'some'
54
+ expected = 'hand' + @y
55
+ s_proc = SerializableProc.new { @x.sub!('awe','hand'); @x + @y }
56
+ @x, @y = 'wonder', 'ful'
57
+ s_proc.call.should.equal(expected)
58
+ s_proc[].should.equal(expected)
59
+ end
60
+
61
+ should 'not affect any outside-scope change to instance variable' do
62
+ @x, @y = 'awe', 'some'
63
+ s_proc = SerializableProc.new { @x.sub!('awe','hand'); @x + @y }
64
+ @x, @y = 'wonder', 'ful'
65
+ s_proc.call ; s_proc[]
66
+ @x.should.equal('wonder')
67
+ @y.should.equal('ful')
68
+ end
69
+
70
+ should 'reflect bound class variable value (unaffected by outside-scope change)' do
71
+ @@x, @@y = 'awe', 'some'
72
+ expected = 'hand' + @@y
73
+ s_proc = SerializableProc.new { @@x.sub!('awe','hand'); @@x + @@y }
74
+ @@x, @@y = 'wonder', 'ful'
75
+ s_proc.call.should.equal(expected)
76
+ s_proc[].should.equal(expected)
77
+ end
78
+
79
+ should 'not affect any outside-scope change to class variable' do
80
+ @@x, @@y = 'awe', 'some'
81
+ s_proc = SerializableProc.new { @@x.sub!('awe','hand'); @@x + @@y }
82
+ @@x, @@y = 'wonder', 'ful'
83
+ s_proc.call ; s_proc[]
84
+ @@x.should.equal('wonder')
85
+ @@y.should.equal('ful')
86
+ end
87
+
88
+ should 'reflect bound global variable value (unaffected by outside-scope change)' do
89
+ $x, $y = 'awe', 'some'
90
+ expected = 'hand' + $y
91
+ s_proc = SerializableProc.new { $x.sub!('awe','hand'); $x + $y }
92
+ $x, $y = 'wonder', 'ful'
93
+ s_proc.call.should.equal(expected)
94
+ s_proc[].should.equal(expected)
95
+ end
96
+
97
+ should 'not affect any outside-scope change to global variable' do
98
+ $x, $y = 'awe', 'some'
99
+ s_proc = SerializableProc.new { $x.sub!('awe','hand'); $x + $y }
100
+ $x, $y = 'wonder', 'ful'
101
+ s_proc.call ; s_proc[]
102
+ $x.should.equal('wonder')
103
+ $y.should.equal('ful')
104
+ end
105
+
106
+ end
107
+
108
+ describe '>> clone' do
109
+ should 'return a serializable proc that yields +ve ==' do
110
+ s_proc = SerializableProc.new { %w{b c}.map{|x| x } }
111
+ clone = s_proc.clone
112
+ clone.should.equal(s_proc)
113
+ clone.object_id.should.not.equal(s_proc.object_id)
114
+ end
115
+ end
116
+
117
+ describe '>> binding' do
118
+ should 'raise NotImplementedError' do
119
+ lambda { SerializableProc.new { x }.binding }.should.raise(NotImplementedError)
120
+ end
121
+ # should 'return binding that contains duplicated contextual reference values' do
122
+ # x, @x, @@x, $x = 'lx', 'ix', 'cx', 'gx'
123
+ # expected = {'x' => x.dup, '@x' => @x.dup, '@@x' => @@x.dup, '$x' => $x.dup}
124
+ # s_proc = SerializableProc.new { [x, @x, @@x, $x] }
125
+ # x, @x, @@x, $x = 'ly', 'iy', 'cy', 'gy'
126
+ # expected.each{|k,v| s_proc.binding.eval(k).should.equal(v) }
127
+ # end
128
+ end
129
+
130
+ describe '>> to_proc' do
131
+
132
+ class << self
133
+ def work(&block) ; yield ; end
134
+ end
135
+
136
+ should 'return a non-serializable proc' do
137
+ o_proc = lambda { %w{b c}.map{|x| x } }
138
+ s_proc = SerializableProc.new(&o_proc)
139
+ n_proc = s_proc.to_proc
140
+ s_proc.should.not == n_proc
141
+ n_proc.class.should == Proc
142
+ n_proc.call.should.equal(o_proc.call)
143
+ end
144
+
145
+ should "support passing to a method using '&' char" do
146
+ s_proc = SerializableProc.new { %w{b c}.map{|x| x } }
147
+ work(&s_proc).should.equal(%w{b c})
148
+ end
149
+
150
+ end
151
+
152
+ describe '>> to_s' do
153
+
154
+ extend SerializableProc::Spec::Helpers
155
+
156
+ should 'return extracted code when debug is not specified' do
157
+ x, @x, @@x, $x = 'lx', 'ix', 'cx', 'gx'
158
+ SerializableProc.new{ [x,@x,@@x,$x] }.to_s.should.be \
159
+ having_same_semantics_as('lambda { [x, @x, @@x, $x] }')
160
+ end
161
+
162
+ should 'return extracted code when debug is turned off' do
163
+ x, @x, @@x, $x = 'lx', 'ix', 'cx', 'gx'
164
+ SerializableProc.new{ [x,@x,@@x,$x] }.to_s(false).should.be \
165
+ having_same_semantics_as('lambda { [x, @x, @@x, $x] }')
166
+ end
167
+
168
+ should 'return runnable code when debug is turned on' do
169
+ x, @x, @@x, $x = 'lx', 'ix', 'cx', 'gx'
170
+ SerializableProc.new{ [x,@x,@@x,$x] }.to_s(true).should.be \
171
+ having_same_semantics_as('lambda { [lvar_x, ivar_x, cvar_x, gvar_x] }')
172
+ end
173
+
174
+ end
175
+
176
+ describe '>> arity' do
177
+ {
178
+ __LINE__ => lambda { },
179
+ __LINE__ => lambda {|x| },
180
+ __LINE__ => lambda {|x,y| },
181
+ __LINE__ => lambda {|*x| },
182
+ __LINE__ => lambda {|x, *y| },
183
+ __LINE__ => lambda {|(x,y)| },
184
+ }.each do |debug, block|
185
+ should "return arity of initializing block [##{debug}]" do
186
+ SerializableProc.new(&block).arity.should.equal(block.arity)
187
+ end
188
+ end
189
+ end
190
+
191
+ end
@@ -0,0 +1,100 @@
1
+ require 'rubygems'
2
+ require 'bacon'
3
+ require 'tempfile'
4
+ require 'ruby2ruby'
5
+
6
+ $parse_tree_installed =
7
+ begin
8
+ require 'parse_tree'
9
+ true
10
+ rescue LoadError
11
+ require 'ruby_parser'
12
+ nil
13
+ end
14
+
15
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
16
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
17
+ require 'serializable_proc'
18
+
19
+ Bacon.summary_on_exit
20
+
21
+ class SerializableProc
22
+
23
+ attr_reader :code, :file, :line
24
+
25
+ def binding_dump
26
+ @binding.instance_variable_get(:@vars)
27
+ end
28
+
29
+ module Spec
30
+
31
+ module Matchers
32
+
33
+ def having_same_semantics_as(code2)
34
+ to_code = lambda{|sexp| ::Ruby2Ruby.new.process(sexp) }
35
+ to_sexp = $parse_tree_installed ?
36
+ lambda{|code| Unifier.new.process(::ParseTree.translate(code)) } :
37
+ lambda{|code| ::RubyParser.new.parse(code) }
38
+ normalize = lambda{|code| to_code[to_sexp[code]].sub('lambda','proc') }
39
+ lambda {|code1| normalize[code1].should.equal(normalize[code2]) }
40
+ end
41
+
42
+ def same_object_as(o2)
43
+ lambda {|o1| o1.object_id == o2.object_id }
44
+ end
45
+
46
+ def having_expected_proc_attrs(file, line, code)
47
+ lambda do |s_proc|
48
+ s_proc.code[:runnable].should.be having_same_semantics_as(code)
49
+ s_proc.file.should.equal(file)
50
+ s_proc.line.should.equal(line)
51
+ end
52
+ end
53
+
54
+ def raising_cannot_serialize_variable_error(var)
55
+ lambda do |block|
56
+ block.should.raise(SerializableProc::CannotSerializeVariableError).
57
+ message.should.equal('Variable %s cannot be serialized !!' % var)
58
+ true
59
+ end
60
+ end
61
+
62
+ def raising_cannot_analyse_error(descrp)
63
+ lambda do |block|
64
+ block.should.raise(SerializableProc::CannotAnalyseCodeError).message.should.
65
+ equal("Static code analysis can only handle single occurrence of #{descrp} per line !!")
66
+ true
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ module Macros
73
+
74
+ def should_have_expected_binding(s_proc, expected)
75
+ s_proc.binding_dump.should.equal(expected)
76
+ expected.each do |key, val|
77
+ (s_proc.binding_dump[key].should.not.be same_object_as(val)) if val
78
+ end
79
+ end
80
+
81
+ def should_handle_proc_variable(file, code, test_args)
82
+ test_args.each do |line, block|
83
+ should "handle proc variable [##{line}]" do
84
+ s_proc = SerializableProc.new(&block)
85
+ s_proc.code[:runnable].should.be having_same_semantics_as(code)
86
+ s_proc.file.should.equal(file)
87
+ s_proc.line.should.equal(line.succ)
88
+ end
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ module Helpers
95
+ include Macros
96
+ include Matchers
97
+ end
98
+
99
+ end
100
+ end