ygg 0.0.2 → 1.0.6
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/README.rdoc +12 -14
- data/emulations/samples/event.ygg +3 -0
- data/emulations/samples/invalid_resource.ygg +10 -0
- data/emulations/samples/to_modify.ygg +177 -0
- data/emulations/samples/to_modify_2.ygg +153 -0
- data/emulations/samples/user.ygg +6 -0
- data/emulations/samples/valid.ygg +3 -0
- data/emulations/save_yaml_object.rb +16 -0
- data/emulations/tree.ygg +7 -0
- data/features/accessors.feature +26 -0
- data/features/dsl.feature +24 -0
- data/features/events.feature +17 -0
- data/features/simple_record.feature +40 -0
- data/features/steps/accessors.rb +8 -0
- data/features/steps/dsl.rb +20 -0
- data/features/steps/events.rb +12 -0
- data/features/steps/simple_record.rb +104 -0
- data/features/support/env.rb +2 -0
- data/features/support/hooks.rb +3 -0
- data/lib/ygg.rb +97 -3
- data/lib/ygg/bck/base.rb +63 -0
- data/lib/ygg/bck/branch.rb +31 -0
- data/lib/ygg/bck/exceptions.rb +3 -0
- data/lib/ygg/bck/link.rb +6 -0
- data/lib/ygg/{base.rb → bck/model.rb} +7 -107
- data/lib/ygg/databundle.rb +25 -0
- data/lib/ygg/exceptions.rb +6 -0
- data/lib/ygg/record.rb +7 -0
- data/lib/ygg/session.rb +44 -0
- data/lib/ygg/version.rb +3 -3
- data/spec/spec_helper.rb +61 -0
- data/spec/ygg/databundle_spec.rb +54 -0
- data/spec/ygg/session_spec.rb +203 -0
- data/spec/ygg/ygg_spec.rb +364 -0
- data/ygg.gemspec +5 -0
- metadata +67 -4
data/lib/ygg/bck/link.rb
ADDED
@@ -1,61 +1,11 @@
|
|
1
|
-
require "yaml"
|
2
|
-
|
3
1
|
module Ygg
|
4
|
-
|
5
|
-
@@source
|
6
|
-
|
7
|
-
def self.load(source = "ygg.yaml", &block)
|
8
|
-
|
9
|
-
if source.kind_of? Symbol
|
10
|
-
if File.exists? "#{source}.yml"
|
11
|
-
source = "#{source}.yml"
|
12
|
-
else
|
13
|
-
source = "#{source}.yaml"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
@@source ||= source if source == "ygg.yaml"
|
17
|
-
@@source = source if source != "ygg.yaml"
|
18
|
-
|
19
|
-
@@data = {}
|
20
|
-
@@data = YAML.load_file @@source if File.exists? @@source
|
21
|
-
|
22
|
-
@@data.each do |key, model|
|
23
|
-
model.each do |resource|
|
24
|
-
resource.loading! if resource.respond_to? :loading!
|
25
|
-
end if model.kind_of? Array
|
26
|
-
end
|
27
|
-
|
28
|
-
if block; yield; self.save; self.close; end
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.save
|
32
|
-
@@data ||= {}
|
33
|
-
@@data.each do |key, model|
|
34
|
-
model.each do |resource|
|
35
|
-
resource._finalize! if resource.respond_to? :_finalize!
|
36
|
-
end if model.kind_of? Array
|
37
|
-
end
|
38
|
-
|
39
|
-
File.open @@source, "w" do |file|
|
40
|
-
file.write YAML.dump(@@data)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.close; @@data = nil; @@source; end
|
45
|
-
|
46
|
-
def self.data; @@data ||= {}; end
|
47
|
-
|
48
|
-
def self.symbolize(res)
|
49
|
-
return res.to_s.downcase.to_sym if res.kind_of? Class
|
50
|
-
res.class.to_s.downcase.to_sym
|
51
|
-
end
|
52
|
-
|
2
|
+
|
53
3
|
class Model
|
54
4
|
@@validator = [/(.*)/, [:index]]
|
55
5
|
|
56
6
|
def initialize(*args)
|
57
|
-
Ygg.data[
|
58
|
-
Ygg.data[
|
7
|
+
Ygg.data[self.class] ||= []
|
8
|
+
Ygg.data[self.class] << self
|
59
9
|
@created_at ||= Time.now if respond_to? :created_at
|
60
10
|
@updated_at ||= Time.now if respond_to? :updated_at
|
61
11
|
|
@@ -79,11 +29,11 @@ module Ygg
|
|
79
29
|
def _finalize!; finalize! if respond_to? :finalize!; end
|
80
30
|
def _loading!; loading! if respond_to? :loading!; end
|
81
31
|
def destroy
|
82
|
-
Ygg.data[
|
32
|
+
Ygg.data[self.class].delete self
|
83
33
|
# Destroy all Links
|
84
34
|
end
|
85
35
|
def _id
|
86
|
-
Ygg.data[
|
36
|
+
Ygg.data[self.class].index self
|
87
37
|
end
|
88
38
|
|
89
39
|
# Instance methods : Links
|
@@ -144,7 +94,7 @@ module Ygg
|
|
144
94
|
end
|
145
95
|
|
146
96
|
# Class methods : Shorthands
|
147
|
-
def self.all; Ygg.data[
|
97
|
+
def self.all; Ygg.data[self.class]; end
|
148
98
|
def self.validator; @@validator; end
|
149
99
|
|
150
100
|
# Class methods : Metamethods
|
@@ -170,55 +120,5 @@ module Ygg
|
|
170
120
|
end
|
171
121
|
end
|
172
122
|
end
|
173
|
-
|
174
|
-
class Link < Model
|
175
|
-
attr_accessor :source, :target, :info
|
176
|
-
def &(data); @info = data; end
|
177
|
-
end
|
178
|
-
|
179
|
-
class InvalidInitializor < RuntimeError; end
|
180
|
-
|
181
|
-
module Delegator
|
182
|
-
def delegate(target, *methods)
|
183
|
-
methods.each do |method|
|
184
|
-
self.class.send :define_method, method do |*a, &b|
|
185
|
-
target.send method, *a, &b
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
class Array
|
193
|
-
def method_missing(x, *args)
|
194
|
-
if matches = x.to_s.match(/^sort_by_(.+)$/)
|
195
|
-
key = matches[1].to_sym
|
196
|
-
self.sort_by { |x| x.send(key) }
|
197
|
-
|
198
|
-
elsif matches = x.to_s.match(/^by_(.+)$/) # Extend with regexps
|
199
|
-
key = matches[1].to_sym
|
200
|
-
|
201
|
-
if to_find = args.shift
|
202
|
-
result = []
|
203
|
-
self.each do |res|
|
204
|
-
if res.respond_to? key
|
205
|
-
datus = res.send key
|
206
|
-
result << res if datus == to_find
|
207
|
-
end
|
208
|
-
end
|
209
|
-
return [] if result.length == 0
|
210
|
-
return result[0] if result.length == 1
|
211
|
-
return result
|
212
|
-
|
213
|
-
else
|
214
|
-
self.send(:"sort_by_#{key}")[0] unless empty?
|
215
|
-
end
|
216
|
-
|
217
|
-
return []
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
#include Ygg::Delegator
|
223
123
|
|
224
|
-
|
124
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Ygg
|
2
|
+
class DataBundle
|
3
|
+
def initialize(source_array = nil)
|
4
|
+
@data = source_array || []
|
5
|
+
end
|
6
|
+
|
7
|
+
def of_type(type)
|
8
|
+
@result = DataBundle.new
|
9
|
+
@data.each do |resource|
|
10
|
+
@result << resource if resource.instance_of? type
|
11
|
+
end
|
12
|
+
return @result.empty? ? nil : @result
|
13
|
+
end
|
14
|
+
|
15
|
+
# Delegate to array
|
16
|
+
def first; @data.first; end
|
17
|
+
def empty?; @data.empty?; end
|
18
|
+
def <<(*args); @data.<<(*args); end
|
19
|
+
def to_yaml; @data.to_yaml; end
|
20
|
+
def include?(*args); @data.include?(*args); end
|
21
|
+
def each(&block); @data.each { |e| block.call(e) }; end
|
22
|
+
def length; @data.length; end
|
23
|
+
def last; @data.last; end
|
24
|
+
end
|
25
|
+
end
|
data/lib/ygg/record.rb
ADDED
data/lib/ygg/session.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Ygg
|
2
|
+
class Session
|
3
|
+
def initialize(source = nil)
|
4
|
+
if source
|
5
|
+
@source = source
|
6
|
+
@records = DataBundle.new load
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def load(path = nil)
|
11
|
+
@source = path if path
|
12
|
+
begin
|
13
|
+
File.exists?(@source) ? YAML.load_file(@source) : []
|
14
|
+
rescue Psych::SyntaxError => syntax_error
|
15
|
+
raise InvalidResourceException,
|
16
|
+
"The resource on #{@source} is invalid"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def shutdown
|
21
|
+
File.open @source, "w" do |file|
|
22
|
+
file.write @records.to_yaml
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def source
|
27
|
+
raise Ygg::UnavailableResourceException, "No resource was setted." unless @source
|
28
|
+
@source
|
29
|
+
end
|
30
|
+
|
31
|
+
def source=(source); @source = source; end
|
32
|
+
def records; @records; end
|
33
|
+
|
34
|
+
def has_loaded_some?(of_class)
|
35
|
+
raise ArgumentError, "Not a class" unless of_class.is_a? Class
|
36
|
+
raise NoInteractionDoneException,
|
37
|
+
"No interaction done yet." unless records
|
38
|
+
records.each do |record|
|
39
|
+
return true if record.is_a? of_class
|
40
|
+
end
|
41
|
+
return false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/ygg/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module Ygg
|
2
|
-
VERSION = "
|
3
|
-
end
|
1
|
+
module Ygg
|
2
|
+
VERSION = "1.0.6"
|
3
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require "ygg"
|
2
|
+
|
3
|
+
module YggDemo
|
4
|
+
class User
|
5
|
+
attr_accessor :name
|
6
|
+
end
|
7
|
+
|
8
|
+
class Account
|
9
|
+
attr_accessor :currency, :amount
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def base_path
|
14
|
+
File.dirname(File.dirname(__FILE__))
|
15
|
+
end
|
16
|
+
def folder_path
|
17
|
+
"emulations/samples"
|
18
|
+
end
|
19
|
+
def full_base_path
|
20
|
+
"#{base_path}/#{folder_path}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def non_existent_file_identifier; :non_existent; end
|
24
|
+
def non_existent_file_path
|
25
|
+
"#{folder_path}/#{non_existent_file_identifier}.ygg"
|
26
|
+
end
|
27
|
+
def non_existent_full_path
|
28
|
+
"#{base_path}/#{non_existent_file_path}"
|
29
|
+
end
|
30
|
+
def valid_file_identifier; :valid; end
|
31
|
+
def valid_file_path; "#{valid_file_identifier}.ygg"; end
|
32
|
+
def valid_full_path; "#{full_base_path}/#{valid_file_path}"; end
|
33
|
+
def to_create_file_identifier; :to_create; end
|
34
|
+
def to_create_file_path; "#{to_create_file_identifier}.ygg"; end
|
35
|
+
def to_create_full_path; "#{full_base_path}/#{to_create_file_path}"; end
|
36
|
+
|
37
|
+
def to_modify_file_identifier; :to_modify; end
|
38
|
+
def to_modify_file_path; "#{to_modify_file_identifier}.ygg"; end
|
39
|
+
def to_modify_full_path
|
40
|
+
"#{full_base_path}/#{to_modify_file_path}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_modify_2_file_identifier; :to_modify_2; end
|
44
|
+
def to_modify_2_file_path; "#{to_modify_2_file_identifier}.ygg"; end
|
45
|
+
def to_modify_2_full_path
|
46
|
+
"#{full_base_path}/#{to_modify_2_file_path}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def invalid_resource_file_identifier; :invalid_resource; end
|
50
|
+
def invalid_resource_file_path; "#{invalid_resource_file_identifier}.ygg"; end
|
51
|
+
def invalid_resource_full_path
|
52
|
+
"#{full_base_path}/#{invalid_resource_file_path}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def user_file_identifier; :user; end
|
56
|
+
def user_file_path; "#{user_file_identifier}.ygg"; end
|
57
|
+
def user_full_path; "#{full_base_path}/#{user_file_path}"; end
|
58
|
+
|
59
|
+
def the_last_object_of(resource_path)
|
60
|
+
YAML.load_file(resource_path).last
|
61
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Ygg::DataBundle do
|
4
|
+
describe "#of_type" do
|
5
|
+
context "no data" do
|
6
|
+
it "should return nil" do
|
7
|
+
the_databundle = Ygg::DataBundle.new
|
8
|
+
the_databundle.of_type(Object).should be_nil
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "data but nothing of that type" do
|
13
|
+
it "should return nil" do
|
14
|
+
the_databundle = Ygg::DataBundle.new ['element']
|
15
|
+
the_databundle.of_type(Array).should be_nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "one object of that kind" do
|
20
|
+
it "should return a databundle with the object" do
|
21
|
+
the_databundle = Ygg::DataBundle.new ['element', 4]
|
22
|
+
the_databundle.of_type(String).should include('element')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#which_are" do
|
28
|
+
pending # return "#is_a?"
|
29
|
+
end
|
30
|
+
|
31
|
+
context "Non empty inner array" do
|
32
|
+
before :each do
|
33
|
+
@the_databundle = Ygg::DataBundle.new ['element']
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#first"
|
37
|
+
describe "#empty?"
|
38
|
+
describe "#<<"
|
39
|
+
describe "#to_yaml"
|
40
|
+
describe "#include?"
|
41
|
+
end
|
42
|
+
|
43
|
+
context "Empty inner array" do
|
44
|
+
before :each do
|
45
|
+
@the_databundle = Ygg::DataBundle
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#first"
|
49
|
+
describe "#empty?"
|
50
|
+
describe "#<<"
|
51
|
+
describe "#to_yaml"
|
52
|
+
describe "#include?"
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Ygg::Session do
|
4
|
+
def should_be_invalid(resource)
|
5
|
+
File.should exist(resource)
|
6
|
+
expect { YAML.load_file resource
|
7
|
+
}.to raise_error( Psych::SyntaxError )
|
8
|
+
end
|
9
|
+
|
10
|
+
def it_should_not_include_a(type, file_path)
|
11
|
+
data = YAML.load_file(file_path)
|
12
|
+
data.each do |record|
|
13
|
+
raise "It includes a #{type}" if record.is_a? type
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def it_should_include_a(type, file_path)
|
18
|
+
data = YAML.load_file(file_path)
|
19
|
+
data.each do |record|
|
20
|
+
return if record.is_a? type
|
21
|
+
end
|
22
|
+
raise "It does not includes a #{type}"
|
23
|
+
end
|
24
|
+
|
25
|
+
before :each do
|
26
|
+
@path = File.dirname(File.dirname(File.dirname(__FILE__)))
|
27
|
+
should_be_invalid invalid_resource_full_path
|
28
|
+
end
|
29
|
+
|
30
|
+
describe ".new" do
|
31
|
+
it "should return a new session" do
|
32
|
+
@session = Ygg::Session.new "#{@path}/emulations/samples/user.ygg"
|
33
|
+
end
|
34
|
+
|
35
|
+
context "an invalid resource is passed" do
|
36
|
+
it "should fail" do
|
37
|
+
expect { Ygg::Session.new invalid_resource_full_path
|
38
|
+
}.to raise_error(
|
39
|
+
Ygg::InvalidResourceException,
|
40
|
+
"The resource on #{invalid_resource_full_path} is invalid" )
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#records" do
|
46
|
+
context "the file has the user Xavier Via" do
|
47
|
+
before :each do
|
48
|
+
@session = Ygg::Session.new "#{@path}/emulations/samples/user.ygg"
|
49
|
+
@the_record = @session.records.first
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not be empty the session data" do
|
53
|
+
@session.records.should_not be_empty
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should be a record of type User" do
|
57
|
+
@the_record.should be_a(YggDemo::User)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should be named Xavier Via" do
|
61
|
+
@the_record.name.should == "Xavier Via"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "the file is empty" do
|
66
|
+
it "should be empty the session data" do
|
67
|
+
@session = Ygg::Session.new "#{@path}/emulations/samples/random.ygg"
|
68
|
+
@session.records.should be_empty
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#shutdown" do
|
74
|
+
before :each do
|
75
|
+
@source = "#{@path}/emulations/samples/random.ygg"
|
76
|
+
@session = Ygg::Session.new @source
|
77
|
+
end
|
78
|
+
|
79
|
+
context "a string has been added to the data" do
|
80
|
+
before :each do
|
81
|
+
@session.records << "Hello data!"
|
82
|
+
@session.shutdown
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should create a file after the source" do
|
86
|
+
File.should exist(@source)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should include the string in the file" do
|
90
|
+
@content = File.read @source
|
91
|
+
@content.should include("Hello data!")
|
92
|
+
end
|
93
|
+
|
94
|
+
after :each do
|
95
|
+
File.unlink @source
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#source" do
|
101
|
+
it "should fail if no source specified" do
|
102
|
+
session = Ygg::Session.new
|
103
|
+
expect { session.source
|
104
|
+
}.to raise_error( Ygg::UnavailableResourceException,
|
105
|
+
"No resource was setted." )
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should return something" do
|
109
|
+
pending "dunno how to define what must return"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#source=" do
|
114
|
+
it "should setup the source" do
|
115
|
+
session = Ygg::Session.new
|
116
|
+
session.source = valid_full_path
|
117
|
+
session.source.should == valid_full_path
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should change the previous source" do
|
121
|
+
session = Ygg::Session.new
|
122
|
+
session.source = valid_full_path
|
123
|
+
expect { session.source = to_modify_full_path
|
124
|
+
}.to change { session.source
|
125
|
+
}.from( valid_full_path ).to( to_modify_full_path )
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "#load" do
|
130
|
+
context "if no argument is passed" do
|
131
|
+
it "should fail if the argument is not a valid YAML stream" do
|
132
|
+
session = Ygg::Session.new
|
133
|
+
session.source = invalid_resource_full_path
|
134
|
+
expect { session.load
|
135
|
+
}.to raise_error(
|
136
|
+
Ygg::InvalidResourceException,
|
137
|
+
"The resource on #{invalid_resource_full_path} is invalid" )
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "an argument is passed" do
|
142
|
+
it "should override @source" do
|
143
|
+
session = Ygg::Session.new valid_full_path
|
144
|
+
expect { session.load to_modify_full_path
|
145
|
+
}.to change { session.source
|
146
|
+
}.from(valid_full_path).to(to_modify_full_path)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should fail if the argument is not a valid YAML stream" do
|
150
|
+
session = Ygg::Session.new
|
151
|
+
expect { session.load invalid_resource_full_path
|
152
|
+
}.to raise_error(
|
153
|
+
Ygg::InvalidResourceException,
|
154
|
+
"The resource on #{invalid_resource_full_path} is invalid" )
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "the source is a non existent file" do
|
159
|
+
it "should return an empty array" do
|
160
|
+
File.should_not exist( non_existent_full_path )
|
161
|
+
session = Ygg::Session.new non_existent_full_path
|
162
|
+
session.load.should be_empty
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context "the source is a valid YAML stream" do
|
167
|
+
it "should return the object from the last document" do
|
168
|
+
the_last_object_of(user_full_path).should be_a(YggDemo::Account)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "#has_loaded_some?" do
|
174
|
+
it "should fail on no argument" do
|
175
|
+
expect { Ygg::Session.new.has_loaded_some?
|
176
|
+
}.to raise_error( ArgumentError )
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should fail if argument is not a class" do
|
180
|
+
expect { Ygg::Session.new.has_loaded_some? (Object.new)
|
181
|
+
}.to raise_error( ArgumentError, "Not a class" )
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should fail if no loading has been done" do
|
185
|
+
expect { Ygg::Session.new.has_loaded_some? (Object)
|
186
|
+
}.to raise_error(
|
187
|
+
Ygg::NoInteractionDoneException,
|
188
|
+
"No interaction done yet." )
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should return false if there is no object of that type" do
|
192
|
+
it_should_not_include_a Array, user_full_path
|
193
|
+
session = Ygg::Session.new user_full_path
|
194
|
+
session.should_not have_loaded_some(Array)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should return true if there is some object of that type" do
|
198
|
+
it_should_include_a YggDemo::User, user_full_path
|
199
|
+
session = Ygg::Session.new user_full_path
|
200
|
+
session.should have_loaded_some(YggDemo::User)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|