ribs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Binary file
Binary file
@@ -0,0 +1,94 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class Artist
4
+ Ribs!
5
+ end
6
+
7
+ describe Artist do
8
+ it "should be able to find all artists" do
9
+ Artist.find(:all).length.should == 3
10
+ end
11
+
12
+ it "should return correct value and type for id property" do
13
+ Artist.find(:all).map { |a| a.id }.sort.should == [1,2,3]
14
+ end
15
+
16
+ it "should return correct value and type for name property" do
17
+ Artist.find(:all).map { |a| a.name }.sort.should == ["David Bowie","New Model Army","Public Image Ltd"]
18
+ end
19
+
20
+ it "should only have the appropriate methods defined" do
21
+ methods = (Artist.instance_methods - Object.instance_methods).sort
22
+ methods.should == ['__ribs_meat', 'destroy!', 'id=', 'name', 'name=', 'save']
23
+ end
24
+
25
+ it "should be possible to create a new instance by setting properties" do
26
+ begin
27
+ artist = Artist.new
28
+ artist.id = 4
29
+ artist.name = "Assemblage 23"
30
+ artist.save
31
+ artist2 = Artist.find(4)
32
+ artist2.should_not be_nil
33
+ artist2.id.should == 4
34
+ artist2.name.should == "Assemblage 23"
35
+ ensure
36
+ reset_database!
37
+ end
38
+ end
39
+
40
+ it "should be possible to create a new instance by giving properties to new" do
41
+ begin
42
+ artist = Artist.new :id => 4, :name => "Assemblage 23"
43
+ artist.save
44
+ artist2 = Artist.find(4)
45
+ artist2.should_not be_nil
46
+ artist2.id.should == 4
47
+ artist2.name.should == "Assemblage 23"
48
+ ensure
49
+ reset_database!
50
+ end
51
+ end
52
+
53
+ it "should be possible to create a new instance by using create" do
54
+ begin
55
+ Artist.create :id => 4, :name => "Assemblage 23"
56
+ artist = Artist.find(4)
57
+ artist.should_not be_nil
58
+ artist.id.should == 4
59
+ artist.name.should == "Assemblage 23"
60
+ ensure
61
+ reset_database!
62
+ end
63
+ end
64
+
65
+ it "should be possible to update name property on existing bean" do
66
+ begin
67
+ artist = Artist.find(2)
68
+ artist.name = "U2"
69
+ Artist.find(2).name.should == "New Model Army"
70
+ artist.save
71
+ Artist.find(2).name.should == "U2"
72
+ ensure
73
+ reset_database!
74
+ end
75
+ end
76
+
77
+ it "should be possible to delete existing bean" do
78
+ begin
79
+ Artist.find(2).destroy!
80
+ Artist.find(2).should be_nil
81
+ ensure
82
+ reset_database!
83
+ end
84
+ end
85
+
86
+ it "should be possible to delete existing bean by id" do
87
+ begin
88
+ Artist.destroy(2)
89
+ Artist.find(2).should be_nil
90
+ ensure
91
+ reset_database!
92
+ end
93
+ end
94
+ end
data/test/find_spec.rb ADDED
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
@@ -0,0 +1,79 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe "Simple select" do
4
+ it "should return correct data for ints" do
5
+ Ribs.with_session do |s|
6
+ s.select("SELECT TRACK_ID, volume FROM DB_TRACK").should == [[1, 13], [2, 13]]
7
+ end
8
+ end
9
+
10
+ it "should return correct data for strings" do
11
+ Ribs.with_session do |s|
12
+ s.select("SELECT title, filePath FROM DB_TRACK").should == [["foobar", "c:/abc/cde/foo.mp3"], ["flux", "d:/abc/cde/flax.mp3"]]
13
+ end
14
+ end
15
+
16
+ it "should return correct data for times" do
17
+ Ribs.with_session do |s|
18
+ s.select("SELECT playTime FROM DB_TRACK").should == [[Time.time_at(14,50,0)], [Time.time_at(16,23,0)]]
19
+ end
20
+ end
21
+
22
+ it "should return correct data for dates" do
23
+ Ribs.with_session do |s|
24
+ s.select("SELECT added FROM DB_TRACK").should == [[Time.local(1984, 12, 13, 0, 0, 0)], [Time.local(1983, 12, 13, 0, 0, 0)]]
25
+ end
26
+ end
27
+
28
+ it "should return correct data for timestamp" do
29
+ Ribs.with_session do |s|
30
+ s.select("SELECT lastPlayed FROM DB_TRACK").should == [[Time.local(1984, 12, 14, 12,3,11)], [Time.local(1982, 5, 3, 13,3,7)]]
31
+ end
32
+ end
33
+
34
+ it "should return correct data for floats" do
35
+ result = Ribs.with_session do |s|
36
+ s.select("SELECT fraction FROM DB_TRACK")
37
+ end
38
+ result[0].length.should == 1
39
+ result[0][0].should be_close(3.4, 0.00001)
40
+ result[1].length.should == 1
41
+ result[1][0].should be_close(3.5, 0.00001)
42
+
43
+ end
44
+
45
+ it "should return correct data for doubles" do
46
+ result = Ribs.with_session do |s|
47
+ s.select("SELECT otherFraction FROM DB_TRACK")
48
+ end
49
+ result[0].length.should == 1
50
+ result[0][0].should be_close(5.7, 0.00001)
51
+ result[1].length.should == 1
52
+ result[1][0].should be_close(35435.4522234, 0.01)
53
+ end
54
+
55
+ it "should return correct data for blobs" do
56
+ Ribs.with_session do |s|
57
+ s.select("SELECT data FROM DB_TRACK").should == [["abc"], ["mumsi"]]
58
+ end
59
+ end
60
+
61
+ it "should return correct data for clobs" do
62
+ Ribs.with_session do |s|
63
+ s.select("SELECT description FROM DB_TRACK").should == [["foobar"], ["maxi"]]
64
+ end
65
+ end
66
+
67
+ it "should return correct data for booleans" do
68
+ Ribs.with_session do |s|
69
+ # Not strictly correct - an artifact of a lack in Derby
70
+ s.select("SELECT good FROM DB_TRACK").should == [[1], [0]]
71
+ end
72
+ end
73
+
74
+ it "should return correct data for decimal" do
75
+ Ribs.with_session do |s|
76
+ s.select("SELECT price FROM DB_TRACK").should == [[BigDecimal.new("13134.11")], [BigDecimal.new("55454.33")]]
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,83 @@
1
+ require 'java'
2
+ require 'rubygems'
3
+ require 'spec'
4
+ require 'bigdecimal'
5
+ require 'ribs'
6
+
7
+ class Time
8
+ def self.time_at(hrs, min, sec)
9
+ Time.local(1970, 1, 1, hrs, min, sec)
10
+ end
11
+
12
+ def self.date_at(year, month, day)
13
+ Time.local(year, month, day, 0, 0, 0)
14
+ end
15
+ end
16
+
17
+ Ribs::DB.define do |db|
18
+ # It's also possible to configure through JNDI here
19
+
20
+ db.dialect = 'Derby'
21
+ db.uri = 'jdbc:derby:test_database;create=true'
22
+ db.driver = 'org.apache.derby.jdbc.EmbeddedDriver'
23
+ # db.properties['hibernate.show_sql'] = 'true'
24
+ end
25
+
26
+ def reset_database!
27
+ Ribs.with_session do |s|
28
+ s.ddl "DROP TABLE DB_TRACK" rescue nil
29
+ s.ddl "DROP TABLE ARTIST" rescue nil
30
+
31
+ # GENERATED ALWAYS AS IDENTITY
32
+ # Add new columns for TIMESTAMP, BINARY, DECIMAL, FLOAT, BOOLEAN
33
+ s.ddl <<SQL
34
+ CREATE TABLE DB_TRACK (
35
+ TRACK_ID INT NOT NULL,
36
+ title VARCHAR(255) NOT NULL,
37
+ filePath VARCHAR(255),
38
+ playTime TIME,
39
+ added DATE,
40
+ volume INT NOT NULL,
41
+ lastPlayed TIMESTAMP,
42
+ data BLOB,
43
+ description CLOB,
44
+ fraction FLOAT,
45
+ otherFraction DOUBLE,
46
+ good SMALLINT,
47
+ price DECIMAL(10,2),
48
+ PRIMARY KEY (TRACK_ID)
49
+ )
50
+ SQL
51
+
52
+ s.ddl <<SQL
53
+ CREATE TABLE ARTIST (
54
+ ID INT NOT NULL,
55
+ name VARCHAR(255) NOT NULL,
56
+ PRIMARY KEY (ID)
57
+ )
58
+ SQL
59
+
60
+ template = <<SQL
61
+ INSERT INTO DB_TRACK(TRACK_ID, title, filePath, playTime, added, volume, lastPlayed, data, description, fraction, otherFraction, good, price) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
62
+ SQL
63
+
64
+ s.insert(template,
65
+ [1, "foobar", "c:/abc/cde/foo.mp3", [Time.time_at(14,50,0), :time], [Time.local(1984, 12, 13, 0,0,0), :date], 13,
66
+ [Time.local(1984, 12, 14, 12,3,11), :timestamp], ["abc", :binary], ["foobar", :text], 3.4, 5.7, true, BigDecimal.new("13134.11")],
67
+ [2, "flux", "d:/abc/cde/flax.mp3", [Time.time_at(16,23,0), :time], [Time.local(1983, 12, 13, 0,0,0), :date], 13,
68
+ [Time.local(1982, 5, 3, 13,3,7), :timestamp], ["mumsi", :binary], ["maxi", :text], 3.5, 35435.4522234, false, BigDecimal.new("55454.33")])
69
+
70
+ s.insert("INSERT INTO ARTIST(ID, name) VALUES(?, ?)",
71
+ [1, "Public Image Ltd"],
72
+ [2, "New Model Army"],
73
+ [3, "David Bowie"])
74
+ end
75
+ end
76
+
77
+ reset_database!
78
+
79
+ at_exit do
80
+ require 'fileutils'
81
+ FileUtils.rm_rf('test_database')
82
+ FileUtils.rm_rf('derby.log')
83
+ end
@@ -0,0 +1,415 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class Track
4
+ Ribs! do |rib|
5
+ rib.table :DB_TRACK
6
+
7
+ rib.primary_key :TRACK_ID
8
+
9
+ rib.col :title, :track_title
10
+ rib.col :playTime, :time
11
+ rib.col :added, :date_added
12
+ rib.col :lastPlayed, :last_played_at
13
+ rib.col :data, :file_data
14
+ rib.col :description, :desc
15
+ rib.col :fraction, :some_fraction
16
+ rib.col :good, :is_good
17
+ rib.col :price, :full_price
18
+
19
+ rib.avoid :filePath
20
+ end
21
+ end
22
+
23
+ describe Track do
24
+ it "should be able to find things based on mapped primary key" do
25
+ track = Track.find(2)
26
+ track.track_id.should == 2
27
+ track.track_title.should == "flux"
28
+ track.time.should == Time.time_at(16,23,0)
29
+ track.date_added.should == Time.local(1983, 12, 13, 0,0,0)
30
+ track.volume.should == 13
31
+ end
32
+
33
+ it "should have property that wasn't named" do
34
+ prop = Track.ribs_metadata['VOLUME']
35
+ prop.name.should == 'VOLUME'
36
+ colarr = prop.column_iterator.to_a
37
+ colarr.length.should == 1
38
+ colarr[0].name.should == 'VOLUME'
39
+ end
40
+
41
+ it "shouldn't have property that's avoided" do
42
+ Track.ribs_metadata['filePath'].should be_nil
43
+ end
44
+
45
+ it "should have correct names for defined properties" do
46
+ props = Track.ribs_metadata.properties
47
+ # the primary keys aren't actually part of the properties
48
+ props.keys.sort.should == ['OTHERFRACTION', 'VOLUME', 'track_title', 'time', 'date_added', 'file_data', 'desc', 'some_fraction', 'is_good', 'full_price', 'last_played_at'].sort
49
+ props.values.map { |p| p.column_iterator.to_a[0].name }.sort.should ==
50
+ ['VOLUME', 'TITLE', 'PLAYTIME', 'ADDED', 'OTHERFRACTION', 'LASTPLAYED', 'DATA', 'DESCRIPTION', 'FRACTION', 'GOOD', 'PRICE'].sort
51
+ end
52
+
53
+ it "should have correct value and type for OTHERFRACTION property" do
54
+ res = Track.find(:all).map { |a| a.otherfraction }.sort
55
+ res[0].should be_close(5.7, 0.00001)
56
+ res[1].should be_close(35435.4522234, 0.01)
57
+ end
58
+
59
+ it "should have correct value and type for VOLUME property" do
60
+ Track.find(:all).map { |a| a.volume }.sort.should == [13, 13]
61
+ end
62
+
63
+ it "should have correct value and type for track_title property" do
64
+ Track.find(:all).map { |a| a.track_title }.sort.should == ["flux", "foobar"]
65
+ end
66
+
67
+ it "should have correct value and type for time property" do
68
+ Track.find(:all).map { |a| a.time }.sort.should == [Time.time_at(14,50,0), Time.time_at(16,23,0)]
69
+ end
70
+
71
+ it "should have correct value and type for date_added property" do
72
+ Track.find(:all).map { |a| a.date_added }.sort.should == [Time.local(1983, 12, 13, 0, 0, 0),Time.local(1984, 12, 13, 0, 0, 0)]
73
+ end
74
+
75
+ it "should have correct value and type for file_data property" do
76
+ Track.find(:all).map { |a| a.file_data }.sort.should == ["abc", "mumsi"]
77
+ end
78
+
79
+ it "should have correct value and type for desc property" do
80
+ Track.find(:all).map { |a| a.desc }.sort.should == ["foobar", "maxi"]
81
+ end
82
+
83
+ it "should have correct value and type for some_fraction property" do
84
+ res = Track.find(:all).map { |a| a.some_fraction }.sort
85
+ res[0].should be_close(3.4, 0.00001)
86
+ res[1].should be_close(3.5, 0.00001)
87
+ end
88
+
89
+ it "should have correct value and type for is_good property" do
90
+ Track.find(:all).map { |a| a.is_good }.sort_by{|v| v ? 0 : 1}.should == [true, false]
91
+ end
92
+
93
+ it "should have correct value and type for full_price property" do
94
+ Track.find(:all).map { |a| a.full_price }.sort.should == [BigDecimal.new("13134.11"), BigDecimal.new("55454.33")]
95
+ end
96
+
97
+ it "should have correct value and type for last_played_at property" do
98
+ Track.find(:all).map { |a| a.last_played_at }.sort.should == [Time.local(1982, 5, 3, 13,3,7), Time.local(1984, 12, 14, 12,3,11)]
99
+ end
100
+
101
+ it "should be possible to create a new instance by setting properties" do
102
+ begin
103
+ track = Track.new
104
+ track.track_id = 3
105
+ track.track_title = "Born to raise hell"
106
+ track.time = Time.time_at(0,3,25)
107
+ track.date_added = Time.local(2003, 8, 31, 0, 0, 0)
108
+ track.last_played_at = Time.local(2008, 8, 31, 21, 41, 30)
109
+ track.file_data = "abc def"
110
+ track.desc = "This track I really don't know anything about, in fact"
111
+ track.some_fraction = 3.1415
112
+ track.is_good = false
113
+ track.full_price = BigDecimal.new("14.49")
114
+ track.volume = 5
115
+
116
+ Track.find(3).should be_nil
117
+
118
+ track.save
119
+
120
+ Track.find(3).should_not be_nil
121
+ ensure
122
+ reset_database!
123
+ end
124
+ end
125
+
126
+ it "should be possible to create a new instance by giving properties to new" do
127
+ begin
128
+ track = Track.new(
129
+ :track_id => 3,
130
+ :track_title => "Born to raise hell",
131
+ :time => Time.time_at(0,3,25),
132
+ :date_added => Time.local(2003, 8, 31, 0, 0, 0),
133
+ :last_played_at => Time.local(2008, 8, 31, 21, 41, 30),
134
+ :file_data => "abc def",
135
+ :desc => "This track I really don't know anything about, in fact",
136
+ :some_fraction => 3.1415,
137
+ :is_good => false,
138
+ :full_price => BigDecimal.new("14.49"),
139
+ :volume => 5)
140
+
141
+ Track.find(3).should be_nil
142
+
143
+ track.save
144
+
145
+ Track.find(3).should_not be_nil
146
+ ensure
147
+ reset_database!
148
+ end
149
+ end
150
+
151
+ it "should be possible to create a new instance by using create" do
152
+ begin
153
+ Track.find(3).should be_nil
154
+ track = Track.create(
155
+ :track_id => 3,
156
+ :track_title => "Born to raise hell",
157
+ :time => Time.time_at(0,3,25),
158
+ :date_added => Time.local(2003, 8, 31, 0, 0, 0),
159
+ :last_played_at => Time.local(2008, 8, 31, 21, 41, 30),
160
+ :file_data => "abc def",
161
+ :desc => "This track I really don't know anything about, in fact",
162
+ :some_fraction => 3.1415,
163
+ :is_good => false,
164
+ :full_price => BigDecimal.new("14.49"),
165
+ :volume => 5)
166
+
167
+ Track.find(3).should_not be_nil
168
+ ensure
169
+ reset_database!
170
+ end
171
+ end
172
+
173
+ def create_simple
174
+ Track.create(
175
+ :track_id => 3,
176
+ :track_title => "Born to raise hell",
177
+ :time => Time.time_at(0,3,25),
178
+ :date_added => Time.local(2003, 8, 31, 0, 0, 0),
179
+ :last_played_at => Time.local(2008, 8, 31, 21, 41, 30),
180
+ :file_data => "abc def",
181
+ :desc => "This track I really don't know anything about, in fact",
182
+ :some_fraction => 3.1415,
183
+ :is_good => false,
184
+ :full_price => BigDecimal.new("14.49"),
185
+ :volume => 5)
186
+
187
+ Track.find(3)
188
+ end
189
+
190
+ it "should have correct value and type for TRACK_ID property on newly created bean" do
191
+ begin
192
+ create_simple.track_id.should == 3
193
+ ensure
194
+ reset_database!
195
+ end
196
+ end
197
+
198
+ it "should have correct value and type for track_title property on newly created bean" do
199
+ begin
200
+ create_simple.track_title.should == "Born to raise hell"
201
+ ensure
202
+ reset_database!
203
+ end
204
+ end
205
+
206
+ it "should have correct value and type for time property on newly created bean" do
207
+ begin
208
+ create_simple.time.should == Time.time_at(0,3,25)
209
+ ensure
210
+ reset_database!
211
+ end
212
+ end
213
+
214
+ it "should have correct value and type for date_added property on newly created bean" do
215
+ begin
216
+ create_simple.date_added.should == Time.local(2003, 8, 31, 0, 0, 0)
217
+ ensure
218
+ reset_database!
219
+ end
220
+ end
221
+
222
+ it "should have correct value and type for last_played_at property on newly created bean" do
223
+ begin
224
+ create_simple.last_played_at.should == Time.local(2008, 8, 31, 21, 41, 30)
225
+ ensure
226
+ reset_database!
227
+ end
228
+ end
229
+
230
+ it "should have correct value and type for file_data property on newly created bean" do
231
+ begin
232
+ create_simple.file_data.should == "abc def"
233
+ ensure
234
+ reset_database!
235
+ end
236
+ end
237
+
238
+ it "should have correct value and type for desc property on newly created bean" do
239
+ begin
240
+ create_simple.desc.should == "This track I really don't know anything about, in fact"
241
+ ensure
242
+ reset_database!
243
+ end
244
+ end
245
+
246
+ it "should have correct value and type for some_fraction property on newly created bean" do
247
+ begin
248
+ create_simple.some_fraction.should be_close(3.1415, 0.00001)
249
+ ensure
250
+ reset_database!
251
+ end
252
+ end
253
+
254
+ it "should have correct value and type for is_good property on newly created bean" do
255
+ begin
256
+ create_simple.is_good.should be_false
257
+ ensure
258
+ reset_database!
259
+ end
260
+ end
261
+
262
+ it "should have correct value and type for full_price property on newly created bean" do
263
+ begin
264
+ create_simple.full_price.should == BigDecimal.new("14.49")
265
+ ensure
266
+ reset_database!
267
+ end
268
+ end
269
+
270
+ it "should have correct value and type for volume property on newly created bean" do
271
+ begin
272
+ create_simple.volume.should == 5
273
+ ensure
274
+ reset_database!
275
+ end
276
+ end
277
+
278
+ it "should be possible to update track_title property on existing bean" do
279
+ begin
280
+ v = Track.find(1)
281
+ v.track_title = "new value here"
282
+ v.save
283
+
284
+ Track.find(1).track_title.should == "new value here"
285
+ ensure
286
+ reset_database!
287
+ end
288
+ end
289
+
290
+ it "should be possible to update time property on existing bean" do
291
+ begin
292
+ v = Track.find(1)
293
+ v.time = Time.time_at(23,32,33)
294
+ v.save
295
+
296
+ Track.find(1).time.should == Time.time_at(23,32,33)
297
+ ensure
298
+ reset_database!
299
+ end
300
+ end
301
+
302
+ it "should be possible to update date_added property on existing bean" do
303
+ begin
304
+ v = Track.find(1)
305
+ v.date_added = Time.local(2004,10,9,0,0,0)
306
+ v.save
307
+
308
+ Track.find(1).date_added.should == Time.local(2004,10,9,0,0,0)
309
+ ensure
310
+ reset_database!
311
+ end
312
+ end
313
+
314
+ it "should be possible to update last_played_at property on existing bean" do
315
+ begin
316
+ v = Track.find(1)
317
+ v.last_played_at = Time.local(2005,8,8,10,24,12)
318
+ v.save
319
+
320
+ Track.find(1).last_played_at.should == Time.local(2005,8,8,10,24,12)
321
+ ensure
322
+ reset_database!
323
+ end
324
+ end
325
+
326
+ it "should be possible to update file_data property on existing bean" do
327
+ begin
328
+ v = Track.find(1)
329
+ v.file_data = "Some data"
330
+ v.save
331
+
332
+ Track.find(1).file_data.should == "Some data"
333
+ ensure
334
+ reset_database!
335
+ end
336
+ end
337
+
338
+ it "should be possible to update desc property on existing bean" do
339
+ begin
340
+ v = Track.find(1)
341
+ v.desc = "Some description"
342
+ v.save
343
+
344
+ Track.find(1).desc.should == "Some description"
345
+ ensure
346
+ reset_database!
347
+ end
348
+ end
349
+
350
+ it "should be possible to update some_fraction property on existing bean" do
351
+ begin
352
+ v = Track.find(1)
353
+ v.some_fraction = 3.1416 #Anal test
354
+ v.save
355
+
356
+ Track.find(1).some_fraction.should be_close(3.1416, 0.00001)
357
+ ensure
358
+ reset_database!
359
+ end
360
+ end
361
+
362
+ it "should be possible to update is_good property on existing bean" do
363
+ begin
364
+ v = Track.find(1)
365
+ v.is_good = false
366
+ v.save
367
+
368
+ Track.find(1).is_good.should be_false
369
+ ensure
370
+ reset_database!
371
+ end
372
+ end
373
+
374
+ it "should be possible to update full_price property on existing bean" do
375
+ begin
376
+ v = Track.find(1)
377
+ v.full_price = BigDecimal.new("142.12")
378
+ v.save
379
+
380
+ Track.find(1).full_price.should == BigDecimal.new("142.12")
381
+ ensure
382
+ reset_database!
383
+ end
384
+ end
385
+
386
+ it "should be possible to update volume property on existing bean" do
387
+ begin
388
+ v = Track.find(1)
389
+ v.volume = 42
390
+ v.save
391
+
392
+ Track.find(1).volume.should == 42
393
+ ensure
394
+ reset_database!
395
+ end
396
+ end
397
+
398
+ it "should be possible to delete existing bean" do
399
+ begin
400
+ Track.find(1).destroy!
401
+ Track.find(1).should be_nil
402
+ ensure
403
+ reset_database!
404
+ end
405
+ end
406
+
407
+ it "should be possible to delete existing bean by id" do
408
+ begin
409
+ Track.destroy(2)
410
+ Track.find(2).should be_nil
411
+ ensure
412
+ reset_database!
413
+ end
414
+ end
415
+ end