JamieFlournoy-machinist 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/.autotest +7 -0
- data/.gitignore +4 -0
- data/FAQ.markdown +18 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +319 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/init.rb +2 -0
- data/lib/machinist.rb +121 -0
- data/lib/machinist/active_record.rb +99 -0
- data/lib/machinist/blueprints.rb +25 -0
- data/lib/machinist/data_mapper.rb +83 -0
- data/lib/machinist/object.rb +30 -0
- data/lib/machinist/sequel.rb +62 -0
- data/lib/sham.rb +81 -0
- data/machinist.gemspec +90 -0
- data/spec/active_record_spec.rb +200 -0
- data/spec/data_mapper_spec.rb +136 -0
- data/spec/db/.gitignore +1 -0
- data/spec/db/schema.rb +20 -0
- data/spec/log/.gitignore +1 -0
- data/spec/machinist_spec.rb +208 -0
- data/spec/sequel_spec.rb +151 -0
- data/spec/sham_spec.rb +95 -0
- data/spec/spec_helper.rb +9 -0
- metadata +177 -0
@@ -0,0 +1,200 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'machinist/active_record'
|
3
|
+
require 'active_support/whiny_nil'
|
4
|
+
|
5
|
+
module MachinistActiveRecordSpecs
|
6
|
+
|
7
|
+
class Person < ActiveRecord::Base
|
8
|
+
attr_protected :password
|
9
|
+
end
|
10
|
+
|
11
|
+
class Admin < Person
|
12
|
+
end
|
13
|
+
|
14
|
+
class Post < ActiveRecord::Base
|
15
|
+
has_many :comments
|
16
|
+
end
|
17
|
+
|
18
|
+
class Comment < ActiveRecord::Base
|
19
|
+
belongs_to :post
|
20
|
+
belongs_to :author, :class_name => "Person"
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Machinist, "ActiveRecord adapter" do
|
24
|
+
before(:suite) do
|
25
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/log/test.log")
|
26
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
27
|
+
load(File.dirname(__FILE__) + "/db/schema.rb")
|
28
|
+
end
|
29
|
+
|
30
|
+
before(:each) do
|
31
|
+
[Person, Admin, Post, Comment].each(&:clear_blueprints!)
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "make method" do
|
35
|
+
it "should support single-table inheritance" do
|
36
|
+
Person.blueprint { }
|
37
|
+
Admin.blueprint { }
|
38
|
+
admin = Admin.make
|
39
|
+
admin.should_not be_new_record
|
40
|
+
admin.type.should == "Admin"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should save the constructed object" do
|
44
|
+
Person.blueprint { }
|
45
|
+
person = Person.make
|
46
|
+
person.should_not be_new_record
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should create an object through belongs_to association" do
|
50
|
+
Post.blueprint { }
|
51
|
+
Comment.blueprint { post }
|
52
|
+
Comment.make.post.class.should == Post
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should create an object through belongs_to association with a class_name attribute" do
|
56
|
+
Person.blueprint { }
|
57
|
+
Comment.blueprint { author }
|
58
|
+
Comment.make.author.class.should == Person
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should create an object through belongs_to association using a named blueprint" do
|
62
|
+
Post.blueprint { }
|
63
|
+
Post.blueprint(:dummy) do
|
64
|
+
title { 'Dummy Post' }
|
65
|
+
end
|
66
|
+
Comment.blueprint { post(:dummy) }
|
67
|
+
Comment.make.post.title.should == 'Dummy Post'
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should allow creating an object through a has_many association" do
|
71
|
+
Post.blueprint do
|
72
|
+
comments { [Comment.make] }
|
73
|
+
end
|
74
|
+
Comment.blueprint { }
|
75
|
+
Post.make.comments.should have(1).instance_of(Comment)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should allow setting a protected attribute in the blueprint" do
|
79
|
+
Person.blueprint do
|
80
|
+
password { "Test" }
|
81
|
+
end
|
82
|
+
Person.make.password.should == "Test"
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should allow overriding a protected attribute" do
|
86
|
+
Person.blueprint do
|
87
|
+
password { "Test" }
|
88
|
+
end
|
89
|
+
Person.make(:password => "New").password.should == "New"
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should allow setting the id attribute in a blueprint" do
|
93
|
+
Person.blueprint do
|
94
|
+
id { 12345 }
|
95
|
+
end
|
96
|
+
Person.make.id.should == 12345
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should allow setting the type attribute in a blueprint" do
|
100
|
+
Person.blueprint do
|
101
|
+
type { "Person" }
|
102
|
+
end
|
103
|
+
Person.make.type.should == "Person"
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "on a has_many association" do
|
107
|
+
before do
|
108
|
+
Post.blueprint { }
|
109
|
+
Comment.blueprint { post }
|
110
|
+
@post = Post.make
|
111
|
+
@comment = @post.comments.make
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should save the created object" do
|
115
|
+
@comment.should_not be_new_record
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should set the parent association on the created object" do
|
119
|
+
@comment.post.should == @post
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "plan method" do
|
125
|
+
it "should not save the constructed object" do
|
126
|
+
person_count = Person.count
|
127
|
+
Person.blueprint { }
|
128
|
+
person = Person.plan
|
129
|
+
Person.count.should == person_count
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should create an object through a belongs_to association, and return its id" do
|
133
|
+
Post.blueprint { }
|
134
|
+
Comment.blueprint { post }
|
135
|
+
post_count = Post.count
|
136
|
+
comment = Comment.plan
|
137
|
+
Post.count.should == post_count + 1
|
138
|
+
comment[:post].should be_nil
|
139
|
+
comment[:post_id].should_not be_nil
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "on a belongs_to association" do
|
143
|
+
it "should allow explicitly setting the association to nil" do
|
144
|
+
Comment.blueprint { post }
|
145
|
+
Comment.blueprint(:no_post) { post { nil } }
|
146
|
+
lambda {
|
147
|
+
@comment = Comment.plan(:no_post)
|
148
|
+
}.should_not raise_error
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "on a has_many association" do
|
153
|
+
before do
|
154
|
+
Post.blueprint { }
|
155
|
+
Comment.blueprint do
|
156
|
+
post
|
157
|
+
body { "Test" }
|
158
|
+
end
|
159
|
+
@post = Post.make
|
160
|
+
@post_count = Post.count
|
161
|
+
@comment = @post.comments.plan
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should not include the parent in the returned hash" do
|
165
|
+
@comment[:post].should be_nil
|
166
|
+
@comment[:post_id].should be_nil
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should not create an extra parent object" do
|
170
|
+
Post.count.should == @post_count
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "make_unsaved method" do
|
176
|
+
it "should not save the constructed object" do
|
177
|
+
Person.blueprint { }
|
178
|
+
person = Person.make_unsaved
|
179
|
+
person.should be_new_record
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should not save associated objects" do
|
183
|
+
Post.blueprint { }
|
184
|
+
Comment.blueprint { post }
|
185
|
+
comment = Comment.make_unsaved
|
186
|
+
comment.post.should be_new_record
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should save objects made within a passed-in block" do
|
190
|
+
Post.blueprint { }
|
191
|
+
Comment.blueprint { }
|
192
|
+
comment = nil
|
193
|
+
post = Post.make_unsaved { comment = Comment.make }
|
194
|
+
post.should be_new_record
|
195
|
+
comment.should_not be_new_record
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'machinist/data_mapper'
|
3
|
+
require 'dm-validations'
|
4
|
+
|
5
|
+
module MachinistDataMapperSpecs
|
6
|
+
|
7
|
+
class Person
|
8
|
+
include DataMapper::Resource
|
9
|
+
property :id, Serial
|
10
|
+
property :name, String, :length => (0..10)
|
11
|
+
property :type, Discriminator
|
12
|
+
property :password, String
|
13
|
+
property :admin, Boolean, :default => false
|
14
|
+
end
|
15
|
+
|
16
|
+
class Admin < Person
|
17
|
+
end
|
18
|
+
|
19
|
+
class Post
|
20
|
+
include DataMapper::Resource
|
21
|
+
property :id, Serial
|
22
|
+
property :title, String
|
23
|
+
property :body, Text
|
24
|
+
property :published, Boolean, :default => true
|
25
|
+
has n, :comments
|
26
|
+
end
|
27
|
+
|
28
|
+
class Comment
|
29
|
+
include DataMapper::Resource
|
30
|
+
property :id, Serial
|
31
|
+
property :post_id, Integer
|
32
|
+
property :author_id, Integer
|
33
|
+
belongs_to :post
|
34
|
+
belongs_to :author, :model => "Person", :child_key => [:author_id]
|
35
|
+
end
|
36
|
+
|
37
|
+
describe Machinist, "DataMapper adapter" do
|
38
|
+
before(:suite) do
|
39
|
+
DataMapper::Logger.new(File.dirname(__FILE__) + "/log/test.log", :debug)
|
40
|
+
DataMapper.setup(:default, "sqlite3::memory:")
|
41
|
+
DataMapper.auto_migrate!
|
42
|
+
end
|
43
|
+
|
44
|
+
before(:each) do
|
45
|
+
[Person, Admin, Post, Comment].each(&:clear_blueprints!)
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "make method" do
|
49
|
+
it "should support inheritance" do
|
50
|
+
Person.blueprint {}
|
51
|
+
Admin.blueprint {}
|
52
|
+
|
53
|
+
admin = Admin.make
|
54
|
+
admin.should_not be_new
|
55
|
+
admin.type.should_not be_nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should save the constructed object" do
|
59
|
+
Person.blueprint { }
|
60
|
+
person = Person.make
|
61
|
+
person.should_not be_new
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should create an object through a belongs_to association" do
|
65
|
+
Post.blueprint { }
|
66
|
+
Comment.blueprint { post }
|
67
|
+
Comment.make.post.class.should == Post
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
it "should create an object through a belongs_to association with a class_name attribute" do
|
72
|
+
Person.blueprint { }
|
73
|
+
Comment.blueprint { author }
|
74
|
+
Comment.make.author.class.should == Person
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should raise an exception if the object can't be saved" do
|
78
|
+
Person.blueprint { }
|
79
|
+
lambda { Person.make(:name => "More than ten characters") }.should raise_error(RuntimeError)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "plan method" do
|
84
|
+
it "should not save the constructed object" do
|
85
|
+
person_count = Person.all.length
|
86
|
+
Person.blueprint { }
|
87
|
+
person = Person.plan
|
88
|
+
Person.all.length.should == person_count
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should return a regular attribute in the hash" do
|
92
|
+
Post.blueprint do
|
93
|
+
title { "Test" }
|
94
|
+
end
|
95
|
+
post = Post.plan
|
96
|
+
post[:title].should == "Test"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should create an object through a belongs_to association, and return its id" do
|
100
|
+
Post.blueprint { }
|
101
|
+
Comment.blueprint { post }
|
102
|
+
post_count = Post.all.length
|
103
|
+
comment = Comment.plan
|
104
|
+
Post.all.length.should == post_count + 1
|
105
|
+
comment[:post].should be_nil
|
106
|
+
comment[:post_id].should_not be_nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "make_unsaved method" do
|
111
|
+
it "should not save the constructed object" do
|
112
|
+
Person.blueprint { }
|
113
|
+
person = Person.make_unsaved
|
114
|
+
person.should be_new
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should not save associated objects" do
|
118
|
+
Post.blueprint { }
|
119
|
+
Comment.blueprint { post }
|
120
|
+
comment = Comment.make_unsaved
|
121
|
+
comment.post.should be_new
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should save objects made within a passed-in block" do
|
125
|
+
Post.blueprint { }
|
126
|
+
Comment.blueprint { }
|
127
|
+
comment = nil
|
128
|
+
post = Post.make_unsaved { comment = Comment.make }
|
129
|
+
post.should be_new
|
130
|
+
comment.should_not be_new
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
data/spec/db/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.sqlite3
|
data/spec/db/schema.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
create_table :people, :force => true do |t|
|
3
|
+
t.column :name, :string
|
4
|
+
t.column :type, :string
|
5
|
+
t.column :password, :string
|
6
|
+
t.column :admin, :boolean, :default => false
|
7
|
+
end
|
8
|
+
|
9
|
+
create_table :posts, :force => true do |t|
|
10
|
+
t.column :title, :string
|
11
|
+
t.column :body, :text
|
12
|
+
t.column :published, :boolean, :default => true
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :comments, :force => true do |t|
|
16
|
+
t.column :post_id, :integer
|
17
|
+
t.column :author_id, :integer
|
18
|
+
t.column :body, :text
|
19
|
+
end
|
20
|
+
end
|
data/spec/log/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.log
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'machinist/object'
|
3
|
+
|
4
|
+
module MachinistSpecs
|
5
|
+
|
6
|
+
class Person
|
7
|
+
attr_accessor :name, :admin
|
8
|
+
end
|
9
|
+
|
10
|
+
class Post
|
11
|
+
attr_accessor :title, :body, :published
|
12
|
+
end
|
13
|
+
|
14
|
+
class Grandpa
|
15
|
+
attr_accessor :name
|
16
|
+
end
|
17
|
+
|
18
|
+
class Dad < Grandpa
|
19
|
+
attr_accessor :name
|
20
|
+
end
|
21
|
+
|
22
|
+
class Son < Dad
|
23
|
+
attr_accessor :name
|
24
|
+
end
|
25
|
+
|
26
|
+
describe Machinist do
|
27
|
+
before(:each) do
|
28
|
+
[Person, Post, Grandpa, Dad, Son].each(&:clear_blueprints!)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should raise for make on a class with no blueprint" do
|
32
|
+
lambda { Person.make }.should raise_error(RuntimeError, "No blueprint for class MachinistSpecs::Person")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should set an attribute on the constructed object from a constant in the blueprint" do
|
36
|
+
Person.blueprint do
|
37
|
+
name "Fred"
|
38
|
+
end
|
39
|
+
Person.make.name.should == "Fred"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should set an attribute on the constructed object from a block in the blueprint" do
|
43
|
+
Person.blueprint do
|
44
|
+
name { "Fred" }
|
45
|
+
end
|
46
|
+
Person.make.name.should == "Fred"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should default to calling Sham for an attribute in the blueprint" do
|
50
|
+
Sham.clear
|
51
|
+
Sham.name { "Fred" }
|
52
|
+
Person.blueprint { name }
|
53
|
+
Person.make.name.should == "Fred"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should let the blueprint override an attribute with a default value" do
|
57
|
+
Post.blueprint do
|
58
|
+
published { false }
|
59
|
+
end
|
60
|
+
Post.make.published.should be_false
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should allow overridden attribute names to be strings" do
|
64
|
+
Person.blueprint do
|
65
|
+
name { "Fred" }
|
66
|
+
end
|
67
|
+
Person.make("name" => "Bill").name.should == "Bill"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should override an attribute from the blueprint with a passed-in attribute" do
|
71
|
+
block_called = false
|
72
|
+
Person.blueprint do
|
73
|
+
name { block_called = true; "Fred" }
|
74
|
+
end
|
75
|
+
Person.make(:name => "Bill").name.should == "Bill"
|
76
|
+
block_called.should be_false
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should call a passed-in block with the object being constructed" do
|
80
|
+
Person.blueprint { }
|
81
|
+
block_called = false
|
82
|
+
Person.make do |person|
|
83
|
+
block_called = true
|
84
|
+
person.class.should == Person
|
85
|
+
end
|
86
|
+
block_called.should be_true
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should provide access to the object being constructed from within the blueprint" do
|
90
|
+
person = nil
|
91
|
+
Person.blueprint { person = object }
|
92
|
+
Person.make
|
93
|
+
person.class.should == Person
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should allow reading of a previously assigned attribute from within the blueprint" do
|
97
|
+
Post.blueprint do
|
98
|
+
title { "Test" }
|
99
|
+
body { title }
|
100
|
+
end
|
101
|
+
Post.make.body.should == "Test"
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "named blueprints" do
|
105
|
+
before do
|
106
|
+
@block_called = false
|
107
|
+
Person.blueprint do
|
108
|
+
name { "Fred" }
|
109
|
+
admin { @block_called = true; false }
|
110
|
+
end
|
111
|
+
Person.blueprint(:admin) do
|
112
|
+
admin { true }
|
113
|
+
end
|
114
|
+
@person = Person.make(:admin)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should override an attribute from the parent blueprint in the child blueprint" do
|
118
|
+
@person.admin.should == true
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should not call the block for an attribute from the parent blueprint if that attribute is overridden in the child" do
|
122
|
+
@block_called.should be_false
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should set an attribute defined in the parent blueprint" do
|
126
|
+
@person.name.should == "Fred"
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should return the correct list of named blueprints" do
|
130
|
+
Person.blueprint(:foo) { }
|
131
|
+
Person.blueprint(:bar) { }
|
132
|
+
Person.named_blueprints.to_set.should == [:admin, :foo, :bar].to_set
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should raise exception for make if named blueprint is not defined" do
|
136
|
+
lambda { Person.make(:bogus) }.should raise_error(
|
137
|
+
RuntimeError, "No blueprint named 'bogus' defined for class MachinistSpecs::Person"
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should raise a sensible error when calling a named blueprint with no master" do
|
142
|
+
Post.blueprint(:foo) { }
|
143
|
+
lambda { Post.make(:foo) }.should raise_error(
|
144
|
+
RuntimeError, "Can't construct an object from a named blueprint without a default blueprint for class MachinistSpecs::Post"
|
145
|
+
)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "blueprint inheritance" do
|
150
|
+
it "should inherit blueprinted attributes from the parent class" do
|
151
|
+
Dad.blueprint do
|
152
|
+
name { "Fred" }
|
153
|
+
end
|
154
|
+
Son.blueprint { }
|
155
|
+
Son.make.name.should == "Fred"
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should override blueprinted attributes in the child class" do
|
159
|
+
Dad.blueprint do
|
160
|
+
name { "Fred" }
|
161
|
+
end
|
162
|
+
Son.blueprint do
|
163
|
+
name { "George" }
|
164
|
+
end
|
165
|
+
Dad.make.name.should == "Fred"
|
166
|
+
Son.make.name.should == "George"
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should inherit from blueprinted attributes in ancestor class" do
|
170
|
+
Grandpa.blueprint do
|
171
|
+
name { "Fred" }
|
172
|
+
end
|
173
|
+
Son.blueprint { }
|
174
|
+
Grandpa.make.name.should == "Fred"
|
175
|
+
lambda { Dad.make }.should raise_error(RuntimeError)
|
176
|
+
Son.make.name.should == "Fred"
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should follow inheritance for named blueprints correctly" do
|
180
|
+
Dad.blueprint do
|
181
|
+
name { "John" }
|
182
|
+
end
|
183
|
+
Dad.blueprint(:special) do
|
184
|
+
name { "Paul" }
|
185
|
+
end
|
186
|
+
Son.blueprint { }
|
187
|
+
Son.blueprint(:special) { }
|
188
|
+
Son.make(:special).name.should == "John"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "clear_blueprints! method" do
|
193
|
+
it "should clear the list of blueprints" do
|
194
|
+
Person.blueprint(:foo){}
|
195
|
+
Person.clear_blueprints!
|
196
|
+
Person.named_blueprints.should == []
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should clear master blueprint too" do
|
200
|
+
Person.blueprint(:foo) {}
|
201
|
+
Person.blueprint {} # master
|
202
|
+
Person.clear_blueprints!
|
203
|
+
lambda { Person.make }.should raise_error(RuntimeError)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|