waves 0.7.3 → 0.7.5
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/app/Rakefile +11 -19
- data/app/bin/waves-console +3 -5
- data/app/bin/waves-server +3 -5
- data/app/configurations/development.rb.erb +19 -11
- data/app/configurations/mapping.rb.erb +4 -5
- data/app/configurations/production.rb.erb +18 -13
- data/app/{doc/EMTPY → controllers/.gitignore} +0 -0
- data/app/{public/css/EMPTY → doc/.gitignore} +0 -0
- data/app/{public/flash/EMPTY → helpers/.gitignore} +0 -0
- data/app/lib/application.rb.erb +4 -51
- data/app/{public/images/EMPTY → lib/tasks/.gitignore} +0 -0
- data/app/{public/javascript/EMPTY → log/.gitignore} +0 -0
- data/app/{tmp/sessions/EMPTY → models/.gitignore} +0 -0
- data/app/public/css/.gitignore +0 -0
- data/app/public/flash/.gitignore +0 -0
- data/app/public/images/.gitignore +0 -0
- data/app/public/javascript/.gitignore +0 -0
- data/app/schema/migrations/.gitignore +0 -0
- data/app/startup.rb +5 -0
- data/app/templates/layouts/default.mab +2 -2
- data/app/tmp/sessions/.gitignore +0 -0
- data/app/views/.gitignore +0 -0
- data/bin/waves +38 -27
- data/bin/waves-console +3 -25
- data/bin/waves-server +4 -45
- data/lib/commands/waves-console.rb +21 -0
- data/lib/commands/waves-server.rb +55 -0
- data/lib/controllers/base.rb +11 -0
- data/lib/controllers/mixin.rb +130 -102
- data/lib/dispatchers/base.rb +65 -50
- data/lib/dispatchers/default.rb +79 -52
- data/lib/foundations/default.rb +26 -0
- data/lib/foundations/simple.rb +30 -0
- data/lib/helpers/common.rb +60 -56
- data/lib/helpers/default.rb +13 -0
- data/lib/helpers/form.rb +39 -38
- data/lib/helpers/formatting.rb +11 -11
- data/lib/helpers/model.rb +12 -12
- data/lib/helpers/view.rb +13 -13
- data/lib/layers/default_errors.rb +29 -0
- data/lib/layers/mvc.rb +58 -0
- data/lib/layers/orm/active_record.rb +41 -0
- data/lib/layers/orm/active_record/migrations/empty.rb.erb +9 -0
- data/lib/layers/orm/active_record/tasks/schema.rb +30 -0
- data/lib/layers/orm/data_mapper.rb +42 -0
- data/lib/layers/orm/filebase.rb +22 -0
- data/lib/layers/orm/migration.rb +70 -0
- data/lib/layers/orm/sequel.rb +82 -0
- data/lib/layers/orm/sequel/migrations/empty.rb.erb +9 -0
- data/lib/layers/orm/sequel/tasks/schema.rb +24 -0
- data/lib/layers/simple.rb +39 -0
- data/lib/layers/simple_errors.rb +26 -0
- data/lib/mapping/mapping.rb +222 -120
- data/lib/mapping/pretty_urls.rb +42 -41
- data/lib/renderers/erubis.rb +54 -31
- data/lib/renderers/markaby.rb +28 -28
- data/lib/renderers/mixin.rb +49 -52
- data/lib/runtime/application.rb +66 -48
- data/lib/runtime/blackboard.rb +57 -0
- data/lib/runtime/configuration.rb +117 -101
- data/lib/runtime/console.rb +19 -20
- data/lib/runtime/debugger.rb +9 -0
- data/lib/runtime/logger.rb +43 -37
- data/lib/runtime/mime_types.rb +19 -19
- data/lib/runtime/request.rb +72 -46
- data/lib/runtime/response.rb +37 -37
- data/lib/runtime/response_mixin.rb +26 -23
- data/lib/runtime/response_proxy.rb +25 -24
- data/lib/runtime/server.rb +99 -80
- data/lib/runtime/session.rb +63 -53
- data/lib/tasks/cluster.rb +26 -0
- data/lib/tasks/gem.rb +31 -0
- data/lib/tasks/generate.rb +80 -0
- data/lib/utilities/hash.rb +22 -0
- data/lib/utilities/inflect.rb +194 -0
- data/lib/utilities/integer.rb +15 -12
- data/lib/utilities/kernel.rb +32 -32
- data/lib/utilities/module.rb +11 -4
- data/lib/utilities/object.rb +5 -5
- data/lib/utilities/proc.rb +10 -0
- data/lib/utilities/string.rb +44 -38
- data/lib/utilities/symbol.rb +4 -4
- data/lib/views/base.rb +9 -0
- data/lib/views/mixin.rb +91 -89
- data/lib/waves.rb +29 -9
- metadata +52 -26
- data/app/configurations/default.rb.erb +0 -8
- data/app/controllers/default.rb.erb +0 -29
- data/app/helpers/default.rb.erb +0 -13
- data/app/lib/startup.rb.erb +0 -3
- data/app/lib/tasks/cluster.rb +0 -24
- data/app/lib/tasks/generate.rb +0 -15
- data/app/lib/tasks/schema.rb +0 -29
- data/app/models/default.rb.erb +0 -13
- data/app/schema/migrations/templates/empty.rb.erb +0 -9
- data/app/views/default.rb.erb +0 -13
data/lib/runtime/session.rb
CHANGED
@@ -1,56 +1,66 @@
|
|
1
1
|
module Waves
|
2
|
-
|
2
|
+
|
3
3
|
# Encapsulates the session associated with a given request. A session has an expiration
|
4
|
-
# and path
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
4
|
+
# and path, which must be provided in a Waves::Configuration. Sensible defaults are defined
|
5
|
+
# in Waves::Configurations::Default
|
6
|
+
class Session
|
7
|
+
|
8
|
+
# Concoct a (probably) unique session id
|
9
|
+
def self.generate_session_id
|
10
|
+
# from Camping ...
|
11
|
+
chars = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']
|
12
|
+
(0..48).inject(''){|s,x| s+=chars[ rand(chars.length) ] }
|
13
|
+
end
|
14
|
+
|
15
|
+
# Create a new session object using the given request. This is not necessarily the
|
16
|
+
# same as constructing a new session. The session_id cookie for the request domain
|
17
|
+
# is used to store a session id. The actual session data will be stored in a directory
|
18
|
+
# specified by the application's configuration file.
|
19
|
+
def initialize( request )
|
20
|
+
@request = request
|
21
|
+
@data ||= ( File.exist?( session_path ) ? load_session : {} )
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return the session data as a hash
|
25
|
+
def to_hash
|
26
|
+
@data.to_hash
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.base_path
|
30
|
+
Waves::Application.instance.config.session[:path]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Save the session data. You shouldn't typically have to call this directly, since it
|
34
|
+
# is called by Waves::Response#finish.
|
35
|
+
def save
|
36
|
+
if @data && @data.length > 0
|
37
|
+
File.write( session_path, @data.to_yaml )
|
38
|
+
@request.response.set_cookie( 'session_id',
|
39
|
+
:value => session_id, :path => '/',
|
40
|
+
:expires => Time.now + Waves::Server.config.session[:duration] )
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Access a given data element of the session using the given key.
|
45
|
+
def [](key) ; @data[key] ; end
|
46
|
+
# Set the given data element of the session using the given key and value.
|
47
|
+
def []=(key,val) ; @data[key] = val ; end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def session_id
|
52
|
+
@session_id ||= ( @request.cookies['session_id'] || Waves::Session.generate_session_id )
|
53
|
+
end
|
54
|
+
|
55
|
+
def session_path
|
56
|
+
Waves::Session.base_path / session_id
|
57
|
+
end
|
58
|
+
|
59
|
+
def load_session
|
60
|
+
YAML.load( File.read( session_path ) )
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
55
65
|
end
|
56
|
-
|
66
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
namespace :cluster do
|
2
|
+
|
3
|
+
desc 'Start a cluster of waves applications.'
|
4
|
+
task :start do |task|
|
5
|
+
using_waves_src = defined?(WAVES) || ENV['WAVES'] || File.exist( File.dirname(__FILE__) / :waves )
|
6
|
+
script = using_waves_src ? :bin / 'waves-server' : 'waves-server'
|
7
|
+
( Waves::Console.config.ports || [ Waves::Console.config.port ] ).each do |port|
|
8
|
+
cmd = "#{script} -p #{port} -c #{ENV['mode']||'development'} -d"
|
9
|
+
puts cmd ; `#{cmd}`
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'Stop a cluster of waves applications.'
|
14
|
+
task :stop do |task|
|
15
|
+
Dir[ :log / '*.pid' ].each do |pidfile|
|
16
|
+
pid = File.read(pidfile).to_i
|
17
|
+
puts "Stopping process #{pid} ..."
|
18
|
+
Process.kill( 'INT', pid ) rescue nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Restart a cluster of waves applications.'
|
23
|
+
task :restart => [ :stop, :start ] do |task|
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/lib/tasks/gem.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
namespace :gem do
|
2
|
+
desc "freeze a gem using gem=<gem name> [version=<gem version>]"
|
3
|
+
task :freeze do
|
4
|
+
raise "No gem specified" unless gem_name = ENV['gem']
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
Gem.manage_gems
|
8
|
+
|
9
|
+
gem = (version = ENV['version']) ?
|
10
|
+
Gem.cache.search(gem_name, "= #{version}").first :
|
11
|
+
Gem.cache.search(gem_name).sort_by { |g| g.version }.last
|
12
|
+
|
13
|
+
version ||= gem.version.version rescue nil
|
14
|
+
|
15
|
+
target_dir = File.join(Waves::Configurations::Default.root, 'gems')
|
16
|
+
mkdir_p target_dir
|
17
|
+
sh "gem install #{gem_name} --version #{version} -i #{target_dir} --no-rdoc --no-ri"
|
18
|
+
|
19
|
+
puts "Unpacked #{gem_name} #{version} to '#{target_dir}'"
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "unfreeze a gem using gem=<gem>"
|
23
|
+
task :unfreeze do
|
24
|
+
raise "No gem specified" unless gem_name = ENV['gem']
|
25
|
+
|
26
|
+
target_dir = File.join(Waves::Configurations::Default.root, 'gems')
|
27
|
+
ENV['GEM_HOME'] = target_dir # no install_dir option for gem uninstall
|
28
|
+
|
29
|
+
sh "gem uninstall #{gem_name}"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
namespace :generate do
|
2
|
+
|
3
|
+
desc 'Generate a new controller, with name=<name>'
|
4
|
+
task :controller do |task|
|
5
|
+
name = ENV['name']
|
6
|
+
controller_name = name.camel_case
|
7
|
+
raise "Cannot generate Default yet" if controller_name == 'Default'
|
8
|
+
|
9
|
+
filename = File.expand_path "controllers/#{name}.rb"
|
10
|
+
if File.exist?(filename)
|
11
|
+
$stderr.puts "#{filename} already exists"
|
12
|
+
exit
|
13
|
+
end
|
14
|
+
|
15
|
+
controller = <<TEXT
|
16
|
+
module #{Waves.application.name}
|
17
|
+
module Controllers
|
18
|
+
class #{controller_name} < Default
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
TEXT
|
24
|
+
|
25
|
+
File.write( filename, controller )
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'Generate new view, with name=<name>'
|
29
|
+
task :view do |task|
|
30
|
+
name = ENV['name']
|
31
|
+
view_name = name.camel_case
|
32
|
+
raise "Cannot generate Default yet" if view_name == 'Default'
|
33
|
+
|
34
|
+
filename = File.expand_path "views/#{name}.rb"
|
35
|
+
if File.exist?(filename)
|
36
|
+
$stderr.puts "#{filename} already exists"
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
|
40
|
+
view = <<TEXT
|
41
|
+
module #{Waves.application.name}
|
42
|
+
module Views
|
43
|
+
class #{view_name} < Default
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
TEXT
|
49
|
+
|
50
|
+
File.write( filename, view )
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'Generate a new helper, with name=<name>'
|
54
|
+
task :helper do |task|
|
55
|
+
name = ENV['name']
|
56
|
+
helper_name = name.camel_case
|
57
|
+
raise "Cannot generate Default yet" if helper_name == 'Default'
|
58
|
+
|
59
|
+
filename = File.expand_path "helpers/#{name}.rb"
|
60
|
+
if File.exist?(filename)
|
61
|
+
$stderr.puts "#{filename} already exists"
|
62
|
+
exit
|
63
|
+
end
|
64
|
+
|
65
|
+
helper = <<TEXT
|
66
|
+
module #{Waves.application.name}
|
67
|
+
module Helpers
|
68
|
+
module #{helper_name}
|
69
|
+
include Waves::Helpers::Default
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
TEXT
|
75
|
+
|
76
|
+
File.write( filename, helper )
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
def stringify_keys
|
4
|
+
inject({}) do |options, (key, value)|
|
5
|
+
options[key.to_s] = value
|
6
|
+
options
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Destructively convert all keys to symbols.
|
11
|
+
def symbolize_keys!
|
12
|
+
keys.each do |key|
|
13
|
+
unless key.is_a?(Symbol)
|
14
|
+
self[key.to_sym] = self[key]
|
15
|
+
delete(key)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
# Much love to Facets (more specifically English) for this module
|
2
|
+
# http://english.rubyforge.org/
|
3
|
+
# changed slightly in the hopes of one day implementing a different set
|
4
|
+
# of rules for different languages
|
5
|
+
# NOTE: this is NOT implemented yet.
|
6
|
+
# plural and singular work directly with the English class
|
7
|
+
|
8
|
+
module Inflect # :nodoc:
|
9
|
+
module InflectorMethods
|
10
|
+
|
11
|
+
# Define a general exception.
|
12
|
+
def word(singular, plural=nil)
|
13
|
+
plural = singular unless plural
|
14
|
+
singular_word(singular, plural)
|
15
|
+
plural_word(singular, plural)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Define a singularization exception.
|
19
|
+
def singular_word(singular, plural)
|
20
|
+
@singular_of[plural] = singular
|
21
|
+
end
|
22
|
+
|
23
|
+
# Define a pluralization exception.
|
24
|
+
def plural_word(singular, plural)
|
25
|
+
@plural_of[singular] = plural
|
26
|
+
end
|
27
|
+
|
28
|
+
# Define a general rule.
|
29
|
+
def rule(singular, plural)
|
30
|
+
singular_rule(singular, plural)
|
31
|
+
plural_rule(singular, plural)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Define a singularization rule.
|
35
|
+
def singular_rule(singular, plural)
|
36
|
+
@singular_rules << [singular, plural]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Define a plurualization rule.
|
40
|
+
def plural_rule(singular, plural)
|
41
|
+
@plural_rules << [singular, plural]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Read prepared singularization rules.
|
45
|
+
def singularization_rules
|
46
|
+
return @singularization_rules if @singularization_rules
|
47
|
+
sorted = @singular_rules.sort_by{ |s, p| "#{p}".size }.reverse
|
48
|
+
@singularization_rules = sorted.collect do |s, p|
|
49
|
+
[ /#{p}$/, "#{s}" ]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Read prepared pluralization rules.
|
54
|
+
def pluralization_rules
|
55
|
+
return @pluralization_rules if @pluralization_rules
|
56
|
+
sorted = @plural_rules.sort_by{ |s, p| "#{s}".size }.reverse
|
57
|
+
@pluralization_rules = sorted.collect do |s, p|
|
58
|
+
[ /#{s}$/, "#{p}" ]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
def plural_of
|
64
|
+
@plural_of
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
def singular_of
|
69
|
+
@singular_of
|
70
|
+
end
|
71
|
+
|
72
|
+
# Convert an English word from plurel to singular.
|
73
|
+
#
|
74
|
+
# "boys".singular #=> boy
|
75
|
+
# "tomatoes".singular #=> tomato
|
76
|
+
#
|
77
|
+
def singular(word)
|
78
|
+
if result = singular_of[word]
|
79
|
+
return result.dup
|
80
|
+
end
|
81
|
+
result = word.dup
|
82
|
+
singularization_rules.each do |(match, replacement)|
|
83
|
+
break if result.gsub!(match, replacement)
|
84
|
+
end
|
85
|
+
return result
|
86
|
+
end
|
87
|
+
|
88
|
+
# Convert an English word from singular to plurel.
|
89
|
+
#
|
90
|
+
# "boy".plural #=> boys
|
91
|
+
# "tomato".plural #=> tomatoes
|
92
|
+
#
|
93
|
+
def plural(word)
|
94
|
+
if result = plural_of[word]
|
95
|
+
return result.dup
|
96
|
+
end
|
97
|
+
#return self.dup if /s$/ =~ self # ???
|
98
|
+
result = word.dup
|
99
|
+
pluralization_rules.each do |(match, replacement)|
|
100
|
+
break if result.gsub!(match, replacement)
|
101
|
+
end
|
102
|
+
return result
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class English # :nodoc:
|
107
|
+
@singular_of = {}
|
108
|
+
@plural_of = {}
|
109
|
+
|
110
|
+
@singular_rules = []
|
111
|
+
@plural_rules = []
|
112
|
+
|
113
|
+
class << self
|
114
|
+
include InflectorMethods
|
115
|
+
end
|
116
|
+
|
117
|
+
# One argument means singular and plural are the same.
|
118
|
+
word 'equipment'
|
119
|
+
word 'information'
|
120
|
+
word 'money'
|
121
|
+
word 'species'
|
122
|
+
word 'series'
|
123
|
+
word 'fish'
|
124
|
+
word 'sheep'
|
125
|
+
word 'moose'
|
126
|
+
word 'hovercraft'
|
127
|
+
|
128
|
+
# Two arguments defines a singular and plural exception.
|
129
|
+
word 'Swiss' , 'Swiss'
|
130
|
+
word 'life' , 'lives'
|
131
|
+
word 'wife' , 'wives'
|
132
|
+
word 'virus' , 'viri'
|
133
|
+
word 'octopus' , 'octopi'
|
134
|
+
word 'cactus' , 'cacti'
|
135
|
+
word 'goose' , 'geese'
|
136
|
+
word 'criterion' , 'criteria'
|
137
|
+
word 'alias' , 'aliases'
|
138
|
+
word 'status' , 'statuses'
|
139
|
+
word 'axis' , 'axes'
|
140
|
+
word 'crisis' , 'crises'
|
141
|
+
word 'testis' , 'testes'
|
142
|
+
word 'child' , 'children'
|
143
|
+
word 'person' , 'people'
|
144
|
+
word 'potato' , 'potatoes'
|
145
|
+
word 'tomato' , 'tomatoes'
|
146
|
+
word 'buffalo' , 'buffaloes'
|
147
|
+
word 'torpedo' , 'torpedoes'
|
148
|
+
word 'quiz' , 'quizes'
|
149
|
+
word 'matrix' , 'matrices'
|
150
|
+
word 'vertex' , 'vetices'
|
151
|
+
word 'index' , 'indices'
|
152
|
+
word 'ox' , 'oxen'
|
153
|
+
word 'mouse' , 'mice'
|
154
|
+
word 'louse' , 'lice'
|
155
|
+
word 'thesis' , 'theses'
|
156
|
+
word 'thief' , 'thieves'
|
157
|
+
word 'analysis' , 'analyses'
|
158
|
+
|
159
|
+
# One-way singularization exception (convert plural to singular).
|
160
|
+
singular_word 'cactus', 'cacti'
|
161
|
+
|
162
|
+
# General rules.
|
163
|
+
rule 'hive' , 'hives'
|
164
|
+
rule 'rf' , 'rves'
|
165
|
+
rule 'af' , 'aves'
|
166
|
+
rule 'ero' , 'eroes'
|
167
|
+
rule 'man' , 'men'
|
168
|
+
rule 'ch' , 'ches'
|
169
|
+
rule 'sh' , 'shes'
|
170
|
+
rule 'ss' , 'sses'
|
171
|
+
rule 'ta' , 'tum'
|
172
|
+
rule 'ia' , 'ium'
|
173
|
+
rule 'ra' , 'rum'
|
174
|
+
rule 'ay' , 'ays'
|
175
|
+
rule 'ey' , 'eys'
|
176
|
+
rule 'oy' , 'oys'
|
177
|
+
rule 'uy' , 'uys'
|
178
|
+
rule 'y' , 'ies'
|
179
|
+
rule 'x' , 'xes'
|
180
|
+
rule 'lf' , 'lves'
|
181
|
+
rule 'us' , 'uses'
|
182
|
+
rule '' , 's'
|
183
|
+
|
184
|
+
# One-way singular rules.
|
185
|
+
singular_rule 'of' , 'ofs' # proof
|
186
|
+
singular_rule 'o' , 'oes' # hero, heroes
|
187
|
+
singular_rule 'f' , 'ves'
|
188
|
+
|
189
|
+
# One-way plural rules.
|
190
|
+
plural_rule 'fe' , 'ves' # safe, wife
|
191
|
+
plural_rule 's' , 'ses'
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|