bel 0.3.0-x86-mingw32 → 0.3.1-x86-mingw32
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.
- checksums.yaml +8 -8
- data/README.md +23 -23
- data/bel.gemspec +0 -1
- data/bin/bel +318 -173
- data/bin/bel2rdf.rb +20 -12
- data/bin/bel_parse.rb +2 -2
- data/bin/bel_upgrade.rb +11 -13
- data/lib/bel/2.0/libbel.so +0 -0
- data/lib/bel/2.1/libbel.so +0 -0
- data/lib/bel/2.2/libbel.so +0 -0
- data/lib/bel/vendor/trollop.rb +861 -0
- data/lib/bel/version.rb +1 -1
- metadata +3 -16
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDY2ZjZlNDk3YWU1NTRhY2MyYWY2NDY4YTcwNDdiMjFiNjBiOTg5NQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjI2ODBjNzYzOWQ2MmFjMGY5ZDBhOGJmODYwYmQ1ZTMxN2ZlZWRjMA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZTI1MzUxYWM0NjAzZDdjOTBjMjk5MGYxYmFjNGQwYjdmN2MxMWIwMjIwNjA2
|
10
|
+
NDc5MmJkOGQzODBjYTJlZjQ3MDliNmZkNGQ0MjM3YzQyNDc1Mzg1YmYwNGQ4
|
11
|
+
YWQ2MTFkZDM2MTdiMmFmMzgxYjhkZDJhNTNkMmZlY2UyYjNhZmQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
OTFiNGUwMWQ4OTg3MmI4MTlhZTQ3NzdkMGQzNzY4MmE3MDcwZjMzYTJhZWY4
|
14
|
+
Y2M4MzlmNjg1Y2ZjYjkzMDY0ODFhYjUxOTE4MDE5MmUwODQwM2ViNzRiZjFk
|
15
|
+
ZjJlMWQ1ZDFjNzliMWVmMDNjMjVhYWRhOGYxZTIxODFiOWRiZjA=
|
data/README.md
CHANGED
@@ -39,29 +39,29 @@ executable commands
|
|
39
39
|
|
40
40
|
```bash
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
42
|
+
Usage: bel [OPTIONS]... COMMAND
|
43
|
+
A set of commands to process BEL knowledge.
|
44
|
+
|
45
|
+
Subcommands:
|
46
|
+
|
47
|
+
bel2rdf
|
48
|
+
compare
|
49
|
+
parse
|
50
|
+
rdfschema
|
51
|
+
summarize
|
52
|
+
translate
|
53
|
+
upgrade
|
54
|
+
|
55
|
+
|
56
|
+
bel 0.3.1
|
57
|
+
Copyright (C) 2015 OpenBEL
|
58
|
+
Apache License, Version 2.0, January 2004
|
59
|
+
http://www.apache.org/licenses/
|
60
|
+
|
61
|
+
Options:
|
62
|
+
-v, --verbose Verbose output.
|
63
|
+
-e, --version Print version and exit
|
64
|
+
-h, --help Show this message
|
65
65
|
```
|
66
66
|
|
67
67
|
|
data/bel.gemspec
CHANGED
data/bin/bel
CHANGED
@@ -1,210 +1,355 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require '
|
2
|
+
require 'erb'
|
3
3
|
require 'bel'
|
4
|
+
require 'bel/vendor/trollop'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# settings
|
12
|
-
subcommand_option_handling :normal
|
13
|
-
arguments :strict
|
14
|
-
|
15
|
-
# global options
|
16
|
-
desc 'Enables verbose output.'
|
17
|
-
switch [:verbose], :default_value => false
|
18
|
-
|
19
|
-
# helpers
|
20
|
-
def which_io(input_file)
|
21
|
-
case input_file
|
22
|
-
when nil
|
23
|
-
$stdin
|
24
|
-
when '-'
|
25
|
-
$stdin
|
26
|
-
else
|
27
|
-
input_file
|
28
|
-
end
|
29
|
-
end
|
6
|
+
VERSION_BANNER = <<-VERSION
|
7
|
+
bel #{BEL::VERSION}
|
8
|
+
Copyright (C) 2015 OpenBEL
|
9
|
+
Apache License, Version 2.0, January 2004
|
10
|
+
http://www.apache.org/licenses/
|
11
|
+
VERSION
|
30
12
|
|
31
|
-
|
32
|
-
|
33
|
-
desc 'Converts BEL to RDF.'
|
34
|
-
long_desc <<-LONGDESC
|
35
|
-
Convers BEL script content into RDF triples. The RDFS schema for BEL can also be included in output.
|
36
|
-
LONGDESC
|
37
|
-
command :bel2rdf do |c|
|
38
|
-
c.flag [:b, :bel],
|
39
|
-
:desc => 'The BEL script file to convert to RDF.'
|
40
|
-
c.flag [:f, :format],
|
41
|
-
:desc => 'The RDF format to serialize.',
|
42
|
-
:default_value => 'ntriples',
|
43
|
-
:must_match => ['ntriples','nquads','turtle']
|
44
|
-
c.switch [:s, :schema], :default_value => false
|
45
|
-
c.action do |global_options,options,args|
|
46
|
-
bel_io = which_io(options[:bel])
|
47
|
-
ARGV.clear.concat([
|
48
|
-
'--format', options[:format]
|
49
|
-
])
|
50
|
-
if bel_io != $stdin
|
51
|
-
ARGV.concat(['--bel', bel_io])
|
52
|
-
end
|
53
|
-
if options[:schema]
|
54
|
-
ARGV.concat(['--schema', options[:schema]])
|
55
|
-
end
|
13
|
+
module BEL
|
14
|
+
module Commands
|
56
15
|
|
57
|
-
|
58
|
-
end
|
59
|
-
end
|
16
|
+
class BelCommand
|
60
17
|
|
18
|
+
attr_reader :global_options
|
61
19
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
long_desc <<-LONGDESC
|
66
|
-
Compare shows a quick breakdown of the differences between knowledge in two BEL script files.
|
67
|
-
LONGDESC
|
68
|
-
arg_name 'file1'
|
69
|
-
arg_name 'file2'
|
70
|
-
command :compare do |c|
|
20
|
+
def initialize(sub_commands)
|
21
|
+
@sub_commands = sub_commands
|
22
|
+
end
|
71
23
|
|
72
|
-
|
73
|
-
|
24
|
+
def run
|
25
|
+
with_this_binding = binding
|
26
|
+
@global_options = Trollop::options do
|
27
|
+
synopsis ERB.new(
|
28
|
+
<<-SYNOPSIS
|
29
|
+
A set of commands to process BEL knowledge.
|
74
30
|
|
75
|
-
|
76
|
-
|
31
|
+
Subcommands:
|
32
|
+
<% @sub_commands.each do |cmd| %>
|
33
|
+
<%= cmd %><% end %>
|
34
|
+
SYNOPSIS
|
35
|
+
).result(with_this_binding)
|
36
|
+
usage "[OPTIONS]... COMMAND"
|
37
|
+
version ::VERSION_BANNER
|
77
38
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
39
|
+
opt :verbose, 'Verbose output.',
|
40
|
+
:long => :verbose,
|
41
|
+
:short => :v
|
42
|
+
stop_on_unknown
|
43
|
+
end
|
82
44
|
|
45
|
+
BelCommand.fail_unless_subcommand
|
46
|
+
end
|
83
47
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
long_desc <<-LONGDESC
|
88
|
-
Show parsed objects from BEL content for debugging purposes. The parsed object name is show along with the BEL script text.
|
89
|
-
LONGDESC
|
90
|
-
command :parse do |c|
|
91
|
-
c.flag [:b, :bel],
|
92
|
-
:desc => 'The BEL script to parse.'
|
93
|
-
c.action do |global_options,options,args|
|
94
|
-
bel_io = which_io(options[:bel])
|
95
|
-
if bel_io != $stdin
|
96
|
-
ARGV.clear.concat(['--bel', bel_io])
|
48
|
+
def self.fail_unless_subcommand
|
49
|
+
Trollop::educate if ARGV.empty?
|
50
|
+
end
|
97
51
|
end
|
98
|
-
require_relative 'bel_parse'
|
99
|
-
end
|
100
|
-
end
|
101
52
|
|
53
|
+
class Bel2rdfCommand
|
102
54
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
55
|
+
def initialize(global_options)
|
56
|
+
@global_options = global_options
|
57
|
+
end
|
58
|
+
|
59
|
+
def run
|
60
|
+
parsed_options =
|
61
|
+
Trollop::options do
|
62
|
+
synopsis "Converts BEL to RDF."
|
63
|
+
usage "[OPTIONS]... bel2rdf [OPTIONS]"
|
64
|
+
|
65
|
+
opt :bel, 'The BEL script to parse and output (Defaults to STDIN).',
|
66
|
+
:type => String,
|
67
|
+
:long => :bel,
|
68
|
+
:short => :b,
|
69
|
+
:default => nil
|
70
|
+
|
71
|
+
opt :format, 'The RDF format to output.',
|
72
|
+
:type => String,
|
73
|
+
:long => :format,
|
74
|
+
:short => :f,
|
75
|
+
:default => 'ntriples'
|
76
|
+
|
77
|
+
opt :schema, 'Includes the BEL RDFS schema in RDF output.',
|
78
|
+
:long => :schema,
|
79
|
+
:short => :s,
|
80
|
+
:default => false
|
81
|
+
end
|
119
82
|
|
83
|
+
ARGV.concat(['--bel', parsed_options[:bel]]) if parsed_options[:bel]
|
84
|
+
ARGV.concat(['--format', parsed_options[:format]]) if parsed_options[:format]
|
85
|
+
ARGV.concat(['--schema', parsed_options[:schema]]) if parsed_options[:schema]
|
120
86
|
|
121
|
-
|
122
|
-
|
123
|
-
desc 'Show summary statistics. for knowledge in provided BEL script.'
|
124
|
-
long_desc <<-LONGDESC
|
125
|
-
Show summary statistics for knowledge in provided BEL script. The output is provided in CSV with an initial header row.
|
126
|
-
LONGDESC
|
127
|
-
command :summarize do |c|
|
128
|
-
c.flag [:b, :bel],
|
129
|
-
:desc => 'The BEL script to parse.'
|
130
|
-
c.action do |global_options,options,args|
|
131
|
-
bel_io = which_io(options[:bel])
|
132
|
-
if bel_io != $stdin
|
133
|
-
ARGV.clear.concat(['--bel', bel_io])
|
87
|
+
require_relative 'bel2rdf'
|
88
|
+
end
|
134
89
|
end
|
135
|
-
require_relative 'bel_summarize'
|
136
|
-
end
|
137
|
-
end
|
138
90
|
|
91
|
+
class CompareCommand
|
92
|
+
|
93
|
+
def initialize(global_options)
|
94
|
+
@global_options = global_options
|
95
|
+
end
|
96
|
+
|
97
|
+
def run
|
98
|
+
parsed_options =
|
99
|
+
Trollop::options do
|
100
|
+
synopsis "Compares knowledge in two BEL script files."
|
101
|
+
usage "[OPTIONS]... compare FILE1 FILE2"
|
102
|
+
end
|
139
103
|
|
140
|
-
#
|
141
|
-
|
142
|
-
desc 'Translates BEL evidence between file formats.'
|
143
|
-
long_desc <<-LONGDESC
|
144
|
-
Translates BEL evidence from one format (e.g. <from_format>) to another format (e.g. <to_format>). If --input-file was not provided then evidence data is read from STDIN (e.g. -).
|
145
|
-
LONGDESC
|
146
|
-
arg_name 'from_format'
|
147
|
-
arg_name 'to_format'
|
148
|
-
command :translate do |c|
|
104
|
+
# Error if two file arguments were not provided.
|
105
|
+
Trollop::educate unless ARGV.size == 2
|
149
106
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
107
|
+
# Error if file arguments are not readable.
|
108
|
+
missing = ARGV.select { |arg|
|
109
|
+
!File.readable?(arg)
|
110
|
+
}
|
111
|
+
if !missing.empty?
|
112
|
+
missing.each do |arg|
|
113
|
+
$stderr.puts %Q{File "#{arg}" is not readable.}
|
114
|
+
end
|
115
|
+
$stderr.puts
|
116
|
+
Trollop::educate
|
117
|
+
end
|
154
118
|
|
155
|
-
|
156
|
-
|
157
|
-
raise "File #{input_io} does not exist or is not readable."
|
119
|
+
ARGV.insert(0, '--verbose') if @global_options[:verbose]
|
120
|
+
require_relative 'bel_compare'
|
158
121
|
end
|
159
122
|
end
|
160
123
|
|
161
|
-
|
124
|
+
class ParseCommand
|
125
|
+
|
126
|
+
def initialize(global_options)
|
127
|
+
@global_options = global_options
|
128
|
+
end
|
129
|
+
|
130
|
+
def run
|
131
|
+
parsed_options =
|
132
|
+
Trollop::options do
|
133
|
+
synopsis "Shows parse of BEL script."
|
134
|
+
usage "[OPTIONS]... parse [OPTIONS]"
|
135
|
+
|
136
|
+
opt :bel, 'The BEL script to parse and output. If not provided the default is to read from standard in (STDIN).',
|
137
|
+
:type => String,
|
138
|
+
:long => :bel,
|
139
|
+
:short => :b,
|
140
|
+
:default => nil
|
141
|
+
end
|
162
142
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
143
|
+
if parsed_options[:bel]
|
144
|
+
ARGV.concat(['--bel', parsed_options[:bel]])
|
145
|
+
else
|
146
|
+
Trollop::educate if !ARGV.empty?
|
147
|
+
end
|
148
|
+
|
149
|
+
require_relative 'bel_parse'
|
150
|
+
end
|
168
151
|
end
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
152
|
+
|
153
|
+
class RdfschemaCommand
|
154
|
+
|
155
|
+
def initialize(global_options)
|
156
|
+
@global_options = global_options
|
157
|
+
end
|
158
|
+
|
159
|
+
def run
|
160
|
+
parsed_options =
|
161
|
+
Trollop::options do
|
162
|
+
synopsis "Outputs the RDFS (e.g. RDF Schema) for BEL."
|
163
|
+
usage "[OPTIONS]... rdfschema [OPTIONS]"
|
164
|
+
|
165
|
+
opt :format, 'The RDF format to output.',
|
166
|
+
:type => String,
|
167
|
+
:long => :format,
|
168
|
+
:short => :f,
|
169
|
+
:default => 'ntriples'
|
170
|
+
end
|
171
|
+
|
172
|
+
formats = ['nquads', 'ntriples', 'turtle']
|
173
|
+
unless formats.include? parsed_options[:format]
|
174
|
+
fmt = parsed_options[:format]
|
175
|
+
$stderr.puts %Q{Format "#{fmt}" must be one of #{formats.join(' ')}.}
|
176
|
+
$stderr.puts
|
177
|
+
Trollop::educate
|
178
|
+
end
|
179
|
+
|
180
|
+
ARGV.concat(['--format', parsed_options[:format]]) if parsed_options[:format]
|
181
|
+
require_relative 'bel_rdfschema'
|
182
|
+
end
|
174
183
|
end
|
175
184
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
185
|
+
class SummarizeCommand
|
186
|
+
|
187
|
+
def initialize(global_options)
|
188
|
+
@global_options = global_options
|
189
|
+
end
|
190
|
+
|
191
|
+
def run
|
192
|
+
parsed_options =
|
193
|
+
Trollop::options do
|
194
|
+
synopsis "Show summary statistics. for knowledge in provided BEL script."
|
195
|
+
usage "[OPTIONS]... summarize [OPTIONS]"
|
196
|
+
|
197
|
+
opt :bel, 'The BEL script to parse and output. If not provided the default is to read from standard in (STDIN).',
|
198
|
+
:type => String,
|
199
|
+
:long => :bel,
|
200
|
+
:short => :b,
|
201
|
+
:default => nil
|
202
|
+
end
|
203
|
+
|
204
|
+
if parsed_options[:bel]
|
205
|
+
ARGV.concat(['--bel', parsed_options[:bel]])
|
206
|
+
else
|
207
|
+
Trollop::educate if !ARGV.empty?
|
208
|
+
end
|
209
|
+
|
210
|
+
require_relative 'bel_summarize'
|
211
|
+
end
|
180
212
|
end
|
181
|
-
end
|
182
|
-
end
|
183
213
|
|
214
|
+
class TranslateCommand
|
215
|
+
|
216
|
+
def initialize(global_options)
|
217
|
+
@global_options = global_options
|
218
|
+
end
|
219
|
+
|
220
|
+
def which_io(input_file)
|
221
|
+
case input_file
|
222
|
+
when nil
|
223
|
+
$stdin
|
224
|
+
when '-'
|
225
|
+
$stdin
|
226
|
+
else
|
227
|
+
input_file
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def run
|
232
|
+
parsed_options =
|
233
|
+
Trollop::options do
|
234
|
+
synopsis "Translates BEL evidence between file formats."
|
235
|
+
usage "[OPTIONS]... translate [OPTIONS] FROM_FORMAT TO_FORMAT"
|
236
|
+
|
237
|
+
opt :'input-file', 'The file containing evidence data to translate. If not specified the default is to read from STDIN.',
|
238
|
+
:type => String,
|
239
|
+
:long => :'input-file',
|
240
|
+
:short => :i,
|
241
|
+
:default => nil
|
242
|
+
end
|
243
|
+
|
244
|
+
input_io = which_io(parsed_options[:'input-file'])
|
245
|
+
|
246
|
+
if ![$stdin, '-'].include?(input_io)
|
247
|
+
unless File.readable?(input_io)
|
248
|
+
$stderr.puts %Q{File "#{input_io}" is not readable.}
|
249
|
+
$stderr.puts
|
250
|
+
Trollop::educate
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
unless ARGV.size == 2
|
255
|
+
$stderr.puts %Q{Formats were not provided.}
|
256
|
+
$stderr.puts
|
257
|
+
Trollop::educate
|
258
|
+
end
|
184
259
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
260
|
+
from_format, to_format = ARGV
|
261
|
+
|
262
|
+
from = BEL::Extension::Format.formatters(from_format)
|
263
|
+
unless from
|
264
|
+
$stderr.puts(
|
265
|
+
from_format ?
|
266
|
+
%Q{The format "#{from_format}" is not available.} :
|
267
|
+
"The from_format argument was not specified."
|
268
|
+
)
|
269
|
+
$stderr.puts
|
270
|
+
Trollop::educate
|
271
|
+
end
|
272
|
+
|
273
|
+
to = BEL::Extension::Format.formatters(to_format)
|
274
|
+
unless to
|
275
|
+
$stderr.puts(
|
276
|
+
to_format ?
|
277
|
+
%Q{The format "#{to_format}" is not available."} :
|
278
|
+
"The to_format argument was not specified."
|
279
|
+
)
|
280
|
+
$stderr.puts
|
281
|
+
Trollop::educate
|
282
|
+
end
|
283
|
+
|
284
|
+
begin
|
285
|
+
BEL::Format.translate(input_io, from_format, to_format, $stdout)
|
286
|
+
ensure
|
287
|
+
$stdout.close
|
288
|
+
end
|
289
|
+
end
|
204
290
|
end
|
205
291
|
|
206
|
-
|
292
|
+
class UpgradeCommand
|
293
|
+
|
294
|
+
def initialize(global_options)
|
295
|
+
@global_options = global_options
|
296
|
+
end
|
297
|
+
|
298
|
+
def run
|
299
|
+
parsed_options =
|
300
|
+
Trollop::options do
|
301
|
+
synopsis "Upgrades BEL knowledge and annotation."
|
302
|
+
usage "[OPTIONS]... upgrade [OPTIONS]"
|
303
|
+
|
304
|
+
opt :bel, 'The BEL script to parse and output. If not provided the default is to read from standard in (STDIN).',
|
305
|
+
:type => String,
|
306
|
+
:long => :bel,
|
307
|
+
:short => :b,
|
308
|
+
:default => nil
|
309
|
+
|
310
|
+
opt :changelog, 'The changelog JSON file or URL.',
|
311
|
+
:type => String,
|
312
|
+
:long => :changelog,
|
313
|
+
:short => :c,
|
314
|
+
:default => 'http://resource.belframework.org/belframework/latest-release/change_log.json'
|
315
|
+
end
|
316
|
+
|
317
|
+
if parsed_options[:bel]
|
318
|
+
ARGV.concat(['--bel', parsed_options[:bel]])
|
319
|
+
else
|
320
|
+
Trollop::educate if !ARGV.empty?
|
321
|
+
end
|
322
|
+
|
323
|
+
ARGV.concat(['--changelog', parsed_options[:changelog]])
|
324
|
+
|
325
|
+
require_relative 'bel_upgrade'
|
326
|
+
end
|
327
|
+
end
|
207
328
|
end
|
208
329
|
end
|
209
330
|
|
210
|
-
|
331
|
+
def find_subcommands_in_module(mod)
|
332
|
+
mod.constants.map(&:to_s).select { |constant|
|
333
|
+
constant.end_with?('Command')
|
334
|
+
}.map { |cmd|
|
335
|
+
cmd.sub(/Command$/, '').downcase
|
336
|
+
}
|
337
|
+
end
|
338
|
+
|
339
|
+
# Run bel command
|
340
|
+
bel_command = BEL::Commands::BelCommand.new(
|
341
|
+
find_subcommands_in_module(BEL::Commands) - ['bel']
|
342
|
+
)
|
343
|
+
bel_command.run
|
344
|
+
|
345
|
+
global_options = bel_command.global_options
|
346
|
+
|
347
|
+
# Run subcommand
|
348
|
+
sub_command = ARGV.shift
|
349
|
+
sub_class = :"#{sub_command.capitalize}Command"
|
350
|
+
if BEL::Commands.const_defined?(sub_class)
|
351
|
+
BEL::Commands.const_get(sub_class).new(global_options).run
|
352
|
+
else
|
353
|
+
$stderr.puts "error: The command #{sub_command} is not valid.\n\n"
|
354
|
+
Trollop::educate
|
355
|
+
end
|