samlown-couchrest 0.37.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/README.md +10 -4
  2. data/Rakefile +1 -1
  3. data/history.txt +8 -0
  4. data/lib/couchrest.rb +27 -49
  5. data/lib/couchrest/{core/database.rb → database.rb} +6 -11
  6. data/lib/couchrest/{core/design.rb → design.rb} +2 -2
  7. data/lib/couchrest/{core/document.rb → document.rb} +1 -1
  8. data/lib/couchrest/helper/attachments.rb +29 -0
  9. data/lib/couchrest/middlewares/logger.rb +3 -3
  10. data/lib/couchrest/monkeypatches.rb +1 -71
  11. data/lib/couchrest/{core/response.rb → response.rb} +0 -0
  12. data/lib/couchrest/{core/rest_api.rb → rest_api.rb} +9 -6
  13. data/lib/couchrest/{core/server.rb → server.rb} +0 -0
  14. data/spec/couchrest/{core/couchrest_spec.rb → couchrest_spec.rb} +16 -3
  15. data/spec/couchrest/{core/database_spec.rb → database_spec.rb} +2 -2
  16. data/spec/couchrest/{core/design_spec.rb → design_spec.rb} +2 -2
  17. data/spec/couchrest/{core/document_spec.rb → document_spec.rb} +1 -1
  18. data/spec/couchrest/{core/server_spec.rb → server_spec.rb} +2 -2
  19. data/spec/spec.opts +0 -1
  20. data/spec/spec_helper.rb +0 -4
  21. metadata +24 -113
  22. data/couchrest.gemspec +0 -183
  23. data/examples/model/example.rb +0 -144
  24. data/lib/couchrest/core/adapters/restclient.rb +0 -35
  25. data/lib/couchrest/core/http_abstraction.rb +0 -48
  26. data/lib/couchrest/core/view.rb +0 -4
  27. data/lib/couchrest/mixins.rb +0 -4
  28. data/lib/couchrest/mixins/attachments.rb +0 -31
  29. data/lib/couchrest/mixins/attribute_protection.rb +0 -74
  30. data/lib/couchrest/mixins/callbacks.rb +0 -532
  31. data/lib/couchrest/mixins/class_proxy.rb +0 -124
  32. data/lib/couchrest/mixins/collection.rb +0 -260
  33. data/lib/couchrest/mixins/design_doc.rb +0 -103
  34. data/lib/couchrest/mixins/document_queries.rb +0 -80
  35. data/lib/couchrest/mixins/extended_attachments.rb +0 -70
  36. data/lib/couchrest/mixins/extended_document_mixins.rb +0 -9
  37. data/lib/couchrest/mixins/properties.rb +0 -154
  38. data/lib/couchrest/mixins/validation.rb +0 -246
  39. data/lib/couchrest/mixins/views.rb +0 -173
  40. data/lib/couchrest/more/casted_array.rb +0 -25
  41. data/lib/couchrest/more/casted_model.rb +0 -58
  42. data/lib/couchrest/more/extended_document.rb +0 -310
  43. data/lib/couchrest/more/property.rb +0 -50
  44. data/lib/couchrest/more/typecast.rb +0 -175
  45. data/lib/couchrest/support/blank.rb +0 -42
  46. data/lib/couchrest/support/rails.rb +0 -42
  47. data/lib/couchrest/validation/auto_validate.rb +0 -157
  48. data/lib/couchrest/validation/contextual_validators.rb +0 -78
  49. data/lib/couchrest/validation/validation_errors.rb +0 -125
  50. data/lib/couchrest/validation/validators/absent_field_validator.rb +0 -74
  51. data/lib/couchrest/validation/validators/confirmation_validator.rb +0 -107
  52. data/lib/couchrest/validation/validators/format_validator.rb +0 -122
  53. data/lib/couchrest/validation/validators/formats/email.rb +0 -66
  54. data/lib/couchrest/validation/validators/formats/url.rb +0 -43
  55. data/lib/couchrest/validation/validators/generic_validator.rb +0 -120
  56. data/lib/couchrest/validation/validators/length_validator.rb +0 -139
  57. data/lib/couchrest/validation/validators/method_validator.rb +0 -89
  58. data/lib/couchrest/validation/validators/numeric_validator.rb +0 -109
  59. data/lib/couchrest/validation/validators/required_field_validator.rb +0 -114
  60. data/spec/couchrest/more/attribute_protection_spec.rb +0 -150
  61. data/spec/couchrest/more/casted_extended_doc_spec.rb +0 -79
  62. data/spec/couchrest/more/casted_model_spec.rb +0 -406
  63. data/spec/couchrest/more/extended_doc_attachment_spec.rb +0 -135
  64. data/spec/couchrest/more/extended_doc_inherited_spec.rb +0 -40
  65. data/spec/couchrest/more/extended_doc_spec.rb +0 -808
  66. data/spec/couchrest/more/extended_doc_subclass_spec.rb +0 -98
  67. data/spec/couchrest/more/extended_doc_view_spec.rb +0 -462
  68. data/spec/couchrest/more/property_spec.rb +0 -628
  69. data/spec/fixtures/more/article.rb +0 -35
  70. data/spec/fixtures/more/card.rb +0 -22
  71. data/spec/fixtures/more/cat.rb +0 -20
  72. data/spec/fixtures/more/course.rb +0 -22
  73. data/spec/fixtures/more/event.rb +0 -8
  74. data/spec/fixtures/more/invoice.rb +0 -17
  75. data/spec/fixtures/more/person.rb +0 -9
  76. data/spec/fixtures/more/question.rb +0 -6
  77. data/spec/fixtures/more/service.rb +0 -12
  78. data/spec/fixtures/more/user.rb +0 -22
@@ -1,406 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require File.expand_path('../../../spec_helper', __FILE__)
4
- require File.join(FIXTURE_PATH, 'more', 'card')
5
- require File.join(FIXTURE_PATH, 'more', 'cat')
6
- require File.join(FIXTURE_PATH, 'more', 'person')
7
- require File.join(FIXTURE_PATH, 'more', 'question')
8
- require File.join(FIXTURE_PATH, 'more', 'course')
9
-
10
-
11
- class WithCastedModelMixin < Hash
12
- include CouchRest::CastedModel
13
- property :name
14
- property :no_value
15
- property :details, :type => 'Object', :default => {}
16
- property :casted_attribute, :cast_as => 'WithCastedModelMixin'
17
- end
18
-
19
- class DummyModel < CouchRest::ExtendedDocument
20
- use_database TEST_SERVER.default_database
21
- raise "Default DB not set" if TEST_SERVER.default_database.nil?
22
- property :casted_attribute, :cast_as => 'WithCastedModelMixin'
23
- property :keywords, :cast_as => ["String"]
24
- end
25
-
26
- class CastedCallbackDoc < CouchRest::ExtendedDocument
27
- use_database TEST_SERVER.default_database
28
- raise "Default DB not set" if TEST_SERVER.default_database.nil?
29
- property :callback_model, :cast_as => 'WithCastedCallBackModel'
30
- end
31
- class WithCastedCallBackModel < Hash
32
- include CouchRest::CastedModel
33
- include CouchRest::Validation
34
- property :name
35
- property :run_before_validate
36
- property :run_after_validate
37
-
38
- before_validate do |object|
39
- object.run_before_validate = true
40
- end
41
- after_validate do |object|
42
- object.run_after_validate = true
43
- end
44
- end
45
-
46
- describe CouchRest::CastedModel do
47
-
48
- describe "A non hash class including CastedModel" do
49
- it "should fail raising and include error" do
50
- lambda do
51
- class NotAHashButWithCastedModelMixin
52
- include CouchRest::CastedModel
53
- property :name
54
- end
55
-
56
- end.should raise_error
57
- end
58
- end
59
-
60
- describe "isolated" do
61
- before(:each) do
62
- @obj = WithCastedModelMixin.new
63
- end
64
- it "should automatically include the property mixin and define getters and setters" do
65
- @obj.name = 'Matt'
66
- @obj.name.should == 'Matt'
67
- end
68
-
69
- it "should allow override of default" do
70
- @obj = WithCastedModelMixin.new(:name => 'Eric', :details => {'color' => 'orange'})
71
- @obj.name.should == 'Eric'
72
- @obj.details['color'].should == 'orange'
73
- end
74
- end
75
-
76
- describe "casted as an attribute, but without a value" do
77
- before(:each) do
78
- @obj = DummyModel.new
79
- @casted_obj = @obj.casted_attribute
80
- end
81
- it "should be nil" do
82
- @casted_obj.should == nil
83
- end
84
- end
85
-
86
- describe "casted as attribute" do
87
- before(:each) do
88
- casted = {:name => 'not whatever'}
89
- @obj = DummyModel.new(:casted_attribute => {:name => 'whatever', :casted_attribute => casted})
90
- @casted_obj = @obj.casted_attribute
91
- end
92
-
93
- it "should be available from its parent" do
94
- @casted_obj.should be_an_instance_of(WithCastedModelMixin)
95
- end
96
-
97
- it "should have the getters defined" do
98
- @casted_obj.name.should == 'whatever'
99
- end
100
-
101
- it "should know who casted it" do
102
- @casted_obj.casted_by.should == @obj
103
- end
104
-
105
- it "should return nil for the 'no_value' attribute" do
106
- @casted_obj.no_value.should be_nil
107
- end
108
-
109
- it "should return nil for the unknown attribute" do
110
- @casted_obj["unknown"].should be_nil
111
- end
112
-
113
- it "should return {} for the hash attribute" do
114
- @casted_obj.details.should == {}
115
- end
116
-
117
- it "should cast its own attributes" do
118
- @casted_obj.casted_attribute.should be_instance_of(WithCastedModelMixin)
119
- end
120
- end
121
-
122
- describe "casted as an array of a different type" do
123
- before(:each) do
124
- @obj = DummyModel.new(:keywords => ['couch', 'sofa', 'relax', 'canapé'])
125
- end
126
-
127
- it "should cast the array properly" do
128
- @obj.keywords.should be_an_instance_of(Array)
129
- @obj.keywords.first.should == 'couch'
130
- end
131
- end
132
-
133
- describe "update attributes without saving" do
134
- before(:each) do
135
- @question = Question.new(:q => "What is your quest?", :a => "To seek the Holy Grail")
136
- end
137
- it "should work for attribute= methods" do
138
- @question.q.should == "What is your quest?"
139
- @question['a'].should == "To seek the Holy Grail"
140
- @question.update_attributes_without_saving(:q => "What is your favorite color?", 'a' => "Blue")
141
- @question['q'].should == "What is your favorite color?"
142
- @question.a.should == "Blue"
143
- end
144
-
145
- it "should also work for attributes= alias" do
146
- @question.respond_to?(:attributes=).should be_true
147
- @question.attributes = {:q => "What is your favorite color?", 'a' => "Blue"}
148
- @question['q'].should == "What is your favorite color?"
149
- @question.a.should == "Blue"
150
- end
151
-
152
- it "should flip out if an attribute= method is missing" do
153
- lambda {
154
- @q.update_attributes_without_saving('foo' => "something", :a => "No green")
155
- }.should raise_error(NoMethodError)
156
- end
157
-
158
- it "should not change any attributes if there is an error" do
159
- lambda {
160
- @q.update_attributes_without_saving('foo' => "something", :a => "No green")
161
- }.should raise_error(NoMethodError)
162
- @question.q.should == "What is your quest?"
163
- @question.a.should == "To seek the Holy Grail"
164
- end
165
- end
166
-
167
- describe "saved document with casted models" do
168
- before(:each) do
169
- reset_test_db!
170
- @obj = DummyModel.new(:casted_attribute => {:name => 'whatever'})
171
- @obj.save.should be_true
172
- @obj = DummyModel.get(@obj.id)
173
- end
174
-
175
- it "should be able to load with the casted models" do
176
- casted_obj = @obj.casted_attribute
177
- casted_obj.should_not be_nil
178
- casted_obj.should be_an_instance_of(WithCastedModelMixin)
179
- end
180
-
181
- it "should have defined getters for the casted model" do
182
- casted_obj = @obj.casted_attribute
183
- casted_obj.name.should == "whatever"
184
- end
185
-
186
- it "should have defined setters for the casted model" do
187
- casted_obj = @obj.casted_attribute
188
- casted_obj.name = "test"
189
- casted_obj.name.should == "test"
190
- end
191
-
192
- it "should retain an override of a casted model attribute's default" do
193
- casted_obj = @obj.casted_attribute
194
- casted_obj.details['color'] = 'orange'
195
- @obj.save
196
- casted_obj = DummyModel.get(@obj.id).casted_attribute
197
- casted_obj.details['color'].should == 'orange'
198
- end
199
-
200
- end
201
-
202
- describe "saving document with array of casted models and validation" do
203
- before :each do
204
- @cat = Cat.new
205
- @cat.save
206
- end
207
-
208
- it "should save" do
209
- toy = CatToy.new :name => "Mouse"
210
- @cat.toys.push(toy)
211
- @cat.save.should be_true
212
- @cat = Cat.get @cat.id
213
- @cat.toys.class.should == CouchRest::CastedArray
214
- @cat.toys.first.class.should == CatToy
215
- @cat.toys.first.should === toy
216
- end
217
-
218
- it "should fail because name is not present" do
219
- toy = CatToy.new
220
- @cat.toys.push(toy)
221
- @cat.should_not be_valid
222
- @cat.save.should be_false
223
- end
224
-
225
- it "should not fail if the casted model doesn't have validation" do
226
- Cat.property :masters, :cast_as => ['Person'], :default => []
227
- Cat.validates_presence_of :name
228
- cat = Cat.new(:name => 'kitty')
229
- cat.should be_valid
230
- cat.masters.push Person.new
231
- cat.should be_valid
232
- end
233
- end
234
-
235
- describe "calling valid?" do
236
- before :each do
237
- @cat = Cat.new
238
- @toy1 = CatToy.new
239
- @toy2 = CatToy.new
240
- @toy3 = CatToy.new
241
- @cat.favorite_toy = @toy1
242
- @cat.toys << @toy2
243
- @cat.toys << @toy3
244
- end
245
-
246
- describe "on the top document" do
247
- it "should put errors on all invalid casted models" do
248
- @cat.should_not be_valid
249
- @cat.errors.should_not be_empty
250
- @toy1.errors.should_not be_empty
251
- @toy2.errors.should_not be_empty
252
- @toy3.errors.should_not be_empty
253
- end
254
-
255
- it "should not put errors on valid casted models" do
256
- @toy1.name = "Feather"
257
- @toy2.name = "Twine"
258
- @cat.should_not be_valid
259
- @cat.errors.should_not be_empty
260
- @toy1.errors.should be_empty
261
- @toy2.errors.should be_empty
262
- @toy3.errors.should_not be_empty
263
- end
264
- end
265
-
266
- describe "on a casted model property" do
267
- it "should only validate itself" do
268
- @toy1.should_not be_valid
269
- @toy1.errors.should_not be_empty
270
- @cat.errors.should be_empty
271
- @toy2.errors.should be_empty
272
- @toy3.errors.should be_empty
273
- end
274
- end
275
-
276
- describe "on a casted model inside a casted collection" do
277
- it "should only validate itself" do
278
- @toy2.should_not be_valid
279
- @toy2.errors.should_not be_empty
280
- @cat.errors.should be_empty
281
- @toy1.errors.should be_empty
282
- @toy3.errors.should be_empty
283
- end
284
- end
285
- end
286
-
287
- describe "calling new? on a casted model" do
288
- before :each do
289
- reset_test_db!
290
- @cat = Cat.new(:name => 'Sockington')
291
- @favorite_toy = CatToy.new(:name => 'Catnip Ball')
292
- @cat.favorite_toy = @favorite_toy
293
- @cat.toys << CatToy.new(:name => 'Fuzzy Stick')
294
- end
295
-
296
- it "should be true on new" do
297
- CatToy.new.should be_new
298
- CatToy.new.new_record?.should be_true
299
- end
300
-
301
- it "should be true after assignment" do
302
- @cat.should be_new
303
- @cat.favorite_toy.should be_new
304
- @cat.toys.first.should be_new
305
- end
306
-
307
- it "should not be true after create or save" do
308
- @cat.create
309
- @cat.save
310
- @cat.favorite_toy.should_not be_new
311
- @cat.toys.first.should_not be_new
312
- end
313
-
314
- it "should not be true after get from the database" do
315
- @cat.save
316
- @cat = Cat.get(@cat.id)
317
- @cat.favorite_toy.should_not be_new
318
- @cat.toys.first.should_not be_new
319
- end
320
-
321
- it "should still be true after a failed create or save" do
322
- @cat.name = nil
323
- @cat.create.should be_false
324
- @cat.save.should be_false
325
- @cat.favorite_toy.should be_new
326
- @cat.toys.first.should be_new
327
- end
328
- end
329
-
330
- describe "calling base_doc from a nested casted model" do
331
- before :each do
332
- @course = Course.new(:title => 'Science 101')
333
- @professor = Person.new(:name => 'Professor Plum')
334
- @cat = Cat.new(:name => 'Scratchy')
335
- @toy1 = CatToy.new
336
- @toy2 = CatToy.new
337
- @course.professor = @professor
338
- @professor.pet = @cat
339
- @cat.favorite_toy = @toy1
340
- @cat.toys << @toy2
341
- end
342
-
343
- it "should reference the top document for" do
344
- @course.base_doc.should === @course
345
- @professor.casted_by.should === @course
346
- @professor.base_doc.should === @course
347
- @cat.base_doc.should === @course
348
- @toy1.base_doc.should === @course
349
- @toy2.base_doc.should === @course
350
- end
351
-
352
- it "should call setter on top document" do
353
- @toy1.base_doc.should_not be_nil
354
- @toy1.base_doc.title = 'Tom Foolery'
355
- @course.title.should == 'Tom Foolery'
356
- end
357
-
358
- it "should return nil if not yet casted" do
359
- person = Person.new
360
- person.base_doc.should == nil
361
- end
362
- end
363
-
364
- describe "calling base_doc.save from a nested casted model" do
365
- before :each do
366
- reset_test_db!
367
- @cat = Cat.new(:name => 'Snowball')
368
- @toy = CatToy.new
369
- @cat.favorite_toy = @toy
370
- end
371
-
372
- it "should not save parent document when casted model is invalid" do
373
- @toy.should_not be_valid
374
- @toy.base_doc.save.should be_false
375
- lambda{@toy.base_doc.save!}.should raise_error
376
- end
377
-
378
- it "should save parent document when nested casted model is valid" do
379
- @toy.name = "Mr Squeaks"
380
- @toy.should be_valid
381
- @toy.base_doc.save.should be_true
382
- lambda{@toy.base_doc.save!}.should_not raise_error
383
- end
384
- end
385
-
386
- describe "callbacks" do
387
- before(:each) do
388
- @doc = CastedCallbackDoc.new
389
- @model = WithCastedCallBackModel.new
390
- @doc.callback_model = @model
391
- end
392
-
393
- describe "validate" do
394
- it "should run before_validate before validating" do
395
- @model.run_before_validate.should be_nil
396
- @model.should be_valid
397
- @model.run_before_validate.should be_true
398
- end
399
- it "should run after_validate after validating" do
400
- @model.run_after_validate.should be_nil
401
- @model.should be_valid
402
- @model.run_after_validate.should be_true
403
- end
404
- end
405
- end
406
- end
@@ -1,135 +0,0 @@
1
- require File.expand_path('../../../spec_helper', __FILE__)
2
-
3
- describe "ExtendedDocument attachments" do
4
-
5
- describe "#has_attachment?" do
6
- before(:each) do
7
- reset_test_db!
8
- @obj = Basic.new
9
- @obj.save.should == true
10
- @file = File.open(FIXTURE_PATH + '/attachments/test.html')
11
- @attachment_name = 'my_attachment'
12
- @obj.create_attachment(:file => @file, :name => @attachment_name)
13
- end
14
-
15
- it 'should return false if there is no attachment' do
16
- @obj.has_attachment?('bogus').should be_false
17
- end
18
-
19
- it 'should return true if there is an attachment' do
20
- @obj.has_attachment?(@attachment_name).should be_true
21
- end
22
-
23
- it 'should return true if an object with an attachment is reloaded' do
24
- @obj.save.should be_true
25
- reloaded_obj = Basic.get(@obj.id)
26
- reloaded_obj.has_attachment?(@attachment_name).should be_true
27
- end
28
-
29
- it 'should return false if an attachment has been removed' do
30
- @obj.delete_attachment(@attachment_name)
31
- @obj.has_attachment?(@attachment_name).should be_false
32
- end
33
- end
34
-
35
- describe "creating an attachment" do
36
- before(:each) do
37
- @obj = Basic.new
38
- @obj.save.should == true
39
- @file_ext = File.open(FIXTURE_PATH + '/attachments/test.html')
40
- @file_no_ext = File.open(FIXTURE_PATH + '/attachments/README')
41
- @attachment_name = 'my_attachment'
42
- @content_type = 'media/mp3'
43
- end
44
-
45
- it "should create an attachment from file with an extension" do
46
- @obj.create_attachment(:file => @file_ext, :name => @attachment_name)
47
- @obj.save.should == true
48
- reloaded_obj = Basic.get(@obj.id)
49
- reloaded_obj['_attachments'][@attachment_name].should_not be_nil
50
- end
51
-
52
- it "should create an attachment from file without an extension" do
53
- @obj.create_attachment(:file => @file_no_ext, :name => @attachment_name)
54
- @obj.save.should == true
55
- reloaded_obj = Basic.get(@obj.id)
56
- reloaded_obj['_attachments'][@attachment_name].should_not be_nil
57
- end
58
-
59
- it 'should raise ArgumentError if :file is missing' do
60
- lambda{ @obj.create_attachment(:name => @attachment_name) }.should raise_error
61
- end
62
-
63
- it 'should raise ArgumentError if :name is missing' do
64
- lambda{ @obj.create_attachment(:file => @file_ext) }.should raise_error
65
- end
66
-
67
- it 'should set the content-type if passed' do
68
- @obj.create_attachment(:file => @file_ext, :name => @attachment_name, :content_type => @content_type)
69
- @obj['_attachments'][@attachment_name]['content_type'].should == @content_type
70
- end
71
- end
72
-
73
- describe 'reading, updating, and deleting an attachment' do
74
- before(:each) do
75
- @obj = Basic.new
76
- @file = File.open(FIXTURE_PATH + '/attachments/test.html')
77
- @attachment_name = 'my_attachment'
78
- @obj.create_attachment(:file => @file, :name => @attachment_name)
79
- @obj.save.should == true
80
- @file.rewind
81
- @content_type = 'media/mp3'
82
- end
83
-
84
- it 'should read an attachment that exists' do
85
- @obj.read_attachment(@attachment_name).should == @file.read
86
- end
87
-
88
- it 'should update an attachment that exists' do
89
- file = File.open(FIXTURE_PATH + '/attachments/README')
90
- @file.should_not == file
91
- @obj.update_attachment(:file => file, :name => @attachment_name)
92
- @obj.save
93
- reloaded_obj = Basic.get(@obj.id)
94
- file.rewind
95
- reloaded_obj.read_attachment(@attachment_name).should_not == @file.read
96
- reloaded_obj.read_attachment(@attachment_name).should == file.read
97
- end
98
-
99
- it 'should se the content-type if passed' do
100
- file = File.open(FIXTURE_PATH + '/attachments/README')
101
- @file.should_not == file
102
- @obj.update_attachment(:file => file, :name => @attachment_name, :content_type => @content_type)
103
- @obj['_attachments'][@attachment_name]['content_type'].should == @content_type
104
- end
105
-
106
- it 'should delete an attachment that exists' do
107
- @obj.delete_attachment(@attachment_name)
108
- @obj.save
109
- lambda{Basic.get(@obj.id).read_attachment(@attachment_name)}.should raise_error
110
- end
111
- end
112
-
113
- describe "#attachment_url" do
114
- before(:each) do
115
- @obj = Basic.new
116
- @file = File.open(FIXTURE_PATH + '/attachments/test.html')
117
- @attachment_name = 'my_attachment'
118
- @obj.create_attachment(:file => @file, :name => @attachment_name)
119
- @obj.save.should == true
120
- end
121
-
122
- it 'should return nil if attachment does not exist' do
123
- @obj.attachment_url('bogus').should be_nil
124
- end
125
-
126
- it 'should return the attachment URL as specified by CouchDB HttpDocumentApi' do
127
- @obj.attachment_url(@attachment_name).should == "#{Basic.database}/#{@obj.id}/#{@attachment_name}"
128
- end
129
-
130
- it 'should return the attachment URI' do
131
- @obj.attachment_uri(@attachment_name).should == "#{Basic.database.uri}/#{@obj.id}/#{@attachment_name}"
132
- end
133
-
134
- end
135
- end