waves 0.7.3 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/app/Rakefile +11 -19
  2. data/app/bin/waves-console +3 -5
  3. data/app/bin/waves-server +3 -5
  4. data/app/configurations/development.rb.erb +19 -11
  5. data/app/configurations/mapping.rb.erb +4 -5
  6. data/app/configurations/production.rb.erb +18 -13
  7. data/app/{doc/EMTPY → controllers/.gitignore} +0 -0
  8. data/app/{public/css/EMPTY → doc/.gitignore} +0 -0
  9. data/app/{public/flash/EMPTY → helpers/.gitignore} +0 -0
  10. data/app/lib/application.rb.erb +4 -51
  11. data/app/{public/images/EMPTY → lib/tasks/.gitignore} +0 -0
  12. data/app/{public/javascript/EMPTY → log/.gitignore} +0 -0
  13. data/app/{tmp/sessions/EMPTY → models/.gitignore} +0 -0
  14. data/app/public/css/.gitignore +0 -0
  15. data/app/public/flash/.gitignore +0 -0
  16. data/app/public/images/.gitignore +0 -0
  17. data/app/public/javascript/.gitignore +0 -0
  18. data/app/schema/migrations/.gitignore +0 -0
  19. data/app/startup.rb +5 -0
  20. data/app/templates/layouts/default.mab +2 -2
  21. data/app/tmp/sessions/.gitignore +0 -0
  22. data/app/views/.gitignore +0 -0
  23. data/bin/waves +38 -27
  24. data/bin/waves-console +3 -25
  25. data/bin/waves-server +4 -45
  26. data/lib/commands/waves-console.rb +21 -0
  27. data/lib/commands/waves-server.rb +55 -0
  28. data/lib/controllers/base.rb +11 -0
  29. data/lib/controllers/mixin.rb +130 -102
  30. data/lib/dispatchers/base.rb +65 -50
  31. data/lib/dispatchers/default.rb +79 -52
  32. data/lib/foundations/default.rb +26 -0
  33. data/lib/foundations/simple.rb +30 -0
  34. data/lib/helpers/common.rb +60 -56
  35. data/lib/helpers/default.rb +13 -0
  36. data/lib/helpers/form.rb +39 -38
  37. data/lib/helpers/formatting.rb +11 -11
  38. data/lib/helpers/model.rb +12 -12
  39. data/lib/helpers/view.rb +13 -13
  40. data/lib/layers/default_errors.rb +29 -0
  41. data/lib/layers/mvc.rb +58 -0
  42. data/lib/layers/orm/active_record.rb +41 -0
  43. data/lib/layers/orm/active_record/migrations/empty.rb.erb +9 -0
  44. data/lib/layers/orm/active_record/tasks/schema.rb +30 -0
  45. data/lib/layers/orm/data_mapper.rb +42 -0
  46. data/lib/layers/orm/filebase.rb +22 -0
  47. data/lib/layers/orm/migration.rb +70 -0
  48. data/lib/layers/orm/sequel.rb +82 -0
  49. data/lib/layers/orm/sequel/migrations/empty.rb.erb +9 -0
  50. data/lib/layers/orm/sequel/tasks/schema.rb +24 -0
  51. data/lib/layers/simple.rb +39 -0
  52. data/lib/layers/simple_errors.rb +26 -0
  53. data/lib/mapping/mapping.rb +222 -120
  54. data/lib/mapping/pretty_urls.rb +42 -41
  55. data/lib/renderers/erubis.rb +54 -31
  56. data/lib/renderers/markaby.rb +28 -28
  57. data/lib/renderers/mixin.rb +49 -52
  58. data/lib/runtime/application.rb +66 -48
  59. data/lib/runtime/blackboard.rb +57 -0
  60. data/lib/runtime/configuration.rb +117 -101
  61. data/lib/runtime/console.rb +19 -20
  62. data/lib/runtime/debugger.rb +9 -0
  63. data/lib/runtime/logger.rb +43 -37
  64. data/lib/runtime/mime_types.rb +19 -19
  65. data/lib/runtime/request.rb +72 -46
  66. data/lib/runtime/response.rb +37 -37
  67. data/lib/runtime/response_mixin.rb +26 -23
  68. data/lib/runtime/response_proxy.rb +25 -24
  69. data/lib/runtime/server.rb +99 -80
  70. data/lib/runtime/session.rb +63 -53
  71. data/lib/tasks/cluster.rb +26 -0
  72. data/lib/tasks/gem.rb +31 -0
  73. data/lib/tasks/generate.rb +80 -0
  74. data/lib/utilities/hash.rb +22 -0
  75. data/lib/utilities/inflect.rb +194 -0
  76. data/lib/utilities/integer.rb +15 -12
  77. data/lib/utilities/kernel.rb +32 -32
  78. data/lib/utilities/module.rb +11 -4
  79. data/lib/utilities/object.rb +5 -5
  80. data/lib/utilities/proc.rb +10 -0
  81. data/lib/utilities/string.rb +44 -38
  82. data/lib/utilities/symbol.rb +4 -4
  83. data/lib/views/base.rb +9 -0
  84. data/lib/views/mixin.rb +91 -89
  85. data/lib/waves.rb +29 -9
  86. metadata +52 -26
  87. data/app/configurations/default.rb.erb +0 -8
  88. data/app/controllers/default.rb.erb +0 -29
  89. data/app/helpers/default.rb.erb +0 -13
  90. data/app/lib/startup.rb.erb +0 -3
  91. data/app/lib/tasks/cluster.rb +0 -24
  92. data/app/lib/tasks/generate.rb +0 -15
  93. data/app/lib/tasks/schema.rb +0 -29
  94. data/app/models/default.rb.erb +0 -13
  95. data/app/schema/migrations/templates/empty.rb.erb +0 -9
  96. data/app/views/default.rb.erb +0 -13
@@ -1,14 +1,17 @@
1
1
  # Added methods to Integer for commonly used "numeric phrases" like 6.days or 30.megabytes.
2
2
  class Integer
3
- def seconds ; self ; end
4
- def minutes ; self * 60 ; end
5
- def hours ; self * 60.minutes ; end
6
- def days ; self * 24.hours ; end
7
- def weeks ; self * 7.days ; end
8
- def bytes ; self ; end
9
- def kilobytes ; self * 1024 ; end
10
- def megabytes ; self * 1024.kilobytes ; end
11
- def gigabytes ; self * 1024.megabytes ; end
12
- def terabytes ; self * 1024.gigabytes ; end
13
- def petabytes ; self * 1024.gigabytes ; end
14
- end
3
+ def seconds ; self ; end
4
+ def minutes ; self * 60 ; end
5
+ def hours ; self * 60.minutes ; end
6
+ def days ; self * 24.hours ; end
7
+ def weeks ; self * 7.days ; end
8
+ def bytes ; self ; end
9
+ def kilobytes ; self * 1024 ; end
10
+ def megabytes ; self * 1024.kilobytes ; end
11
+ def gigabytes ; self * 1024.megabytes ; end
12
+ def terabytes ; self * 1024.gigabytes ; end
13
+ def petabytes ; self * 1024.terabytes ; end
14
+ def exabytes ; self * 1024.petabytes ; end
15
+ def zettabytes ; self * 1024.exabytes ; end
16
+ def yottabytes ; self * 1024.zettabytes ; end
17
+ end
@@ -1,34 +1,34 @@
1
1
  module Kernel
2
- #
3
- # From Rails. This comes in handy when you want to return a value that you need to modify.
4
- # So instead of the awkward:
5
- #
6
- # foo = Foo.new
7
- # foo.bar = 'bar'
8
- # foo
9
- #
10
- # You can just say
11
- #
12
- # returning Foo.new { |foo| foo.bar = 'bar' }
13
- #
14
- def returning( object, &block )
15
- yield object; object
16
- end
17
-
18
- #
19
- # Inspired by a question on comp.lang.ruby. Sort of like returning, except
20
- # you don't need to pass in the returnee as an argument. The drawback is that,
21
- # since this relies on instance_eval, you can't access in methods in the local
22
- # scope. You can work around this by assigning values to variables, but in that
23
- # case, you might be better off using returning.
24
- #
25
- # Our above example would look like this:
26
- #
27
- # with( Foo.new ) { bar = 'bar' }
28
- #
29
- def with( object, &block )
30
- object.instance_eval(&block); object
31
- end
32
-
2
+ #
3
+ # From Rails. This comes in handy when you want to return a value that you need to modify.
4
+ # So instead of the awkward:
5
+ #
6
+ # foo = Foo.new
7
+ # foo.bar = 'bar'
8
+ # foo
9
+ #
10
+ # You can just say
11
+ #
12
+ # returning Foo.new { |foo| foo.bar = 'bar' }
13
+ #
14
+ def returning( object, &block )
15
+ yield object; object
16
+ end
33
17
 
34
- end
18
+ #
19
+ # Inspired by a question on comp.lang.ruby. Sort of like returning, except
20
+ # you don't need to pass in the returnee as an argument. The drawback is that,
21
+ # since this relies on instance_eval, you can't access in methods in the local
22
+ # scope. You can work around this by assigning values to variables, but in that
23
+ # case, you might be better off using returning.
24
+ #
25
+ # Our above example would look like this:
26
+ #
27
+ # with( Foo.new ) { bar = 'bar' }
28
+ #
29
+ def with( object, &block )
30
+ object.instance_eval(&block); object
31
+ end
32
+
33
+
34
+ end
@@ -1,10 +1,17 @@
1
1
  class Module
2
2
 
3
3
  # This comes in handy when you are trying to do meta-programming with modules / classes
4
- # that may be nested within other modules / classes. I think I've seen it defined in
4
+ # that may be nested within other modules / classes. I think I've seen it defined in
5
5
  # facets, but I'm not relying on facets just for this one method.
6
- def basename
7
- self.name.split('::').last || ''
8
- end
6
+ def basename
7
+ self.name.split('::').last || ''
8
+ end
9
+
10
+ # Just a convenience method for accessing a const within a Module
11
+ def []( cname )
12
+ const_get( cname.to_s.camel_case )
13
+ end
14
+
15
+
9
16
 
10
17
  end
@@ -1,9 +1,9 @@
1
1
  class Object
2
- # This is an extremely powerful little function that will be built-in to Ruby 1.9.
3
- # This version is from Mauricio Fernandez via ruby-talk. Works like instance_eval
4
- # except that you can pass parameters to the block. This means you can define a block
5
- # intended for use with instance_eval, pass it to another method, which can then
6
- # invoke with parameters. This is used quite a bit by the Waves::Mapping code.
2
+ # This is an extremely powerful little function that will be built-in to Ruby 1.9.
3
+ # This version is from Mauricio Fernandez via ruby-talk. Works like instance_eval
4
+ # except that you can pass parameters to the block. This means you can define a block
5
+ # intended for use with instance_eval, pass it to another method, which can then
6
+ # invoke with parameters. This is used quite a bit by the Waves::Mapping code.
7
7
  def instance_exec(*args, &block)
8
8
  mname = "__instance_exec_#{Thread.current.object_id.abs}"
9
9
  class << self; self end.class_eval{ define_method(mname, &block) }
@@ -0,0 +1,10 @@
1
+ class Proc
2
+
3
+ # calls the given lambda with the receiver as its argument
4
+ def |(lambda)
5
+ lambda do
6
+ lambda.call( self.call )
7
+ end
8
+ end
9
+
10
+ end
@@ -1,41 +1,47 @@
1
1
  # Waves extends String with a variety of methods for changing from singular to plural and back, and switching to different types of case and word separators. These methods are similar to those found in Rails and other frameworks, but some (all?) of the names are different. The names here were chosen for increased clarity and are hopefully easy to adjust to ...
2
- #
3
- # Notably, the inflector code here is not as comprehensive as the Rails code. This will be fixed in a future version of Waves.
4
2
 
5
3
  class String
6
-
7
- # Does a File.join on the two arguments joined by the /. Very handy
8
- # for doing platform-safe paths without having to use File.join.
9
- #
10
- # I unfortunately don't recall where i first saw this ... see
11
- # Symbol extension as well, allowing for :files / 'afilename.txt'
12
-
13
- def / ( string )
14
- File.join(self,string.to_s)
15
- end
16
-
17
- def singular
18
- gsub(/ies$/,'y').gsub(/es$/,'').gsub(/s$/,'')
19
- end
20
-
21
- def plural
22
- gsub(/(s|sh|ch)$/,'\1es').gsub(/(i|y)$/,'ies').gsub(/([^s])$/,'\1s')
23
- end
24
-
25
- def camel_case
26
- gsub(/(_)(\w)/) { $2.upcase }.gsub(/^([a-z])/) { $1.upcase }
27
- end
28
-
29
- def snake_case
30
- gsub(/([a-z\d])([A-Z])/){ "#{$1}_#{$2}"}.tr("-", "_").downcase
31
- end
32
-
33
- def title_case
34
- gsub(/(^|\s)\s*([a-z])/) { $1 + $2.upcase }
35
- end
36
-
37
- def text
38
- gsub(/[\_\-\.\:]/,' ')
39
- end
40
-
41
- end
4
+
5
+ # Does a File.join on the two arguments joined by the /. Very handy
6
+ # for doing platform-safe paths without having to use File.join.
7
+ #
8
+ # I unfortunately don't recall where i first saw this ... see
9
+ # Symbol extension as well, allowing for :files / 'afilename.txt'
10
+
11
+ def / ( string )
12
+ File.join(self,string.to_s)
13
+ end
14
+
15
+ def singular
16
+ Inflect::English.singular(self)
17
+ end
18
+
19
+ alias_method(:singularize, :singular)
20
+
21
+ def plural
22
+ Inflect::English.plural(self)
23
+ end
24
+
25
+ alias_method(:pluralize, :plural)
26
+
27
+ def lower_camel_case
28
+ gsub(/(_)(\w)/) { $2.upcase }
29
+ end
30
+
31
+ def camel_case
32
+ lower_camel_case.gsub(/^([a-z])/) { $1.upcase }
33
+ end
34
+
35
+ def snake_case
36
+ gsub(/\s+/,'').gsub(/([a-z\d])([A-Z])/){ "#{$1}_#{$2}"}.tr("-", "_").downcase
37
+ end
38
+
39
+ def title_case
40
+ gsub(/(^|\s)\s*([a-z])/) { $1 + $2.upcase }
41
+ end
42
+
43
+ def text
44
+ gsub(/[\_\-\.\:]/,' ')
45
+ end
46
+
47
+ end
@@ -1,7 +1,7 @@
1
1
  class Symbol
2
2
  # Does File.join on self and string, converting self to string before invocation.
3
3
  # See String#/ for more.
4
- def / ( string )
5
- self.to_s / string
6
- end
7
- end
4
+ def / ( string )
5
+ self.to_s / string
6
+ end
7
+ end
data/lib/views/base.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Waves
2
+ module Views
3
+ class Base
4
+
5
+ include Waves::Views::Mixin
6
+
7
+ end
8
+ end
9
+ end
data/lib/views/mixin.rb CHANGED
@@ -1,44 +1,44 @@
1
1
  module Waves
2
-
3
- # Views in Waves are ultimately responsible for generating the response body. Views mirror controllers - both have full access to the request and response, and both may modify the response and even short-circuit the request processing and return a result by calling redirect or calling Response#finish.
4
- #
5
- # Views, like controllers, are classes with methods that are invoked by a mapping block (see Waves::Mapping). View instance methods take an assigns hash that are typically converted into instance variables accesible from within a template.
6
- #
7
- # Like controllers, a default implementation is provided by the +waves+ command when you first create your application. This default can be overridden to change the behaviors for all views, or you can explicitly define a View class to provide specific behavior for one view.
8
- #
9
- # The default implementation simply determines which template to render and renders it, returning the result as a string. This machinery is provided by the View mixin, so it is easy to create your own View implementations.
10
- #
11
- # = Templates
12
- #
13
- # Although you won't typically need to modify or define View classes, you will often create and modify templates. Templates can be evaluated using any registered Renderer. Two are presently packaged with Waves: Markaby and Erubis. Templates have access to the assigns hash passed to the view method as instance variables. These can be explicitly defined by the mapping file or whomever is invoking the view.
14
- #
15
- # *Example*
16
- #
17
- # # Find the story named 'home' and pass it into the view template as @story
18
- # use( :story ) | controller { find( 'home' ) } | view { |x| show( :story => x ) }
19
- #
20
- # = Helpers
21
- #
22
- # Helper methods can be defined for any view template by simply defining them within the default Helper module in <tt>helpers/default.rb</tt> of the generated application. Helpers specific to a particular View class can be explicitly defined by creating a helper module that corresponds to the View class. For examples, for the +User+ View class, you would define a helper module in <tt>user.rb</tt> named +User+.
23
- #
24
- # The default helper class initially includes a wide-variety of helpers, including helpers for layouts, Textile formatting, rendering forms, and nested views, as well as helpers for accessing the request and response objects. More helpers will be added in future releases, but in many cases, there is no need to include all of them in your application.
25
- #
26
- # = Layouts
27
- #
28
- # Layouts are defined using the +Layout+ view class, and layout templates are placed in the +layout+ directory of the +templates+ directory. Layouts are explicitly set within a template using the +layout+ method.
29
- #
30
- # *Example*
31
- #
32
- # layout :default, :title => @story.title do
33
- # h1 @story.title
34
- # textile @story.content
35
- # end
36
- #
37
- #
38
- # The layout method takes a name and an assigns hash that will be available within the layout template as instance variables. In this example, <tt>@title</tt> will be defined as <tt>@story.title</tt> within the layout template named 'default.'
39
- #
40
- # Any number of layouts may be included within a single view, and layouts may even be nested within layouts. This makes it possible to create large numbers of highly structured views that can be easily changed with minimal effort. For example, you might a specific layout associated with form elements. By incorporating this into your views as a +layout+ template, you can make changes across all your forms by changing this single template.
41
- #
2
+
3
+ # Views in Waves are ultimately responsible for generating the response body. Views mirror controllers - both have full access to the request and response, and both may modify the response and even short-circuit the request processing and return a result by calling redirect or calling Response#finish.
4
+ #
5
+ # Views, like controllers, are classes with methods that are invoked by a mapping block (see Waves::Mapping). View instance methods take an assigns hash that are typically converted into instance variables accesible from within a template.
6
+ #
7
+ # Like controllers, a default implementation is provided by the +waves+ command when you first create your application. This default can be overridden to change the behaviors for all views, or you can explicitly define a View class to provide specific behavior for one view.
8
+ #
9
+ # The default implementation simply determines which template to render and renders it, returning the result as a string. This machinery is provided by the View mixin, so it is easy to create your own View implementations.
10
+ #
11
+ # = Templates
12
+ #
13
+ # Although you won't typically need to modify or define View classes, you will often create and modify templates. Templates can be evaluated using any registered Renderer. Two are presently packaged with Waves: Markaby and Erubis. Templates have access to the assigns hash passed to the view method as instance variables. These can be explicitly defined by the mapping file or whomever is invoking the view.
14
+ #
15
+ # *Example*
16
+ #
17
+ # # Find the story named 'home' and pass it as @story into the "story/show" template
18
+ # use( :story ) | controller { find( 'home' ) } | view { |x| show( :story => x ) }
19
+ #
20
+ # = Helpers
21
+ #
22
+ # Helper methods can be defined for any view template by simply defining them within the default Helper module in <tt>helpers/default.rb</tt> of the generated application. Helpers specific to a particular View class can be explicitly defined by creating a helper module that corresponds to the View class. For examples, for the +User+ View class, you would define a helper module in <tt>user.rb</tt> named +User+.
23
+ #
24
+ # The default helper class initially includes a wide-variety of helpers, including helpers for layouts, Textile formatting, rendering forms, and nested views, as well as helpers for accessing the request and response objects. More helpers will be added in future releases, but in many cases, there is no need to include all of them in your application.
25
+ #
26
+ # = Layouts
27
+ #
28
+ # Layouts are defined using the +Layout+ view class, and layout templates are placed in the +layout+ directory of the +templates+ directory. Layouts are explicitly set within a template using the +layout+ method.
29
+ #
30
+ # *Example*
31
+ #
32
+ # layout :default, :title => @story.title do
33
+ # h1 @story.title
34
+ # textile @story.content
35
+ # end
36
+ #
37
+ #
38
+ # The layout method takes a name and an assigns hash that will be available within the layout template as instance variables. In this example, <tt>@title</tt> will be defined as <tt>@story.title</tt> within the layout template named 'default.'
39
+ #
40
+ # Any number of layouts may be included within a single view, and layouts may even be nested within layouts. This makes it possible to create large numbers of highly structured views that can be easily changed with minimal effort. For example, you might specify a layout associated with form elements. By incorporating this into your views as a +layout+ template, you can make changes across all your forms by changing this single template.
41
+ #
42
42
  # = Nested Views
43
43
  #
44
44
  # It is easy to include one view inside another. A common use for this is to define one set of templates for reusable content 'fragments' (somewhat akin to partials in Rails) and another set that incorporate these fragments into specific layouts. Including a view from within another view is done, logically enough, using the +view+ method.
@@ -52,57 +52,59 @@ module Waves
52
52
  #
53
53
  # As always, the request and response objects, and a wide-variety of short-cut methods, are available within view templates via the Waves::ResponseMixin.
54
54
  #
55
-
56
- module Views
57
55
 
58
- class NoTemplateError < Exception ; end
59
-
60
- # A class method that returns the known Renderers, which is any module that is defined within Waves::Renderers and includes the Renderers::Mixin. You can define new Renderers simply be reopening Waves::Renderers and defining a module that mixes in Renderers::Mixin.
61
- def Views.renderers
62
- return [] if Renderers.constants.nil?
63
- Renderers.constants.inject([]) do |rx,cname|
64
- ( Module === (c=Renderers.const_get(cname)) &&
65
- c < Renderers::Mixin ) ? ( rx << c ) : rx
66
- end
67
- end
56
+ module Views
57
+
58
+ class NoTemplateError < Exception ; end
59
+
60
+ # A class method that returns the known Renderers, which is any module that is defined within Waves::Renderers and includes the Renderers::Mixin. You can define new Renderers simply be reopening Waves::Renderers and defining a module that mixes in Renderers::Mixin.
61
+ def Views.renderers
62
+ return [] if Renderers.constants.nil?
63
+ Renderers.constants.sort.inject([]) do |rx,cname|
64
+ ( Module === (c=Renderers.const_get(cname)) &&
65
+ c < Renderers::Mixin ) ? ( rx << c ) : rx
66
+ end
67
+ end
68
+
69
+ # The View mixin simply sets up the machinery for invoking a template, along with methods for accessing the request context and the standard interface for invoking a view method.
70
+ module Mixin
71
+
72
+ attr_reader :request
73
+
74
+ include Waves::ResponseMixin
75
+
76
+ def self.included( c )
77
+ def c.process( request, *args, &block )
78
+ self.new( request ).instance_exec( *args, &block )
79
+ end
80
+ end
81
+
82
+ def initialize( request )
83
+ @request = request
84
+ @layout = :default
85
+ end
86
+
87
+ # Return the first renderer for which a template file can be found.
88
+ # Uses Renderers::Mixin.filename to construct the filename for each renderer.
89
+ def renderer(path)
90
+ Views.renderers.find do |renderer|
91
+ File.exists?( renderer.filename( path ) )
92
+ end
93
+ end
94
+
95
+ def render( path, context = {} )
96
+ context.merge!( :request => request )
97
+ template = renderer( path ) || renderer( :generic / File.basename(path) )
98
+ raise NoTemplateError.new( path ) if template.nil?
99
+ template.render( path, context )
100
+ end
101
+
102
+ def method_missing(name,*args)
103
+ render( "/#{self.class.basename.snake_case}/#{name}", *args )
104
+ end
68
105
 
69
- # The View mixin simply sets up the machinery for invoking a template, along with methods for accessing the request context and the standard interface for invoking a view method.
70
- module Mixin
71
-
72
- attr_reader :request
73
-
74
- include Waves::ResponseMixin
75
-
76
- def self.included( c )
77
- def c.process( request, *args, &block )
78
- self.new( request ).instance_exec( *args, &block )
79
- end
80
- end
81
-
82
- def initialize( request )
83
- @request = request
84
- @layout = :default
85
- end
86
-
87
- def renderer(path)
88
- Views.renderers.find do |renderer|
89
- File.exists?( renderer.filename( path ) )
90
- end
91
- end
92
-
93
- def render( path, context = {} )
94
- context.merge!( :request => request )
95
- template = renderer( path ) || renderer( :generic / File.basename(path) )
96
- raise NoTemplateError.new( path ) if template.nil?
97
- template.render( path, context )
98
- end
99
-
100
- def method_missing(name,*args)
101
- render( "/#{self.class.basename.snake_case}/#{name}", *args )
102
- end
103
-
104
- end
106
+ end
105
107
 
106
- end
108
+ end
107
109
 
108
110
  end