s2container 0.8.0

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.
Files changed (136) hide show
  1. data/Apache_Software_License_2.0.txt +202 -0
  2. data/ChangeLog +1 -0
  3. data/ChangeLog.ja +64 -0
  4. data/README +9 -0
  5. data/Rakefile +51 -0
  6. data/example/example01/example.rb +13 -0
  7. data/example/example01/run.rb +20 -0
  8. data/example/example02/example.rb +30 -0
  9. data/example/example02/run.rb +8 -0
  10. data/example/example03/example.rb +8 -0
  11. data/example/example03/run.rb +12 -0
  12. data/example/example04/example.rb +9 -0
  13. data/example/example04/run.rb +15 -0
  14. data/example/example05/example.rb +9 -0
  15. data/example/example05/run.rb +21 -0
  16. data/example/example06/example.rb +9 -0
  17. data/example/example06/run.rb +8 -0
  18. data/example/example07/example.rb +8 -0
  19. data/example/example07/run.rb +15 -0
  20. data/example/example08/example.rb +9 -0
  21. data/example/example08/run.rb +19 -0
  22. data/example/example09/example.rb +7 -0
  23. data/example/example09/run.rb +11 -0
  24. data/example/example10/example.rb +9 -0
  25. data/example/example10/run.rb +10 -0
  26. data/example/example11/example.rb +18 -0
  27. data/example/example11/run.rb +10 -0
  28. data/example/example12/example.rb +9 -0
  29. data/example/example12/run.rb +8 -0
  30. data/example/example13/example.rb +18 -0
  31. data/example/example13/example.sql +4 -0
  32. data/example/example13/run.rb +22 -0
  33. data/example/example13/sample.db +0 -0
  34. data/example/example14/example.db +0 -0
  35. data/example/example14/example.rb +57 -0
  36. data/example/example14/example.sql +50 -0
  37. data/example/example14/run1.rb +23 -0
  38. data/example/example14/run2.rb +28 -0
  39. data/example/example14/run3.rb +25 -0
  40. data/example/example14/run4.rb +23 -0
  41. data/example/example14/run5.rb +35 -0
  42. data/example/example15/example.rb +20 -0
  43. data/example/example15/run.rb +28 -0
  44. data/example/example16/example.rb +15 -0
  45. data/example/example16/run.rb +24 -0
  46. data/example/quickstart/quickstart1/quickstart.rb +10 -0
  47. data/example/quickstart/quickstart2/quickstart.rb +9 -0
  48. data/example/quickstart/quickstart3/quickstart.rb +9 -0
  49. data/example/quickstart/quickstart4/quickstart.rb +14 -0
  50. data/example/quickstart/quickstart5/quickstart.rb +21 -0
  51. data/example/quickstart/quickstart6/quickstart.rb +28 -0
  52. data/example/quickstart/quickstart7/quickstart.rb +18 -0
  53. data/lib/s2container.rb +25 -0
  54. data/lib/seasar/aop/aspect.rb +30 -0
  55. data/lib/seasar/aop/interceptor/trace-interceptor.rb +48 -0
  56. data/lib/seasar/aop/method-invocation.rb +57 -0
  57. data/lib/seasar/aop/pointcut.rb +53 -0
  58. data/lib/seasar/aop/s2aop-factory.rb +125 -0
  59. data/lib/seasar/aop.rb +29 -0
  60. data/lib/seasar/beans/abstract-property-desc.rb +59 -0
  61. data/lib/seasar/beans/attribute-accessor-desc.rb +60 -0
  62. data/lib/seasar/beans/bean-desc-factory.rb +68 -0
  63. data/lib/seasar/beans/bean-desc.rb +232 -0
  64. data/lib/seasar/beans/instance-variable-desc.rb +58 -0
  65. data/lib/seasar/beans.rb +29 -0
  66. data/lib/seasar/container/arg-def.rb +51 -0
  67. data/lib/seasar/container/aspect-def.rb +60 -0
  68. data/lib/seasar/container/aspect-info-def.rb +94 -0
  69. data/lib/seasar/container/assembler/abstract-assembler.rb +68 -0
  70. data/lib/seasar/container/assembler/auto-property-assembler.rb +57 -0
  71. data/lib/seasar/container/assembler/autobinding-auto-def.rb +55 -0
  72. data/lib/seasar/container/assembler/autobinding-def-factory.rb +57 -0
  73. data/lib/seasar/container/assembler/autobinding-none-def.rb +53 -0
  74. data/lib/seasar/container/assembler/manual-constructor-assembler.rb +67 -0
  75. data/lib/seasar/container/assembler/manual-property-assembler.rb +68 -0
  76. data/lib/seasar/container/autobinding-def.rb +36 -0
  77. data/lib/seasar/container/component-def.rb +229 -0
  78. data/lib/seasar/container/component-info-def.rb +117 -0
  79. data/lib/seasar/container/deployer/abstract-component-deployer.rb +47 -0
  80. data/lib/seasar/container/deployer/instance-def-factory.rb +56 -0
  81. data/lib/seasar/container/deployer/instance-outer-def.rb +44 -0
  82. data/lib/seasar/container/deployer/instance-prototype-def.rb +44 -0
  83. data/lib/seasar/container/deployer/instance-singleton-def.rb +42 -0
  84. data/lib/seasar/container/deployer/outer-component-deployer.rb +47 -0
  85. data/lib/seasar/container/deployer/prototype-component-deployer.rb +51 -0
  86. data/lib/seasar/container/deployer/singleton-component-deployer.rb +54 -0
  87. data/lib/seasar/container/exception/component-notfound-runtime-exception.rb +44 -0
  88. data/lib/seasar/container/exception/cyclic-reference-runtime-exception.rb +43 -0
  89. data/lib/seasar/container/exception/illegal-autobinding-def-runtime-exception.rb +43 -0
  90. data/lib/seasar/container/exception/illegal-instance-def-runtime-exception.rb +44 -0
  91. data/lib/seasar/container/exception/toomany-registration-runtime-exception.rb +46 -0
  92. data/lib/seasar/container/instance-def.rb +37 -0
  93. data/lib/seasar/container/outer-component-def.rb +34 -0
  94. data/lib/seasar/container/property-def.rb +43 -0
  95. data/lib/seasar/container/s2application-context.rb +447 -0
  96. data/lib/seasar/container/s2container-component-def.rb +41 -0
  97. data/lib/seasar/container/s2container.rb +308 -0
  98. data/lib/seasar/container/simple-component-def.rb +45 -0
  99. data/lib/seasar/container/toomany-registration-component-def.rb +69 -0
  100. data/lib/seasar/container.rb +143 -0
  101. data/lib/seasar/dbi/dbi-interceptor.rb +97 -0
  102. data/lib/seasar/dbi/paginate.rb +215 -0
  103. data/lib/seasar/dbi.rb +26 -0
  104. data/lib/seasar/exception/notyet-implemented-exception.rb +25 -0
  105. data/lib/seasar/exception/property-notfound-runtime-exception.rb +48 -0
  106. data/lib/seasar/exception/s2runtime-exception.rb +35 -0
  107. data/lib/seasar/exception/unsupported-operation-exception.rb +25 -0
  108. data/lib/seasar/exception.rb +27 -0
  109. data/lib/seasar/log/s2logger.rb +75 -0
  110. data/lib/seasar/log.rb +38 -0
  111. data/lib/seasar/util/class-util.rb +116 -0
  112. data/lib/seasar/util.rb +24 -0
  113. data/setup.rb +1585 -0
  114. data/test/seasar/aop/test_pointcut.rb +25 -0
  115. data/test/seasar/aop/test_s2aop_factory.rb +90 -0
  116. data/test/seasar/beans/test_bean-desc.rb +179 -0
  117. data/test/seasar/container/assembler/test_auto_property_assembler.rb +87 -0
  118. data/test/seasar/container/assembler/test_autobinding_def_factory.rb +22 -0
  119. data/test/seasar/container/assembler/test_manual_property_assembler.rb +59 -0
  120. data/test/seasar/container/assembler/test_manula_constructor_assembler.rb +59 -0
  121. data/test/seasar/container/assembler/test_proc_constructor_assembler.rb +61 -0
  122. data/test/seasar/container/deployer/test_instance_def_factory.rb +24 -0
  123. data/test/seasar/container/deployer/test_prototype-deployer.rb +25 -0
  124. data/test/seasar/container/deployer/test_singleton-component-deployer.rb +24 -0
  125. data/test/seasar/container/s2app_load_sample.rb +6 -0
  126. data/test/seasar/container/test_arg-def.rb +34 -0
  127. data/test/seasar/container/test_aspect-info-def.rb +85 -0
  128. data/test/seasar/container/test_component-def.rb +91 -0
  129. data/test/seasar/container/test_component-info-def.rb +88 -0
  130. data/test/seasar/container/test_s2application-context.rb +290 -0
  131. data/test/seasar/container/test_s2container.rb +268 -0
  132. data/test/seasar/dbi/test_paginate.rb +265 -0
  133. data/test/seasar/test_log.rb +20 -0
  134. data/test/seasar/test_util.rb +46 -0
  135. data/test/test-suite.rb +7 -0
  136. metadata +211 -0
@@ -0,0 +1,447 @@
1
+ # -*- coding: utf-8 -*-
2
+ #--
3
+ # +----------------------------------------------------------------------+
4
+ # | Copyright 2005-2008 the Seasar Foundation and the Others. |
5
+ # +----------------------------------------------------------------------+
6
+ # | Licensed under the Apache License, Version 2.0 (the "License"); |
7
+ # | you may not use this file except in compliance with the License. |
8
+ # | You may obtain a copy of the License at |
9
+ # | |
10
+ # | http://www.apache.org/licenses/LICENSE-2.0 |
11
+ # | |
12
+ # | Unless required by applicable law or agreed to in writing, software |
13
+ # | distributed under the License is distributed on an "AS IS" BASIS, |
14
+ # | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
15
+ # | either express or implied. See the License for the specific language |
16
+ # | governing permissions and limitations under the License. |
17
+ # +----------------------------------------------------------------------+
18
+ #++
19
+ module Seasar
20
+ module Container
21
+ #
22
+ # The main functions of S2ApplicationContext become the next.
23
+ # - Generate S2Container which contains the registerd class as a component
24
+ # - Automatic setup of Aspect
25
+ # - Singleton S2Container instance management
26
+ #
27
+ class S2ApplicationContext
28
+ @@static_component_infos = []
29
+ @@static_aspect_infos = []
30
+ @@instance = nil
31
+
32
+ class << self
33
+ #
34
+ # - args
35
+ # - none
36
+ # - return
37
+ # - Seasar::Container::S2ApplicationContext
38
+ #
39
+ def instance
40
+ if @@instance.nil?
41
+ @@instance = self.new
42
+ end
43
+ return @@instance;
44
+ end
45
+
46
+ #
47
+ # - args
48
+ # 1. Seasar::Container::S2ApplicationContext
49
+ # - return
50
+ # - nil
51
+ #
52
+ def instance=(obj); @@instance = obj; end
53
+ end
54
+
55
+ #
56
+ # - args
57
+ # - none
58
+ # - return
59
+ # - nil
60
+ #
61
+ def initialize
62
+ self.init
63
+ end
64
+
65
+ #
66
+ # - args
67
+ # - none
68
+ # - return
69
+ # - nil
70
+ #
71
+ def init(options = {})
72
+ if options[:force] == true
73
+ @@static_component_infos = []
74
+ @@static_aspect_infos = []
75
+ @@instance = nil
76
+ end
77
+ @include_patterns = []
78
+ @exclude_patterns = []
79
+ @singletons = {}
80
+ @selectors = []
81
+ @component_infos = []
82
+ @aspect_infos = []
83
+ @snapshot_component_infos = nil
84
+ @snapshot_aspect_infos = nil
85
+ end
86
+
87
+ #
88
+ # - args
89
+ # - none
90
+ # - return
91
+ # - Array : component information list.
92
+ #
93
+ def component_infos
94
+ return @snapshot_component_infos if self.snapshot?
95
+ return @component_infos + @@static_component_infos
96
+ end
97
+
98
+ #
99
+ # - args
100
+ # - none
101
+ # - return
102
+ # - Array : aspect information list.
103
+ #
104
+ def aspect_infos
105
+ return @snapshot_aspect_infos if self.snapshot?
106
+ return @aspect_infos + @@static_aspect_infos
107
+ end
108
+
109
+ #
110
+ # - args
111
+ # - none
112
+ # - return
113
+ # - Boolean
114
+ #
115
+ def snapshot?
116
+ if @snapshot_component_infos.nil? && @snapshot_aspect_infos.nil?
117
+ return false
118
+ else
119
+ return true
120
+ end
121
+ end
122
+
123
+ #
124
+ # - args
125
+ # - none
126
+ # - return
127
+ # - S2ApplicationContext
128
+ #
129
+ def snapshot
130
+ app = S2ApplicationContext.new
131
+ app.snapshot_component_infos = self.component_infos
132
+ app.snapshot_aspect_infos = self.aspect_infos
133
+ return app
134
+ end
135
+
136
+ #
137
+ # - args
138
+ # 1. Array <em>infos</em>
139
+ # - return
140
+ # - nil
141
+ #
142
+ def snapshot_component_infos=(infos)
143
+ @snapshot_component_infos = infos
144
+ end
145
+
146
+ #
147
+ # - args
148
+ # 1. Array <em>infos</em>
149
+ # - return
150
+ # - nil
151
+ #
152
+ def snapshot_aspect_infos=(infos)
153
+ @snapshot_aspect_infos = infos
154
+ end
155
+
156
+ #
157
+ # - args
158
+ # 1. Hash _options_ component information
159
+ # 2. Proc _procedure_ constructor block of component
160
+ # - return
161
+ # - nil
162
+ #
163
+ def register(options = {}, &procedure)
164
+ raise "can not register component_info to snapshot application context." if self.snapshot?
165
+ info = ComponentInfoDef.new(options, &procedure)
166
+ if options[:static] == true
167
+ @@static_component_infos << info
168
+ else
169
+ @component_infos << info
170
+ end
171
+ end
172
+
173
+ #
174
+ # - args
175
+ # 1. Hash _options_ aspect information
176
+ # 2. Proc _procedure_ interceptor block
177
+ # - return
178
+ # - nil
179
+ #
180
+ def register_aspect(options, &procedure)
181
+ raise "can not register aspect_info to snapshot application context." if self.snapshot?
182
+ if block_given?
183
+ options[:interceptor] = procedure
184
+ end
185
+
186
+ info = AspectInfoDef.new(options)
187
+ if options[:static] == true
188
+ @@static_aspect_infos << info
189
+ else
190
+ @aspect_infos << info
191
+ end
192
+ end
193
+ alias aspect register_aspect
194
+
195
+ #
196
+ # - args
197
+ # 1. String|Symbol <em>key</em> component key
198
+ # 2. Array <em>namespaces</em> String namespaces
199
+ # 3. Proc <em>procedure</em> constructor block
200
+ # - return
201
+ # - Object
202
+ #
203
+ def get_component(key, namespaces = [], &procedure)
204
+ namespace_key = self.create_singleton(namespaces)
205
+ return @singletons[namespace_key].get_component(key, &procedure)
206
+ end
207
+ alias component get_component
208
+ alias get get_component
209
+ alias [] get_component
210
+
211
+ #
212
+ # - args
213
+ # 1. String|Symbol <em>key</em> component key
214
+ # 2. Array <em>namespaces</em> String namespaces
215
+ # - return
216
+ # - Boolean
217
+ #
218
+ def has_component_def(key, namespaces = [])
219
+ namespace_key = self.create_singleton(namespaces)
220
+ return @singletons[namespace_key].has_component_def(key)
221
+ end
222
+ alias component_def? has_component_def
223
+
224
+ #
225
+ # - args
226
+ # 1. Array <em>namespaces</em> String namespaces
227
+ # - return
228
+ # - String
229
+ #
230
+ def create_singleton(namespaces)
231
+ namespaces = [namespaces] unless namespaces.is_a?(Array)
232
+ key = namespaces.sort
233
+ if not @singletons.key?(key)
234
+ @singletons[key] = self.create(namespaces)
235
+ end
236
+ return key
237
+ end
238
+
239
+ #
240
+ # - args
241
+ # 1. Array <em>namespaces</em> String namespaces
242
+ # 2. Proc <em>procedure</em> constructor block
243
+ # - return
244
+ # - Seasar::Container::S2Container
245
+ #
246
+ def create(namespaces = [], &procedure)
247
+ namespaces = [namespaces] unless namespaces.is_a?(Array)
248
+ container = S2Container.new
249
+ component_defs = []
250
+ component_infos = self.get_component_infos(namespaces)
251
+ component_infos = self.select_component_infos(component_infos, &procedure)
252
+ component_infos.each {|component_info|
253
+ if component_info.procedure.nil?
254
+ cd = ComponentDef.new(component_info.component_class, component_info.component_name)
255
+ else
256
+ procedure = component_info.procedure
257
+ cd = ComponentDef.new(component_info.component_class, component_info.component_name, &procedure)
258
+ end
259
+ if not component_info.instance.nil?
260
+ cd.instance_def = Seasar::Container::Deployer::InstanceDefFactory.get_instance_def(component_info.instance)
261
+ end
262
+ if not component_info.autobinding.nil?
263
+ cd.autobinding_def = Seasar::Container::Assembler::AutoBindingDefFactory.get_autobinding_def(component_info.autobinding)
264
+ end
265
+ component_defs << cd
266
+ if component_info.namespace.nil?
267
+ container.register(cd)
268
+ else
269
+ self.get_container(container, component_info.namespace).register(cd)
270
+ end
271
+ }
272
+ component_defs.each{|cd|
273
+ self.setup_auto_aspect(cd)
274
+ }
275
+ return container
276
+ end
277
+
278
+ #
279
+ # - args
280
+ # 1. Seasar::Container::ComponentDef <em>component_def</em>
281
+ # - return
282
+ # - nil
283
+ #
284
+ def setup_auto_aspect(component_def)
285
+ self.aspect_infos.each {|aspect_info|
286
+ if aspect_info.applicable?(component_def)
287
+ aspect_def = AspectDef.new(aspect_info.pointcut)
288
+ if component_def.container.has_component_def(aspect_info.interceptor)
289
+ aspect_def.child_component_def = component_def.container.get_component_def(aspect_info.interceptor)
290
+ else
291
+ aspect_def.value = aspect_info.interceptor
292
+ end
293
+ component_def.add_aspect_def(aspect_def)
294
+ end
295
+ }
296
+ end
297
+
298
+ #
299
+ # - args
300
+ # 1. Seasar::Container::S2Container <em>container</em>
301
+ # 2. String <em>namespace</em>
302
+ # - return
303
+ # - Seasar::Container::S2Container
304
+ #
305
+ def get_container(container, namespace)
306
+ items = namespace.split('.', 2)
307
+ if container.has_component_def(items[0])
308
+ if items.length == 1
309
+ return container.get_component(items[0])
310
+ else
311
+ return self.get_container(container.get_component(items[0]), items[1])
312
+ end
313
+ else
314
+ child = S2Container.new
315
+ child.namespace = items[0]
316
+ container.include(child)
317
+ if items.length == 1
318
+ return child
319
+ else
320
+ return self.get_container(child, items[1])
321
+ end
322
+ end
323
+ end
324
+
325
+ #
326
+ # - args
327
+ # 1. Array <em>component_infos</em> Array of Seasar::Container::ComponentInfoDef
328
+ # 2. Proc <em>procedure</em> filter block
329
+ # - return
330
+ # - Array
331
+ #
332
+ def select_component_infos(component_infos, &procedure)
333
+ @selectors.each {|selector|
334
+ component_infos = selector.call(component_infos)
335
+ raise TypeError.new("result of component selector must be Array") if not component_infos.is_a?(Array)
336
+ }
337
+ if not procedure.nil?
338
+ component_infos = procedure.call(component_infos)
339
+ end
340
+ raise TypeError.new("result of component selector must be Array") if not component_infos.is_a?(Array)
341
+ return component_infos
342
+ end
343
+
344
+ #
345
+ # - args
346
+ # 1. Array <em>namespaces</em> String namespaces
347
+ # - return
348
+ # - Array
349
+ #
350
+ def get_component_infos(namespaces)
351
+ component_infos = self.select_component_info_by_namespace(self.component_infos, namespaces)
352
+ component_infos = self.select_component_info_by_include(component_infos)
353
+ component_infos = self.select_component_info_by_exclude(component_infos)
354
+ return component_infos
355
+ end
356
+
357
+ #
358
+ # - args
359
+ # 1. Array <em>component_infos</em> Array of Seasar::Container::ComponentInfoDef
360
+ # - return
361
+ # - Array
362
+ #
363
+ def select_component_info_by_exclude(component_infos)
364
+ return component_infos if @exclude_patterns.size == 0
365
+ result = []
366
+ component_infos.each {|info|
367
+ @exclude_patterns.each {|pattern|
368
+ if info.match(pattern)
369
+ next
370
+ end
371
+ result << info
372
+ break
373
+ }
374
+ }
375
+ return result
376
+ end
377
+
378
+ #
379
+ # - args
380
+ # 1. Array <em>component_infos</em> Array of Seasar::Container::ComponentInfoDef
381
+ # - return
382
+ # - Array
383
+ #
384
+ def select_component_info_by_include(component_infos)
385
+ return component_infos if @include_patterns.size == 0
386
+ result = []
387
+ component_infos.each {|info|
388
+ @include_patterns.each {|pattern|
389
+ if info.match(pattern)
390
+ result << info
391
+ break
392
+ end
393
+ }
394
+ }
395
+ return result
396
+ end
397
+
398
+ #
399
+ # - args
400
+ # 1. Array <em>component_infos</em> Array of Seasar::Container::ComponentInfoDef
401
+ # 2. Array <em>namespaces</em> String namespaces
402
+ # - return
403
+ # - Array
404
+ #
405
+ def select_component_info_by_namespace(component_infos, namespaces)
406
+ return component_infos if namespaces.size == 0
407
+ reg_namespaces = namespaces.map {|v| /^#{v.gsub(/\./, '\.')}\./ }
408
+ return component_infos.select {|info|
409
+ next false if info.namespace.nil?
410
+ next true if namespaces.member?(info.namespace)
411
+ next true if nil != reg_namespaces.find {|reg| info.namespace.match(reg)}
412
+ next false
413
+ }
414
+ end
415
+
416
+ #
417
+ # - args
418
+ # 1. Proc <em>procedure</em> filter block
419
+ # - return
420
+ # - nil
421
+ #
422
+ def select(&procedure)
423
+ @selectors << procedure if not procedure.nil?
424
+ end
425
+
426
+ #
427
+ # - args
428
+ # 1. String|Symbol|Class|Module|Regexp <em>pattern</em>
429
+ # - return
430
+ # - nil
431
+ #
432
+ def include(pattern)
433
+ @include_patterns << pattern
434
+ end
435
+
436
+ #
437
+ # - args
438
+ # 1. String|Symbol|Class|Module|Regexp <em>pattern</em>
439
+ # - return
440
+ # - nil
441
+ #
442
+ def exclude(pattern)
443
+ @exclude_patterns << pattern
444
+ end
445
+ end
446
+ end
447
+ end
@@ -0,0 +1,41 @@
1
+ # -*- coding: utf-8 -*-
2
+ #--
3
+ # +----------------------------------------------------------------------+
4
+ # | Copyright 2005-2008 the Seasar Foundation and the Others. |
5
+ # +----------------------------------------------------------------------+
6
+ # | Licensed under the Apache License, Version 2.0 (the "License"); |
7
+ # | you may not use this file except in compliance with the License. |
8
+ # | You may obtain a copy of the License at |
9
+ # | |
10
+ # | http://www.apache.org/licenses/LICENSE-2.0 |
11
+ # | |
12
+ # | Unless required by applicable law or agreed to in writing, software |
13
+ # | distributed under the License is distributed on an "AS IS" BASIS, |
14
+ # | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
15
+ # | either express or implied. See the License for the specific language |
16
+ # | governing permissions and limitations under the License. |
17
+ # +----------------------------------------------------------------------+
18
+ #++
19
+ module Seasar
20
+ module Container
21
+ class S2ContainerComponentDef < SimpleComponentDef
22
+
23
+ # - args
24
+ # 1. Seasar::Container::S2Container <em>container</em>
25
+ # 2. Symbol|String <em>name</em>
26
+ def initialize(container, name)
27
+ super
28
+ end
29
+
30
+ #
31
+ # - args
32
+ # - none
33
+ # - return
34
+ # - Seasar::Container::S2Container
35
+ #
36
+ def get_container
37
+ return @component
38
+ end
39
+ end
40
+ end
41
+ end