diagnostics 0.0.1 → 0.0.2
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/.gitignore +4 -4
- data/.specification +0 -0
- data/Gemfile.lock +39 -0
- data/LICENSE +20 -0
- data/README.md +7 -0
- data/app/controllers/diagnostics_application_controller.rb +12 -0
- data/app/controllers/diagnostics_controller.rb +6 -0
- data/app/controllers/statuses_controller.rb +20 -0
- data/app/views/diagnostics/_check.html.erb +1 -0
- data/app/views/diagnostics/index.html.erb +22 -0
- data/app/views/layouts/diagnostics.html.erb +114 -0
- data/app/views/statuses/show.html.erb +15 -0
- data/config/routes.rb +6 -0
- data/diagnostics.gemspec +2 -2
- data/features/diagnostic_checks.feature +167 -0
- data/features/step_definitions/aruba.rb +5 -0
- data/features/support/env.rb +2 -0
- data/lib/diagnostics/check.rb +66 -0
- data/lib/diagnostics/data/attribute.rb +15 -0
- data/lib/diagnostics/data/elements.rb +40 -0
- data/lib/diagnostics/data/list.rb +15 -0
- data/lib/diagnostics/data/table.rb +47 -0
- data/lib/diagnostics/data/text.rb +15 -0
- data/lib/diagnostics/data_group.rb +37 -0
- data/lib/diagnostics/methods.rb +46 -0
- data/lib/diagnostics/version.rb +1 -1
- data/rails/init.rb +1 -0
- data/spec/diagnostics/check_spec.rb +78 -0
- data/spec/diagnostics/data/attribute_spec.rb +22 -0
- data/spec/diagnostics/data/list_spec.rb +22 -0
- data/spec/diagnostics/data/table_spec.rb +63 -0
- data/spec/diagnostics/data/text_spec.rb +22 -0
- data/spec/diagnostics/data_group_spec.rb +177 -0
- data/spec/diagnostics/methods_spec.rb +128 -0
- data/spec/diagnostics_spec.rb +11 -0
- data/spec/spec_helper.rb +13 -0
- data/spec_rails/rails2/Gemfile +13 -0
- data/spec_rails/rails2/Gemfile.lock +91 -0
- data/spec_rails/rails2/Rakefile +13 -0
- data/spec_rails/rails2/app_root/README +243 -0
- data/spec_rails/rails2/app_root/Rakefile +10 -0
- data/spec_rails/rails2/app_root/app/controllers/application_controller.rb +10 -0
- data/spec_rails/rails2/app_root/app/helpers/application_helper.rb +3 -0
- data/spec_rails/rails2/app_root/config/boot.rb +114 -0
- data/spec_rails/rails2/app_root/config/database.yml +25 -0
- data/spec_rails/rails2/app_root/config/environment.rb +45 -0
- data/spec_rails/rails2/app_root/config/environments/cucumber.rb +29 -0
- data/spec_rails/rails2/app_root/config/environments/development.rb +17 -0
- data/spec_rails/rails2/app_root/config/environments/production.rb +28 -0
- data/spec_rails/rails2/app_root/config/environments/test.rb +28 -0
- data/spec_rails/rails2/app_root/config/initializers/backtrace_silencers.rb +7 -0
- data/spec_rails/rails2/app_root/config/initializers/cookie_verification_secret.rb +7 -0
- data/spec_rails/rails2/app_root/config/initializers/inflections.rb +10 -0
- data/spec_rails/rails2/app_root/config/initializers/load_diagnostics.rb +2 -0
- data/spec_rails/rails2/app_root/config/initializers/mime_types.rb +5 -0
- data/spec_rails/rails2/app_root/config/initializers/new_rails_defaults.rb +21 -0
- data/spec_rails/rails2/app_root/config/initializers/session_store.rb +15 -0
- data/spec_rails/rails2/app_root/config/locales/en.yml +5 -0
- data/spec_rails/rails2/app_root/config/routes.rb +43 -0
- data/spec_rails/rails2/app_root/db/seeds.rb +7 -0
- data/spec_rails/rails2/app_root/doc/README_FOR_APP +2 -0
- data/spec_rails/rails2/app_root/lib/diagnostics/mysql_check.rb +18 -0
- data/spec_rails/rails2/app_root/lib/diagnostics/server_check.rb +4 -0
- data/spec_rails/rails2/app_root/lib/tasks/cucumber.rake +53 -0
- data/spec_rails/rails2/app_root/public/404.html +30 -0
- data/spec_rails/rails2/app_root/public/422.html +30 -0
- data/spec_rails/rails2/app_root/public/500.html +30 -0
- data/spec_rails/rails2/app_root/public/favicon.ico +0 -0
- data/spec_rails/rails2/app_root/public/images/rails.png +0 -0
- data/spec_rails/rails2/app_root/public/index.html +275 -0
- data/spec_rails/rails2/app_root/public/javascripts/application.js +2 -0
- data/spec_rails/rails2/app_root/public/javascripts/controls.js +963 -0
- data/spec_rails/rails2/app_root/public/javascripts/dragdrop.js +973 -0
- data/spec_rails/rails2/app_root/public/javascripts/effects.js +1128 -0
- data/spec_rails/rails2/app_root/public/javascripts/prototype.js +4320 -0
- data/spec_rails/rails2/app_root/public/robots.txt +5 -0
- data/spec_rails/rails2/app_root/script/about +4 -0
- data/spec_rails/rails2/app_root/script/console +3 -0
- data/spec_rails/rails2/app_root/script/cucumber +10 -0
- data/spec_rails/rails2/app_root/script/dbconsole +3 -0
- data/spec_rails/rails2/app_root/script/destroy +3 -0
- data/spec_rails/rails2/app_root/script/generate +3 -0
- data/spec_rails/rails2/app_root/script/performance/benchmarker +3 -0
- data/spec_rails/rails2/app_root/script/performance/profiler +3 -0
- data/spec_rails/rails2/app_root/script/plugin +3 -0
- data/spec_rails/rails2/app_root/script/runner +3 -0
- data/spec_rails/rails2/app_root/script/server +3 -0
- data/spec_rails/rails2/app_root/test/performance/browsing_test.rb +9 -0
- data/spec_rails/rails2/app_root/test/test_helper.rb +38 -0
- data/spec_rails/rails2/controllers/diagnostics_controller_spec.rb +12 -0
- data/spec_rails/rails2/controllers/statuses_controller_spec.rb +34 -0
- data/spec_rails/rails2/features/diagnostics_page.feature +34 -0
- data/spec_rails/rails2/features/status_page.feature +43 -0
- data/spec_rails/rails2/features/step_definitions/diagnostic_steps.rb +39 -0
- data/spec_rails/rails2/features/step_definitions/web_steps.rb +219 -0
- data/spec_rails/rails2/features/support/env.rb +61 -0
- data/spec_rails/rails2/features/support/hooks.rb +3 -0
- data/spec_rails/rails2/features/support/paths.rb +36 -0
- data/spec_rails/rails2/spec.opts +1 -0
- data/spec_rails/rails2/spec_helper.rb +4 -0
- data/spec_rails/rails3/Gemfile +0 -0
- metadata +119 -7
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Diagnostics
|
|
2
|
+
class Check
|
|
3
|
+
|
|
4
|
+
def self.add(name, &block)
|
|
5
|
+
Diagnostics.checks << check = new(name, &block)
|
|
6
|
+
check
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.find(name)
|
|
10
|
+
Diagnostics.checks.select {|c| c.name == name }[0]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_reader :name
|
|
14
|
+
|
|
15
|
+
def initialize(name, &block)
|
|
16
|
+
@name = name
|
|
17
|
+
instance_eval(&block) if block
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def passed(&block)
|
|
21
|
+
@passed_block = block
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def warning(&block)
|
|
25
|
+
@warning_block = block
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def failed(&block)
|
|
29
|
+
@failed_block = block
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def passed?
|
|
33
|
+
@passed_block_result ||= call_block(@passed_block)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def warning?
|
|
37
|
+
@warning_block_result ||= call_block(@warning_block)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def failed?
|
|
41
|
+
@failed_block_result ||= call_block(@failed_block)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def status
|
|
45
|
+
return :passed if passed?
|
|
46
|
+
return :warning if warning?
|
|
47
|
+
return :failed if failed?
|
|
48
|
+
:none
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def data
|
|
52
|
+
block_given? ? yield(data_group) : data_group
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def data_group
|
|
58
|
+
@data_group ||= DataGroup.new
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def call_block(block)
|
|
62
|
+
block ? block.call : false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Diagnostics
|
|
2
|
+
module Data
|
|
3
|
+
class Elements < Hash
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
self[:attributes] = []
|
|
7
|
+
self[:lists] = []
|
|
8
|
+
self[:texts] = []
|
|
9
|
+
self[:tables] = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def attributes(format)
|
|
13
|
+
elements(:attributes, format)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def lists(format)
|
|
17
|
+
elements(:lists, format)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def texts(format)
|
|
21
|
+
elements(:texts, format)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def tables(format)
|
|
25
|
+
elements(:tables, format)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def elements(key, format)
|
|
31
|
+
format ? format_elements(key, format) : self[key]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def format_elements(key, format)
|
|
35
|
+
self[key].map(&format)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Diagnostics
|
|
2
|
+
module Data
|
|
3
|
+
class Table < Struct.new(:value)
|
|
4
|
+
|
|
5
|
+
def html
|
|
6
|
+
"".tap do |s|
|
|
7
|
+
s.extend(HTMLMethods)
|
|
8
|
+
|
|
9
|
+
s.wrap :table do
|
|
10
|
+
s.wrap :tr do
|
|
11
|
+
value.keys.each do |k|
|
|
12
|
+
s.wrap :th do
|
|
13
|
+
s << k.to_s
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
rows.transpose.each do |v|
|
|
18
|
+
s.wrap :tr do
|
|
19
|
+
v.each do |td|
|
|
20
|
+
s.wrap :td do
|
|
21
|
+
s << td.to_s
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module HTMLMethods
|
|
32
|
+
def wrap(tag)
|
|
33
|
+
concat "<#{tag}>"
|
|
34
|
+
yield if block_given?
|
|
35
|
+
concat "</#{tag}>"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def rows
|
|
42
|
+
value.values.map {|v| [v].flatten }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Diagnostics
|
|
2
|
+
class DataGroup
|
|
3
|
+
|
|
4
|
+
def elements
|
|
5
|
+
@elements ||= Data::Elements.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def attributes(format = nil)
|
|
9
|
+
elements.attributes(format)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def lists(format = nil)
|
|
13
|
+
elements.lists(format)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def texts(format = nil)
|
|
17
|
+
elements.texts(format)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def tables(format = nil)
|
|
21
|
+
elements.tables(format)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def []=(key, value)
|
|
25
|
+
attributes << Data::Attribute.new(key, value)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def <<(object)
|
|
29
|
+
case object
|
|
30
|
+
when Array then lists << Data::List.new(object)
|
|
31
|
+
when String then texts << Data::Text.new(object)
|
|
32
|
+
when Hash then tables << Data::Table.new(object)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Diagnostics
|
|
2
|
+
module Methods
|
|
3
|
+
|
|
4
|
+
DIAGNOSTIC_MESSAGES = {
|
|
5
|
+
:passed => 'All Systems Operational',
|
|
6
|
+
:warning => 'Experiencing Issues',
|
|
7
|
+
:failed => 'System Failure',
|
|
8
|
+
:none => 'No Diagnostic Checks Run'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def diagnostic_checks
|
|
12
|
+
Diagnostics.checks
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def diagnostic_check(name, &block)
|
|
16
|
+
block ? Check.add(name, &block) : Check.find(name)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def diagnostic_message
|
|
20
|
+
case diagnostic_status
|
|
21
|
+
when :passed then DIAGNOSTIC_MESSAGES[:passed]
|
|
22
|
+
when :warning then DIAGNOSTIC_MESSAGES[:warning]
|
|
23
|
+
when :failed then DIAGNOSTIC_MESSAGES[:failed]
|
|
24
|
+
when :none then DIAGNOSTIC_MESSAGES[:none]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def diagnostic_status
|
|
29
|
+
return :failed if diagnostic_statuses.include?(:failed)
|
|
30
|
+
return :warning if diagnostic_statuses.include?(:warning)
|
|
31
|
+
return :passed if diagnostic_statuses.include?(:passed)
|
|
32
|
+
:none
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def diagnostic_statuses
|
|
38
|
+
@diagnostic_statuses ||= diagnostic_checks.map(&:status).uniq
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Object
|
|
45
|
+
include Diagnostics::Methods
|
|
46
|
+
end
|
data/lib/diagnostics/version.rb
CHANGED
data/rails/init.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'diagnostics'
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Diagnostics
|
|
4
|
+
describe Check do
|
|
5
|
+
|
|
6
|
+
describe '.add' do
|
|
7
|
+
it 'adds a check' do
|
|
8
|
+
described_class.add :foo
|
|
9
|
+
Diagnostics.should have(1).checks
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '.find' do
|
|
14
|
+
let!(:check) { described_class.add :foo }
|
|
15
|
+
|
|
16
|
+
it 'finds the check by name' do
|
|
17
|
+
described_class.find(:foo).should eq(check)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe 'status predicate methods' do
|
|
22
|
+
let(:check) do
|
|
23
|
+
described_class.new(:foo) do |c|
|
|
24
|
+
c.passed { true }
|
|
25
|
+
c.warning { false }
|
|
26
|
+
c.failed { false }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe '#passed?' do
|
|
31
|
+
specify { check.should be_passed }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#warning?' do
|
|
35
|
+
specify { check.should_not be_warning }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#failed?' do
|
|
39
|
+
specify { check.should_not be_failed }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe '#status' do
|
|
44
|
+
[:passed, :warning, :failed, :none].each do |status|
|
|
45
|
+
context "with #{status}" do
|
|
46
|
+
let(:check) { create_check(status) }
|
|
47
|
+
specify { check.status.should eq(status) }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def create_check(status = nil)
|
|
52
|
+
described_class.new(:foo) do |c|
|
|
53
|
+
c.passed { status == :passed }
|
|
54
|
+
c.warning { status == :warning }
|
|
55
|
+
c.failed { status == :failed }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe '#data' do
|
|
61
|
+
context 'given a block' do
|
|
62
|
+
it 'yields a DataGroup instance' do
|
|
63
|
+
described_class.new(:foo).data do |d|
|
|
64
|
+
d.should be_an_instance_of(Diagnostics::DataGroup)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context 'given no block' do
|
|
70
|
+
it 'returns a DataGroup instance' do
|
|
71
|
+
data = described_class.new(:foo).data
|
|
72
|
+
data.should be_an_instance_of(Diagnostics::DataGroup)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Diagnostics
|
|
4
|
+
module Data
|
|
5
|
+
describe Attribute do
|
|
6
|
+
let(:attribute) { described_class.new('attr', 'val') }
|
|
7
|
+
|
|
8
|
+
describe '#plain' do
|
|
9
|
+
specify do
|
|
10
|
+
attribute.plain.should eq('attr: val')
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#html' do
|
|
15
|
+
specify do
|
|
16
|
+
attribute.html.should eq('<strong>attr:</strong> val')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Diagnostics
|
|
4
|
+
module Data
|
|
5
|
+
describe List do
|
|
6
|
+
let(:list) { described_class.new(['a', 'b', 'c']) }
|
|
7
|
+
|
|
8
|
+
describe '#plain' do
|
|
9
|
+
specify do
|
|
10
|
+
list.plain.should eq("a\nb\nc")
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#html' do
|
|
15
|
+
specify do
|
|
16
|
+
list.html.should eq('a<br />b<br />c')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Diagnostics
|
|
4
|
+
module Data
|
|
5
|
+
describe Table do
|
|
6
|
+
describe '#html' do
|
|
7
|
+
|
|
8
|
+
specify do
|
|
9
|
+
described_class.new(
|
|
10
|
+
{ 'heading 1' => [1, 2, 3],
|
|
11
|
+
'heading 2' => [4, 5, 6],
|
|
12
|
+
'heading 3' => [7, 8, 9] }
|
|
13
|
+
).html.should eq(
|
|
14
|
+
'<table>' \
|
|
15
|
+
'<tr>' \
|
|
16
|
+
'<th>heading 1</th>' \
|
|
17
|
+
'<th>heading 2</th>' \
|
|
18
|
+
'<th>heading 3</th>' \
|
|
19
|
+
'</tr>' \
|
|
20
|
+
'<tr>' \
|
|
21
|
+
'<td>1</td>' \
|
|
22
|
+
'<td>4</td>' \
|
|
23
|
+
'<td>7</td>' \
|
|
24
|
+
'</tr>' \
|
|
25
|
+
'<tr>' \
|
|
26
|
+
'<td>2</td>' \
|
|
27
|
+
'<td>5</td>' \
|
|
28
|
+
'<td>8</td>' \
|
|
29
|
+
'</tr>' \
|
|
30
|
+
'<tr>' \
|
|
31
|
+
'<td>3</td>' \
|
|
32
|
+
'<td>6</td>' \
|
|
33
|
+
'<td>9</td>' \
|
|
34
|
+
'</tr>' \
|
|
35
|
+
'</table>'
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
specify do
|
|
40
|
+
described_class.new(
|
|
41
|
+
{ 'heading 1' => 1,
|
|
42
|
+
'heading 2' => 2,
|
|
43
|
+
'heading 3' => 3 }
|
|
44
|
+
).html.should eq(
|
|
45
|
+
'<table>' \
|
|
46
|
+
'<tr>' \
|
|
47
|
+
'<th>heading 1</th>' \
|
|
48
|
+
'<th>heading 2</th>' \
|
|
49
|
+
'<th>heading 3</th>' \
|
|
50
|
+
'</tr>' \
|
|
51
|
+
'<tr>' \
|
|
52
|
+
'<td>1</td>' \
|
|
53
|
+
'<td>2</td>' \
|
|
54
|
+
'<td>3</td>' \
|
|
55
|
+
'</tr>' \
|
|
56
|
+
'</table>'
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|