crashlog 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 +2 -1
- data/.rspec +1 -0
- data/.travis.yml +11 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +161 -0
- data/INSTALL +22 -0
- data/README.md +14 -4
- data/Rakefile +13 -0
- data/crashlog.gemspec +12 -3
- data/generators/crashlog/templates/initializer.rb +6 -0
- data/install.rb +2 -0
- data/lib/crash_log/backtrace/line.rb +105 -0
- data/lib/crash_log/backtrace/line_cache.rb +23 -0
- data/lib/crash_log/backtrace.rb +66 -0
- data/lib/crash_log/configuration.bak.rb +199 -0
- data/lib/crash_log/configuration.rb +188 -0
- data/lib/crash_log/logging.rb +51 -0
- data/lib/crash_log/payload.rb +157 -0
- data/lib/crash_log/rack.rb +47 -0
- data/lib/crash_log/rails/action_controller_rescue.rb +32 -0
- data/lib/crash_log/rails/controller_methods.rb +45 -0
- data/lib/crash_log/rails/middleware/debug_exception_catcher.rb +43 -0
- data/lib/crash_log/rails.rb +32 -0
- data/lib/crash_log/railtie.rb +41 -0
- data/lib/crash_log/reporter.rb +105 -0
- data/lib/crash_log/system_information.rb +64 -0
- data/lib/crash_log/templates/payload.rabl +7 -0
- data/lib/crash_log/version.rb +1 -1
- data/lib/crash_log.rb +118 -0
- data/lib/faraday/request/hmac_authentication.rb +73 -0
- data/lib/rails/generators/crashlog/crashlog_generator.rb +42 -0
- data/rails/init.rb +1 -0
- data/spec/crash_log/backtrace_spec.rb +79 -0
- data/spec/crash_log/initializer_spec.rb +53 -0
- data/spec/crash_log/payload_spec.rb +124 -0
- data/spec/crash_log/reporter_spec.rb +179 -0
- data/spec/crash_log_spec.rb +153 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/break_controller.rb +10 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +59 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +44 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/crashlog.rb +6 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/routes.rb +6 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/requests/rack_spec.rb +29 -0
- data/spec/requests/rails_controller_rescue_spec.rb +46 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/doing.rb +1 -0
- data/spec/support/dummy_app.rb +13 -0
- data/spec/support/hash_ext.rb +7 -0
- metadata +197 -7
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color -f d
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -2,3 +2,13 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in crashlog.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
gem 'rspec-rails'
|
7
|
+
gem 'guard-rspec'
|
8
|
+
gem 'uuid'
|
9
|
+
gem 'json_spec'
|
10
|
+
gem 'rake', '~> 0.9.2'
|
11
|
+
gem 'rails', '3.2.7'
|
12
|
+
gem 'pg'
|
13
|
+
gem 'delorean'
|
14
|
+
gem 'rack-test', :git => 'git://github.com/brynary/rack-test.git'
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/brynary/rack-test.git
|
3
|
+
revision: 8153c07db7a96889ad22e2fb543c64548dfde2e5
|
4
|
+
specs:
|
5
|
+
rack-test (0.6.1)
|
6
|
+
rack (>= 1.0)
|
7
|
+
|
8
|
+
PATH
|
9
|
+
remote: .
|
10
|
+
specs:
|
11
|
+
crashlog (0.0.1)
|
12
|
+
activesupport
|
13
|
+
crashlog-auth-hmac (~> 1.1.5)
|
14
|
+
faraday
|
15
|
+
hashr
|
16
|
+
rabl (>= 0.6.14)
|
17
|
+
uuid
|
18
|
+
yajl-ruby
|
19
|
+
|
20
|
+
GEM
|
21
|
+
remote: https://rubygems.org/
|
22
|
+
specs:
|
23
|
+
actionmailer (3.2.7)
|
24
|
+
actionpack (= 3.2.7)
|
25
|
+
mail (~> 2.4.4)
|
26
|
+
actionpack (3.2.7)
|
27
|
+
activemodel (= 3.2.7)
|
28
|
+
activesupport (= 3.2.7)
|
29
|
+
builder (~> 3.0.0)
|
30
|
+
erubis (~> 2.7.0)
|
31
|
+
journey (~> 1.0.4)
|
32
|
+
rack (~> 1.4.0)
|
33
|
+
rack-cache (~> 1.2)
|
34
|
+
rack-test (~> 0.6.1)
|
35
|
+
sprockets (~> 2.1.3)
|
36
|
+
activemodel (3.2.7)
|
37
|
+
activesupport (= 3.2.7)
|
38
|
+
builder (~> 3.0.0)
|
39
|
+
activerecord (3.2.7)
|
40
|
+
activemodel (= 3.2.7)
|
41
|
+
activesupport (= 3.2.7)
|
42
|
+
arel (~> 3.0.2)
|
43
|
+
tzinfo (~> 0.3.29)
|
44
|
+
activeresource (3.2.7)
|
45
|
+
activemodel (= 3.2.7)
|
46
|
+
activesupport (= 3.2.7)
|
47
|
+
activesupport (3.2.7)
|
48
|
+
i18n (~> 0.6)
|
49
|
+
multi_json (~> 1.0)
|
50
|
+
arel (3.0.2)
|
51
|
+
builder (3.0.0)
|
52
|
+
chronic (0.6.7)
|
53
|
+
crashlog-auth-hmac (1.1.5)
|
54
|
+
delorean (2.0.0)
|
55
|
+
chronic
|
56
|
+
diff-lcs (1.1.3)
|
57
|
+
erubis (2.7.0)
|
58
|
+
faraday (0.8.4)
|
59
|
+
multipart-post (~> 1.1)
|
60
|
+
ffi (1.1.1)
|
61
|
+
guard (1.2.3)
|
62
|
+
listen (>= 0.4.2)
|
63
|
+
thor (>= 0.14.6)
|
64
|
+
guard-rspec (1.2.0)
|
65
|
+
guard (>= 1.1)
|
66
|
+
hashr (0.0.22)
|
67
|
+
hike (1.2.1)
|
68
|
+
i18n (0.6.0)
|
69
|
+
journey (1.0.4)
|
70
|
+
json (1.7.4)
|
71
|
+
json_spec (1.0.3)
|
72
|
+
multi_json (~> 1.0)
|
73
|
+
rspec (~> 2.0)
|
74
|
+
listen (0.4.7)
|
75
|
+
rb-fchange (~> 0.0.5)
|
76
|
+
rb-fsevent (~> 0.9.1)
|
77
|
+
rb-inotify (~> 0.8.8)
|
78
|
+
macaddr (1.6.1)
|
79
|
+
systemu (~> 2.5.0)
|
80
|
+
mail (2.4.4)
|
81
|
+
i18n (>= 0.4.0)
|
82
|
+
mime-types (~> 1.16)
|
83
|
+
treetop (~> 1.4.8)
|
84
|
+
mime-types (1.19)
|
85
|
+
multi_json (1.3.6)
|
86
|
+
multipart-post (1.1.5)
|
87
|
+
pg (0.14.0)
|
88
|
+
polyglot (0.3.3)
|
89
|
+
rabl (0.7.0)
|
90
|
+
activesupport (>= 2.3.14)
|
91
|
+
multi_json (~> 1.0)
|
92
|
+
rack (1.4.1)
|
93
|
+
rack-cache (1.2)
|
94
|
+
rack (>= 0.4)
|
95
|
+
rack-ssl (1.3.2)
|
96
|
+
rack
|
97
|
+
rails (3.2.7)
|
98
|
+
actionmailer (= 3.2.7)
|
99
|
+
actionpack (= 3.2.7)
|
100
|
+
activerecord (= 3.2.7)
|
101
|
+
activeresource (= 3.2.7)
|
102
|
+
activesupport (= 3.2.7)
|
103
|
+
bundler (~> 1.0)
|
104
|
+
railties (= 3.2.7)
|
105
|
+
railties (3.2.7)
|
106
|
+
actionpack (= 3.2.7)
|
107
|
+
activesupport (= 3.2.7)
|
108
|
+
rack-ssl (~> 1.3.2)
|
109
|
+
rake (>= 0.8.7)
|
110
|
+
rdoc (~> 3.4)
|
111
|
+
thor (>= 0.14.6, < 2.0)
|
112
|
+
rake (0.9.2.2)
|
113
|
+
rb-fchange (0.0.5)
|
114
|
+
ffi
|
115
|
+
rb-fsevent (0.9.1)
|
116
|
+
rb-inotify (0.8.8)
|
117
|
+
ffi (>= 0.5.0)
|
118
|
+
rdoc (3.12)
|
119
|
+
json (~> 1.4)
|
120
|
+
rspec (2.11.0)
|
121
|
+
rspec-core (~> 2.11.0)
|
122
|
+
rspec-expectations (~> 2.11.0)
|
123
|
+
rspec-mocks (~> 2.11.0)
|
124
|
+
rspec-core (2.11.1)
|
125
|
+
rspec-expectations (2.11.2)
|
126
|
+
diff-lcs (~> 1.1.3)
|
127
|
+
rspec-mocks (2.11.1)
|
128
|
+
rspec-rails (2.11.0)
|
129
|
+
actionpack (>= 3.0)
|
130
|
+
activesupport (>= 3.0)
|
131
|
+
railties (>= 3.0)
|
132
|
+
rspec (~> 2.11.0)
|
133
|
+
sprockets (2.1.3)
|
134
|
+
hike (~> 1.2)
|
135
|
+
rack (~> 1.0)
|
136
|
+
tilt (~> 1.1, != 1.3.0)
|
137
|
+
systemu (2.5.2)
|
138
|
+
thor (0.15.4)
|
139
|
+
tilt (1.3.3)
|
140
|
+
treetop (1.4.10)
|
141
|
+
polyglot
|
142
|
+
polyglot (>= 0.3.1)
|
143
|
+
tzinfo (0.3.33)
|
144
|
+
uuid (2.3.5)
|
145
|
+
macaddr (~> 1.0)
|
146
|
+
yajl-ruby (1.1.0)
|
147
|
+
|
148
|
+
PLATFORMS
|
149
|
+
ruby
|
150
|
+
|
151
|
+
DEPENDENCIES
|
152
|
+
crashlog!
|
153
|
+
delorean
|
154
|
+
guard-rspec
|
155
|
+
json_spec
|
156
|
+
pg
|
157
|
+
rack-test!
|
158
|
+
rails (= 3.2.7)
|
159
|
+
rake (~> 0.9.2)
|
160
|
+
rspec-rails
|
161
|
+
uuid
|
data/INSTALL
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
=== Configuration
|
2
|
+
|
3
|
+
You should have something like this in config/initializers/crashlog.rb.
|
4
|
+
|
5
|
+
CrashLog.configure do |config|
|
6
|
+
config.api_key = 'AhHusJhJHAjJajA'
|
7
|
+
config.project_id = 'ca9e5931-310f-4897-90c2-d0626f0f2a01'
|
8
|
+
end
|
9
|
+
|
10
|
+
(Please note that this configuration should be in a global configuration, and
|
11
|
+
is *not* environment-specific. CrashLog is smart enough to know what errors are
|
12
|
+
caused by what environments, so your staging errors don't get mixed in with
|
13
|
+
your production errors.)
|
14
|
+
|
15
|
+
You can test that CrashLog is working in your production environment by using
|
16
|
+
this rake task (from RAILS_ROOT):
|
17
|
+
|
18
|
+
rake crashlog:test
|
19
|
+
|
20
|
+
If everything is configured properly, that task will send a notice to CrashLog
|
21
|
+
which will be visible immediately.
|
22
|
+
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
#
|
1
|
+
# CrashLog
|
2
2
|
|
3
|
-
|
3
|
+
CrashLog is a exception tracking and notification service that gives you unparalleled
|
4
|
+
insight into issues occurring within your production applications, in realtime.
|
4
5
|
|
5
6
|
## Installation
|
6
7
|
|
@@ -16,9 +17,14 @@ Or install it yourself as:
|
|
16
17
|
|
17
18
|
$ gem install crashlog
|
18
19
|
|
19
|
-
##
|
20
|
+
## Configuration
|
20
21
|
|
21
|
-
|
22
|
+
```ruby
|
23
|
+
CrashLog.configuration do |config|
|
24
|
+
config.api_key = "Your API Key"
|
25
|
+
config.project_id = "Project Project ID"
|
26
|
+
end
|
27
|
+
```
|
22
28
|
|
23
29
|
## Contributing
|
24
30
|
|
@@ -27,3 +33,7 @@ TODO: Write usage instructions here
|
|
27
33
|
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
34
|
4. Push to the branch (`git push origin my-new-feature`)
|
29
35
|
5. Create new Pull Request
|
36
|
+
|
37
|
+
## Contributors
|
38
|
+
|
39
|
+
- Ivan Vanderbyl
|
data/Rakefile
CHANGED
data/crashlog.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
require File.expand_path('../lib/crash_log/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
-
gem.authors = ["
|
6
|
-
gem.email = ["
|
5
|
+
gem.authors = ["TestPilot CI"]
|
6
|
+
gem.email = ["support@crashlog.io"]
|
7
7
|
gem.description = %q{CrashLog Exception reporter}
|
8
8
|
gem.summary = %q{CrashLog is an exception handler for production applications}
|
9
|
-
gem.homepage = ""
|
9
|
+
gem.homepage = "http://crashlog.io"
|
10
10
|
|
11
11
|
gem.files = `git ls-files`.split($\)
|
12
12
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
@@ -14,4 +14,13 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.name = "crashlog"
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = CrashLog::VERSION
|
17
|
+
gem.platform = Gem::Platform::RUBY
|
18
|
+
|
19
|
+
gem.add_dependency("activesupport")
|
20
|
+
gem.add_dependency("faraday")
|
21
|
+
gem.add_dependency("crashlog-auth-hmac", '~> 1.1.5')
|
22
|
+
gem.add_dependency("yajl-ruby")
|
23
|
+
gem.add_dependency("rabl", '>= 0.6.14')
|
24
|
+
gem.add_dependency("uuid")
|
25
|
+
gem.add_dependency("hashr")
|
17
26
|
end
|
data/install.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
module CrashLog
|
2
|
+
class Backtrace
|
3
|
+
class Line
|
4
|
+
|
5
|
+
# Backtrace line parsing regexp
|
6
|
+
# (optionnally allowing leading X: for windows support)
|
7
|
+
INPUT_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$}
|
8
|
+
|
9
|
+
# The file portion of the line (such as app/models/user.rb)
|
10
|
+
attr_reader :file
|
11
|
+
|
12
|
+
# The line number portion of the line
|
13
|
+
attr_reader :number
|
14
|
+
|
15
|
+
# The method of the line (such as index)
|
16
|
+
attr_reader :method
|
17
|
+
|
18
|
+
# Parses a single line of a given backtrace
|
19
|
+
# @param [String] unparsed_line The raw line from +caller+ or some
|
20
|
+
# backtrace.
|
21
|
+
# @return [Line] The parsed backtrace line
|
22
|
+
def self.parse(unparsed_line)
|
23
|
+
_, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
|
24
|
+
new(file, number, method)
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(file, number, method)
|
28
|
+
self.file = file
|
29
|
+
self.number = number.to_i
|
30
|
+
self.method = method
|
31
|
+
end
|
32
|
+
|
33
|
+
# Reconstructs the line in a readable fashion
|
34
|
+
def to_s
|
35
|
+
"#{file}:#{number}:in `#{method}'"
|
36
|
+
end
|
37
|
+
|
38
|
+
def ==(other)
|
39
|
+
to_s == other.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def apply_filter(filter)
|
43
|
+
result = filter.call(file)
|
44
|
+
if result.nil?
|
45
|
+
# Filter returned nil, discard this line
|
46
|
+
@_mark_for_deletion = true
|
47
|
+
else
|
48
|
+
# Filter manipulated parsed file name only
|
49
|
+
self.file = result
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def context_line
|
54
|
+
Backtrace::LineCache::getline(file, number)
|
55
|
+
end
|
56
|
+
|
57
|
+
def pre_context
|
58
|
+
(number-context_lines..number-1).map {|i|
|
59
|
+
Backtrace::LineCache.getline(file, i)
|
60
|
+
}.select { |line| line }
|
61
|
+
end
|
62
|
+
|
63
|
+
def post_context
|
64
|
+
(number+1..number+context_lines).map {|i|
|
65
|
+
Backtrace::LineCache.getline(file, i)
|
66
|
+
}.select { |line| line }
|
67
|
+
end
|
68
|
+
|
69
|
+
def context_lines
|
70
|
+
CrashLog.configuration.context_lines
|
71
|
+
end
|
72
|
+
|
73
|
+
def inspect
|
74
|
+
"<Line:#{to_s}>"
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_hash
|
78
|
+
as_json
|
79
|
+
end
|
80
|
+
|
81
|
+
def as_json
|
82
|
+
{}.tap do |hash|
|
83
|
+
hash[:number] = number
|
84
|
+
hash[:method] = method
|
85
|
+
hash[:file] = file
|
86
|
+
|
87
|
+
if context_lines
|
88
|
+
hash[:context_line] = context_line
|
89
|
+
hash[:pre_context] = pre_context
|
90
|
+
hash[:post_context] = post_context
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def marked_for_deletion?
|
96
|
+
@_mark_for_deletion == true
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
attr_writer :file, :number, :method
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module CrashLog
|
2
|
+
class Backtrace
|
3
|
+
class LineCache
|
4
|
+
class << self
|
5
|
+
CACHE = {}
|
6
|
+
|
7
|
+
def getlines(path)
|
8
|
+
CACHE[path] ||= begin
|
9
|
+
IO.readlines(path).map { |line| line.chomp.gsub(/[']/, '\\\\\'') }
|
10
|
+
rescue
|
11
|
+
[]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def getline(path, n)
|
16
|
+
return nil if n < 1
|
17
|
+
getlines(path)[n-1]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module CrashLog
|
2
|
+
class Backtrace
|
3
|
+
|
4
|
+
autoload :Line, 'crash_log/backtrace/line'
|
5
|
+
autoload :LineCache, 'crash_log/backtrace/line_cache'
|
6
|
+
|
7
|
+
# holder for an Array of Backtrace::Line instances
|
8
|
+
attr_reader :lines
|
9
|
+
|
10
|
+
def self.parse(ruby_backtrace, opts = {})
|
11
|
+
ruby_lines = split_multiline_backtrace(ruby_backtrace)
|
12
|
+
|
13
|
+
lines = ruby_lines.to_a.map do |unparsed_line|
|
14
|
+
Line.parse(unparsed_line)
|
15
|
+
end
|
16
|
+
|
17
|
+
filters = opts[:filters] || []
|
18
|
+
|
19
|
+
lines.each do |line|
|
20
|
+
filters.each do |filter|
|
21
|
+
line.apply_filter(filter)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
lines = lines.reject do |line|
|
26
|
+
line.marked_for_deletion?
|
27
|
+
end
|
28
|
+
|
29
|
+
instance = new(lines)
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(lines)
|
33
|
+
self.lines = lines
|
34
|
+
end
|
35
|
+
|
36
|
+
def inspect
|
37
|
+
"<Backtrace: " + lines.map { |line| line.inspect }.join(", ") + ">"
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(other)
|
41
|
+
if other.respond_to?(:lines)
|
42
|
+
lines == other.lines
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_a
|
49
|
+
lines.map do |line|
|
50
|
+
line.to_hash
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
attr_writer :lines
|
57
|
+
|
58
|
+
def self.split_multiline_backtrace(backtrace)
|
59
|
+
if backtrace.to_a.size == 1
|
60
|
+
backtrace.to_a.first.split(/\n\s*/)
|
61
|
+
else
|
62
|
+
backtrace
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|