bitclust-core 0.5.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 (127) hide show
  1. data/ChangeLog +2907 -0
  2. data/Gemfile +7 -0
  3. data/README +21 -0
  4. data/Rakefile +20 -0
  5. data/bin/bitclust +14 -0
  6. data/bin/refe +36 -0
  7. data/bitclust-dev.gemspec +33 -0
  8. data/bitclust.gemspec +30 -0
  9. data/config.in +23 -0
  10. data/config.ru +48 -0
  11. data/config.ru.sample +31 -0
  12. data/data/bitclust/catalog/ja_JP.EUC-JP +78 -0
  13. data/data/bitclust/catalog/ja_JP.UTF-8 +78 -0
  14. data/data/bitclust/template.lillia/class +98 -0
  15. data/data/bitclust/template.lillia/class-index +28 -0
  16. data/data/bitclust/template.lillia/doc +48 -0
  17. data/data/bitclust/template.lillia/layout +19 -0
  18. data/data/bitclust/template.lillia/library +129 -0
  19. data/data/bitclust/template.lillia/library-index +32 -0
  20. data/data/bitclust/template.lillia/method +20 -0
  21. data/data/bitclust/template.lillia/rd_file +6 -0
  22. data/data/bitclust/template.offline/class +67 -0
  23. data/data/bitclust/template.offline/class-index +28 -0
  24. data/data/bitclust/template.offline/doc +13 -0
  25. data/data/bitclust/template.offline/function +22 -0
  26. data/data/bitclust/template.offline/function-index +24 -0
  27. data/data/bitclust/template.offline/layout +18 -0
  28. data/data/bitclust/template.offline/library +87 -0
  29. data/data/bitclust/template.offline/library-index +32 -0
  30. data/data/bitclust/template.offline/method +21 -0
  31. data/data/bitclust/template.offline/rd_file +6 -0
  32. data/data/bitclust/template/class +133 -0
  33. data/data/bitclust/template/class-index +30 -0
  34. data/data/bitclust/template/doc +14 -0
  35. data/data/bitclust/template/function +21 -0
  36. data/data/bitclust/template/function-index +25 -0
  37. data/data/bitclust/template/layout +19 -0
  38. data/data/bitclust/template/library +89 -0
  39. data/data/bitclust/template/library-index +35 -0
  40. data/data/bitclust/template/method +24 -0
  41. data/data/bitclust/template/opensearchdescription +10 -0
  42. data/data/bitclust/template/search +57 -0
  43. data/lib/bitclust.rb +9 -0
  44. data/lib/bitclust/app.rb +129 -0
  45. data/lib/bitclust/classentry.rb +425 -0
  46. data/lib/bitclust/compat.rb +39 -0
  47. data/lib/bitclust/completion.rb +531 -0
  48. data/lib/bitclust/crossrubyutils.rb +91 -0
  49. data/lib/bitclust/database.rb +181 -0
  50. data/lib/bitclust/docentry.rb +83 -0
  51. data/lib/bitclust/entry.rb +223 -0
  52. data/lib/bitclust/exception.rb +38 -0
  53. data/lib/bitclust/functiondatabase.rb +115 -0
  54. data/lib/bitclust/functionentry.rb +81 -0
  55. data/lib/bitclust/functionreferenceparser.rb +76 -0
  56. data/lib/bitclust/htmlutils.rb +80 -0
  57. data/lib/bitclust/interface.rb +87 -0
  58. data/lib/bitclust/libraryentry.rb +211 -0
  59. data/lib/bitclust/lineinput.rb +165 -0
  60. data/lib/bitclust/messagecatalog.rb +95 -0
  61. data/lib/bitclust/methoddatabase.rb +401 -0
  62. data/lib/bitclust/methodentry.rb +202 -0
  63. data/lib/bitclust/methodid.rb +209 -0
  64. data/lib/bitclust/methodsignature.rb +82 -0
  65. data/lib/bitclust/nameutils.rb +236 -0
  66. data/lib/bitclust/parseutils.rb +60 -0
  67. data/lib/bitclust/preprocessor.rb +273 -0
  68. data/lib/bitclust/rdcompiler.rb +507 -0
  69. data/lib/bitclust/refsdatabase.rb +66 -0
  70. data/lib/bitclust/requesthandler.rb +330 -0
  71. data/lib/bitclust/ridatabase.rb +349 -0
  72. data/lib/bitclust/rrdparser.rb +522 -0
  73. data/lib/bitclust/runner.rb +143 -0
  74. data/lib/bitclust/screen.rb +554 -0
  75. data/lib/bitclust/searcher.rb +518 -0
  76. data/lib/bitclust/server.rb +59 -0
  77. data/lib/bitclust/simplesearcher.rb +84 -0
  78. data/lib/bitclust/subcommand.rb +746 -0
  79. data/lib/bitclust/textutils.rb +51 -0
  80. data/lib/bitclust/version.rb +3 -0
  81. data/packer.rb +224 -0
  82. data/refe2.gemspec +29 -0
  83. data/server.exe +0 -0
  84. data/server.exy +159 -0
  85. data/server.rb +10 -0
  86. data/setup.rb +1596 -0
  87. data/standalone.rb +193 -0
  88. data/test/run_test.rb +15 -0
  89. data/test/test_bitclust.rb +81 -0
  90. data/test/test_entry.rb +39 -0
  91. data/test/test_functiondatabase.rb +55 -0
  92. data/test/test_libraryentry.rb +31 -0
  93. data/test/test_methoddatabase.rb +81 -0
  94. data/test/test_methodsignature.rb +14 -0
  95. data/test/test_nameutils.rb +324 -0
  96. data/test/test_preprocessor.rb +84 -0
  97. data/test/test_rdcompiler.rb +534 -0
  98. data/test/test_refsdatabase.rb +76 -0
  99. data/test/test_rrdparser.rb +26 -0
  100. data/test/test_runner.rb +102 -0
  101. data/test/test_simplesearcher.rb +48 -0
  102. data/theme/default/images/external.png +0 -0
  103. data/theme/default/rurema.png +0 -0
  104. data/theme/default/style.css +288 -0
  105. data/theme/default/test.css +254 -0
  106. data/theme/lillia/rurema.png +0 -0
  107. data/theme/lillia/style.css +331 -0
  108. data/theme/lillia/test.css +254 -0
  109. data/tools/bc-ancestors.rb +153 -0
  110. data/tools/bc-checkparams.rb +246 -0
  111. data/tools/bc-classes.rb +80 -0
  112. data/tools/bc-convert.rb +165 -0
  113. data/tools/bc-list.rb +63 -0
  114. data/tools/bc-methods.rb +171 -0
  115. data/tools/bc-preproc.rb +42 -0
  116. data/tools/bc-rdoc.rb +343 -0
  117. data/tools/bc-tochm.rb +301 -0
  118. data/tools/bc-tohtml.rb +125 -0
  119. data/tools/bc-tohtmlpackage.rb +241 -0
  120. data/tools/check-signature.rb +19 -0
  121. data/tools/forall-ruby.rb +20 -0
  122. data/tools/gencatalog.rb +69 -0
  123. data/tools/statrefm.rb +98 -0
  124. data/tools/stattodo.rb +150 -0
  125. data/tools/update-database.rb +146 -0
  126. data/view.cgi +6 -0
  127. metadata +222 -0
data/standalone.rb ADDED
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/ruby -Ku
2
+ #
3
+ # $Id$
4
+ #
5
+ # Stand-alone BitClust server based on WEBrick
6
+ #
7
+
8
+ require 'uri'
9
+ require 'webrick'
10
+ require 'optparse'
11
+ require 'pathname'
12
+
13
+ def srcdir_root
14
+ Pathname.new(__FILE__).realpath.dirname.cleanpath
15
+ end
16
+
17
+ params = {
18
+ :Port => 10080
19
+ }
20
+ baseurl = nil
21
+ dbpath = nil
22
+ srcdir = libdir = datadir = themedir = theme = templatedir = nil
23
+ encoding = 'utf-8' # encoding of view
24
+ set_srcdir = lambda {|path|
25
+ srcdir = path
26
+ datadir ||= "#{srcdir}/data/bitclust"
27
+ themedir ||= "#{srcdir}/theme"
28
+ libdir ||= "#{srcdir}/lib"
29
+ }
30
+
31
+ debugp = false
32
+ autop = false
33
+ browser = nil
34
+ pid_file = nil
35
+ capi = false
36
+
37
+ parser = OptionParser.new
38
+ parser.banner = "#{$0} [--bind-address=ADDR] [--port=NUM] --baseurl=URL --database=PATH [--srcdir=PATH] [--datadir=PATH] [--themedir=PATH] [--debug] [--auto] [--browser=BROWSER] [--pid-file=PATH] [--capi]"
39
+ parser.on('--bind-address=ADDR', 'Bind address') {|addr|
40
+ params[:BindAddress] = addr
41
+ }
42
+ parser.on('--port=NUM', 'Listening port number') {|num|
43
+ params[:Port] = num.to_i
44
+ }
45
+ parser.on('--baseurl=URL', 'The base URL to host.') {|url|
46
+ baseurl = url
47
+ }
48
+ parser.on('--database=PATH', 'MethodDatabase root directory.') {|path|
49
+ dbpath = path
50
+ }
51
+ parser.on('--srcdir=PATH', 'BitClust source directory.') {|path|
52
+ set_srcdir.call path
53
+ }
54
+ parser.on('--datadir=PATH', 'BitClust data directory.') {|path|
55
+ datadir = path
56
+ }
57
+ parser.on('--templatedir=PATH', 'Template directory.') {|path|
58
+ templatedir = path
59
+ }
60
+ parser.on('--themedir=PATH', 'BitClust theme directory.') {|path|
61
+ themedir = path
62
+ }
63
+ parser.on('--theme=THEME', 'BitClust theme.') {|th|
64
+ theme = th
65
+ }
66
+ parser.on('--[no-]debug', 'Debug mode.') {|flag|
67
+ debugp = flag
68
+ }
69
+ parser.on('--[no-]auto', 'Auto mode.') {|flag|
70
+ autop = flag
71
+ }
72
+ parser.on('--browser=BROWSER', 'Open with the browser.') {|path|
73
+ browser = path
74
+ }
75
+ parser.on('--pid-file=PATH', 'Write pid of the daemon to the specified file.') {|path|
76
+ pid_file = path
77
+ }
78
+ parser.on('--help', 'Prints this message and quit.') {
79
+ puts parser.help
80
+ exit 0
81
+ }
82
+ parser.on('--capi', 'see also FunctionDatabase.') {|path|
83
+ capi = true
84
+ }
85
+ begin
86
+ parser.parse!
87
+ rescue OptionParser::ParseError => err
88
+ $stderr.puts err.message
89
+ $stderr.puts parser.help
90
+ exit 1
91
+ end
92
+
93
+ set_srcdir.call srcdir_root unless srcdir
94
+
95
+ unless baseurl
96
+ $stderr.puts "missing base URL. Use --baseurl"
97
+ exit 1
98
+ end
99
+ unless dbpath || autop
100
+ $stderr.puts "missing database path. Use --database"
101
+ exit 1
102
+ end
103
+ unless datadir
104
+ $stderr.puts "missing datadir. Use --datadir"
105
+ exit 1
106
+ end
107
+ unless themedir
108
+ $stderr.puts "missing themedir. Use --themedir"
109
+ exit 1
110
+ end
111
+ if pid_file
112
+ if File.exist?(pid_file)
113
+ $stderr.puts "There is still #{pid_file}. Is another process running?"
114
+ exit 1
115
+ end
116
+ pid_file = File.expand_path(pid_file)
117
+ end
118
+
119
+ $LOAD_PATH.unshift libdir
120
+ require 'bitclust/app'
121
+
122
+ if debugp
123
+ params[:Logger] = WEBrick::Log.new($stderr, WEBrick::Log::DEBUG)
124
+ params[:AccessLog] = [
125
+ [ $stderr, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
126
+ [ $stderr, WEBrick::AccessLog::REFERER_LOG_FORMAT ],
127
+ [ $stderr, WEBrick::AccessLog::AGENT_LOG_FORMAT ],
128
+ ]
129
+ else
130
+ params[:Logger] = WEBrick::Log.new($stderr, WEBrick::Log::INFO)
131
+ params[:AccessLog] = []
132
+ end
133
+ basepath = URI.parse(baseurl).path
134
+ server = WEBrick::HTTPServer.new(params)
135
+
136
+ if autop
137
+ app = BitClust::App.new(
138
+ :dbpath => Dir.glob("db-*"),
139
+ :baseurl => baseurl,
140
+ :datadir => datadir,
141
+ :templatedir => templatedir,
142
+ :theme => theme,
143
+ :encoding => encoding,
144
+ :capi => capi
145
+ )
146
+ app.interfaces.each do |version, interface|
147
+ server.mount File.join(basepath, version), interface
148
+ end
149
+ server.mount(File.join(basepath, '/'), app)
150
+ else
151
+ viewpath = File.join(basepath, 'view')
152
+ app = BitClust::App.new(
153
+ :viewpath => viewpath,
154
+ :dbpath => dbpath,
155
+ :baseurl => baseurl,
156
+ :datadir => datadir,
157
+ :templatedir => templatedir,
158
+ :theme => theme,
159
+ :encoding => encoding,
160
+ :capi => capi
161
+ )
162
+ app.interfaces.each do |viewpath, interface|
163
+ server.mount viewpath, interface
164
+ end
165
+ # Redirect from '/' to "#{viewpath}/"
166
+ server.mount('/', app)
167
+ end
168
+
169
+ server.mount File.join(basepath, 'theme/'), WEBrick::HTTPServlet::FileHandler, themedir
170
+
171
+ if debugp
172
+ trap(:INT) { server.shutdown }
173
+ else
174
+ WEBrick::Daemon.start do
175
+ trap(:TERM) {
176
+ server.shutdown
177
+ begin
178
+ File.unlink pid_file if pid_file
179
+ rescue Errno::ENOENT
180
+ end
181
+ }
182
+ File.open(pid_file, 'w') {|f| f.write Process.pid } if pid_file
183
+ end
184
+ end
185
+ exit if $".include?("exerb/mkexy.rb")
186
+ if autop && !browser
187
+ case RUBY_PLATFORM
188
+ when /mswin(?!ce)|mingw|cygwin|bccwin/
189
+ browser = "start"
190
+ end
191
+ end
192
+ system("#{browser} http://localhost:#{params[:Port]}/") if browser
193
+ server.start
data/test/run_test.rb ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pathname'
3
+
4
+ gem 'test-unit'
5
+ require 'test/unit'
6
+ require 'test/unit/notify'
7
+ require 'test/unit/rr'
8
+
9
+ base_dir = Pathname.new(__FILE__).dirname.expand_path
10
+ top_dir = (base_dir + '..').expand_path
11
+ lib_dir = top_dir + 'lib'
12
+
13
+ $LOAD_PATH.unshift(lib_dir.to_s)
14
+
15
+ exit Test::Unit::AutoRunner.run(true, base_dir)
@@ -0,0 +1,81 @@
1
+ require 'bitclust'
2
+ require 'bitclust/subcommand'
3
+ require 'stringio'
4
+ require 'tmpdir'
5
+ require 'fileutils'
6
+
7
+ class TestBitClust < Test::Unit::TestCase
8
+ def setup
9
+ @tmpdir = Dir.mktmpdir
10
+ src = "#{@tmpdir}/function/public_func"
11
+ @srcdir = Dir.mkdir File.dirname(src)
12
+ File.open(src, 'w') do |file|
13
+ file.puts <<'HERE'
14
+ filename=test.c
15
+ macro=false
16
+ private=false
17
+ type=VALUE
18
+ name=public_func
19
+ params=()
20
+
21
+
22
+ This is public function.
23
+ HERE
24
+ end
25
+
26
+ @out = StringIO.new
27
+ end
28
+
29
+ def teardown
30
+ FileUtils.rm_r(@tmpdir, :force => true)
31
+ end
32
+
33
+ def search_capi(command, *argv)
34
+ db = BitClust::FunctionDatabase.new(@tmpdir)
35
+ cmd = case command
36
+ when "lookup"
37
+ BitClust::LookupCommand.new
38
+ when "list"
39
+ BitClust::ListCommand.new
40
+ else
41
+ raise "must not happen! command=#{command}"
42
+ end
43
+ @out.string = ""
44
+ $stdout = @out
45
+ begin
46
+ cmd.parse(argv)
47
+ cmd.exec(db, argv)
48
+ ensure
49
+ $stdout = STDOUT
50
+ end
51
+ @out.string
52
+ end
53
+
54
+ def test_list
55
+ assert_equal("public_func\n", search_capi("list", "--function"))
56
+ end
57
+
58
+ def test_lookup
59
+ assert_equal(<<-EOS, search_capi("lookup", "--function=public_func").chomp)
60
+ kind: function
61
+ header: VALUE public_func()
62
+ filename: test.c
63
+
64
+
65
+ This is public function.
66
+ EOS
67
+ end
68
+
69
+ def test_lookup_html
70
+ assert_equal(<<-EOS, search_capi("lookup", "--function=public_func", "--html").chomp)
71
+ <dl>
72
+ <dt>kind</dt><dd>function</dd>
73
+ <dt>header</dt><dd>VALUE public_func()</dd>
74
+ <dt>filename</dt><dd>test.c</dd>
75
+ </dl>
76
+ <p>
77
+ This is public function.
78
+ </p>
79
+ EOS
80
+ end
81
+ end
@@ -0,0 +1,39 @@
1
+ require 'bitclust'
2
+ require 'test/unit'
3
+
4
+ class TestClassEntry < Test::Unit::TestCase
5
+ def setup
6
+ s = <<HERE
7
+ = class Hoge
8
+ alias HogeHoge
9
+ alias HogeHogeHoge
10
+ == Class Methods
11
+ --- hoge
12
+ = class Bar < Hoge
13
+ == Class Methods
14
+ --- bar
15
+ HERE
16
+ @lib, = BitClust::RRDParser.parse(s, 'hoge')
17
+ end
18
+
19
+ def test_entries
20
+ assert_equal(['bar', 'hoge'],
21
+ @lib.fetch_class("Bar").entries(1).map{|e| e.name}.sort)
22
+ end
23
+
24
+ def test_aliases
25
+ assert_equal(['HogeHoge', 'HogeHogeHoge'],
26
+ @lib.fetch_class("Hoge").aliases.map{|e| e.name}.sort)
27
+ end
28
+
29
+ def test_aliasof
30
+ assert_equal(nil, @lib.fetch_class("Hoge").aliasof)
31
+ assert_equal("Hoge", @lib.fetch_class("HogeHoge").aliasof.name)
32
+ end
33
+
34
+ def test_realname
35
+ assert_equal('Hoge', @lib.fetch_class("Hoge").realname)
36
+ assert_equal('Hoge', @lib.fetch_class("HogeHoge").realname)
37
+ end
38
+ end
39
+
@@ -0,0 +1,55 @@
1
+ require 'test/unit'
2
+ require 'bitclust'
3
+ require 'bitclust/functiondatabase'
4
+ require 'tmpdir'
5
+ require 'fileutils'
6
+
7
+ class TesFunctionDatabase < Test::Unit::TestCase
8
+ def setup
9
+ prefix = 'db'
10
+ src = "test.rd"
11
+
12
+ @pwd = Dir.pwd
13
+ Dir.chdir(@tmpdir = Dir.mktmpdir)
14
+ File.open(src, 'w') do |file|
15
+ file.puts <<'HERE'
16
+ --- VALUE func1()
17
+
18
+ some text
19
+
20
+ --- VALUE func2()
21
+
22
+ some text
23
+ HERE
24
+ end
25
+ @db = BitClust::FunctionDatabase.new(prefix)
26
+ @db.transaction {
27
+ @db.update_by_file(src, src)
28
+ }
29
+ end
30
+
31
+ def teardown
32
+ Dir.chdir @pwd
33
+ FileUtils.rm_r(@tmpdir, :force => true)
34
+ end
35
+
36
+ def test_search_functions__function
37
+ result = @db.search_functions('func1')
38
+ assert_not_nil result.first
39
+ assert_equal 1, result.size
40
+ assert_equal 'func1', result.first.name
41
+ end
42
+
43
+ def test_search_functions__functions
44
+ result = @db.search_functions('func')
45
+ assert_not_nil result.first
46
+ assert_equal 2, result.size
47
+ assert_equal %w[func1 func2], result.map(&:name)
48
+ end
49
+
50
+ def test_search_functions__nonexistent
51
+ assert_raise(BitClust::FunctionNotFound) do
52
+ @db.search_functions('nonexistent')
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,31 @@
1
+ require 'bitclust'
2
+ require 'test/unit'
3
+ require 'bitclust/libraryentry'
4
+
5
+ class TestLibraryEntry < Test::Unit::TestCase
6
+ include BitClust
7
+
8
+ def setup
9
+ s = <<DOC
10
+ require hoge/bar
11
+ require hoge/baz
12
+ require hoge/bar
13
+
14
+ = class Hoge
15
+ == Class Methods
16
+ --- hoge
17
+ = class Bar < Hoge
18
+ == Class Methods
19
+ --- bar
20
+ DOC
21
+ @lib_entry, @db = BitClust::RRDParser.parse(s, 'hoge')
22
+ @sublibrary = LibraryEntry.new(@db, 'testsub')
23
+ end
24
+
25
+ def test_sublibrary
26
+ @lib_entry.sublibrary(@sublibrary)
27
+ assert_equal(['testsub'], @lib_entry.sublibraries.map(&:name))
28
+ @lib_entry.sublibrary(@sublibrary)
29
+ assert_equal(['testsub'], @lib_entry.sublibraries.map(&:name))
30
+ end
31
+ end
@@ -0,0 +1,81 @@
1
+ require 'test/unit'
2
+ require 'bitclust'
3
+ require 'bitclust/methoddatabase'
4
+
5
+ class TestMethodDatabase < Test::Unit::TestCase
6
+ def setup
7
+ @prefix = 'db'
8
+ @root = 'src'
9
+ setup_files
10
+ @db = BitClust::MethodDatabase.new(@prefix)
11
+ # init database
12
+ @db.init
13
+ @db.transaction {
14
+ [
15
+ %w[version 1.9.1],
16
+ %w[encoding utf-8]
17
+ ].each do |k,v|
18
+ @db.propset(k, v)
19
+ end
20
+ }
21
+ # update database
22
+ @db.transaction {
23
+ @db.update_by_stdlibtree(@root)
24
+ }
25
+ end
26
+
27
+ def teardown
28
+ FileUtils.rm_r([@prefix, @root], :force => true)
29
+ end
30
+
31
+ def test_search_methods__method
32
+ result = @db.search_methods(BitClust::MethodNamePattern.new(nil, nil, 'at_exit'))
33
+ assert_not_nil result.records.first.entry
34
+ assert_equal 1, result.records.size
35
+ assert_equal 'at_exit', result.records.first.entry.name
36
+ end
37
+
38
+ def test_search_methods__methods
39
+ result = @db.search_methods(BitClust::MethodNamePattern.new(nil, nil, 'foo'))
40
+ assert_not_nil result.records.first.entry
41
+ assert_equal 2, result.records.size
42
+ assert_equal %w[Foo Bar], result.records.map(&:entry).map(&:klass).map(&:name)
43
+ assert_equal 'foo', result.records.first.entry.name
44
+ end
45
+
46
+ def test_search_methods__constant
47
+ result = @db.search_methods(BitClust::MethodNamePattern.new(nil, nil, 'AAA'))
48
+ assert_not_nil result.records.first.entry
49
+ assert_equal 1, result.records.size
50
+ assert_equal 'AAA', result.records.first.entry.name
51
+ end
52
+
53
+ private
54
+ def setup_files
55
+ FileUtils.mkdir_p("#{@root}/_builtin")
56
+ File.open("#{@root}/LIBRARIES", 'w+') do |file|
57
+ file.puts '_builtin'
58
+ end
59
+ File.open("#{@root}/_builtin.rd", 'w+') do |file|
60
+ file.puts <<'HERE'
61
+ description
62
+
63
+ = class Foo < Object
64
+ desctiption
65
+ == Instance Methods
66
+ --- foo
67
+ == Constants
68
+ --- AAA
69
+ = class Bar < Object
70
+ == Instance Methods
71
+ --- foo
72
+ = module Kernel
73
+ description
74
+ == Module Functions
75
+ --- at_exit{ ... } -> Proc
76
+ aaa
77
+
78
+ HERE
79
+ end
80
+ end
81
+ end