gr8 0.1.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.
@@ -0,0 +1,87 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ task :default => :test
4
+
5
+
6
+ desc "run test scripts"
7
+ task :test do
8
+ sh "ruby -Itest test/oktest.rb -ss test/*_test.rb"
9
+ end
10
+
11
+
12
+ desc "build gem file"
13
+ task :build do
14
+ ver = _get_1st_argument(:build, "version")
15
+ dir = "build/gr8-#{ver}"
16
+ rm_rf dir
17
+ #
18
+ spec = _load_gemspec_file("gr8.gemspec")
19
+ spec.files.each do |fpath|
20
+ new_fpath = "#{dir}/#{fpath}"
21
+ dirpath = File.dirname(new_fpath)
22
+ mkdir_p dirpath unless File.exist?(dirpath)
23
+ cp fpath, new_fpath
24
+ end
25
+ #
26
+ Dir.chdir(dir) do
27
+ spec.files.each do |fpath|
28
+ _edit_file(fpath, ver) {|s|
29
+ s.gsub!(/\$[R]elease:.*?\$/, "$\Release: #{ver} $")
30
+ s.gsub!(/\$[R]elease\$/, "$\Release: #{ver} $")
31
+ s
32
+ }
33
+ end
34
+ sh "gem build gr8.gemspec"
35
+ mv "gr8-#{ver}.gem", ".."
36
+ end
37
+ #
38
+ puts "** created: #{dir}.gem"
39
+ end
40
+
41
+
42
+ desc "release preparation"
43
+ task :release => [:test, :build] do
44
+ ver = _get_1st_argument(:build, "version")
45
+ dir = "build/gr8-#{ver}"
46
+ gemfile = "gr8-#{ver}.gem"
47
+ puts ""
48
+ print "** Are you sure to release #{gemfile}? [y/N] "
49
+ answer = $stdin.gets()
50
+ if answer =~ /\A[yY]/
51
+ sh "git tag #{ver}"
52
+ sh "git push --tags"
53
+ Dir.chdir "build" do
54
+ sh "gem push #{gemfile}"
55
+ end
56
+ else
57
+ $stderr.puts "** Canceled."
58
+ end
59
+ end
60
+
61
+
62
+ ## helper functions
63
+
64
+ def _get_1st_argument(task, argname="argument")
65
+ if ARGV.length == 1
66
+ raise ArgumentError.new("rake #{task}: #{argname} required.")
67
+ end
68
+ arg = ARGV[1]
69
+ ARGV.slice(1, ARGV.length).each{|v| task(v.intern) { } }
70
+ return arg
71
+ end
72
+
73
+ def _load_gemspec_file(filename)
74
+ require "rubygems"
75
+ spec = Gem::Specification::load(filename)
76
+ return spec
77
+ end
78
+
79
+ def _edit_file(fpath, ver)
80
+ File.open(fpath, 'r+') do |f|
81
+ s = f.read()
82
+ s2 = yield s
83
+ f.rewind()
84
+ f.truncate(0)
85
+ f.write(s2)
86
+ end
87
+ end
data/bin/gr8 ADDED
@@ -0,0 +1,657 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ ###
5
+ ### gr8 -- a great command-line utility powered by Ruby
6
+ ###
7
+ ### $Release: 0.1.0 $
8
+ ### $Copyright: copyright(c) 2015 kuwata-lab.com all rights reserved $
9
+ ### $License: MIT License $
10
+ ###
11
+
12
+ require "optparse"
13
+
14
+
15
+ def fu
16
+ #; [!ktccp] returns FileUtils class object.
17
+ require "fileutils" unless defined?(FileUtils)
18
+ FileUtils
19
+ end
20
+
21
+
22
+ class String
23
+
24
+ def q
25
+ #; [!ejo5y] quotes string with single-quoation.
26
+ #; [!ageyj] escapes single-quotation characters.
27
+ "'%s'" % self.gsub(/'/) { "\\'" }
28
+ end
29
+
30
+ def qq
31
+ #; [!wwvll] quotes string with double-quotation.
32
+ #; [!rc66j] escapes double-quotation characters.
33
+ '"%s"' % self.gsub(/"/) { '\\"' }
34
+ end
35
+
36
+ end
37
+
38
+
39
+ class Object
40
+
41
+ ## [Experimental]
42
+ ## With Object#_, you can use '_' character as each value
43
+ ## in block argument of map() or select().
44
+ ## For example, 'map{|s|s+".bkp"}' can be 'map{_+".bkp"}'.
45
+ ## See 'transform()', 'map()' or 'select()' for details.
46
+ def _
47
+ #; [!wvemx] returns self object.
48
+ self
49
+ end
50
+
51
+ end
52
+
53
+
54
+ module Enumerable
55
+
56
+ def transform(&block)
57
+ #; [!peitw] similar to map() or collect(), make each item as self in block.
58
+ collect {|x| x.instance_exec(x, &block) }
59
+ end
60
+ alias xf transform
61
+
62
+ alias __map map
63
+ def map(&block)
64
+ #; [!zfmcx] each item is available as self in block of map().
65
+ __map {|x| x.instance_exec(x, &block) }
66
+ end
67
+
68
+ alias __select select
69
+ def select(&block)
70
+ #; [!41hap] each item is available as self in block of select().
71
+ __select {|x| x.instance_exec(x, &block) }
72
+ end
73
+
74
+ def sum
75
+ #; [!9izc1] returns sum of numbers.
76
+ inject(0, :+)
77
+ end
78
+
79
+ def sum_i
80
+ #; [!01ehd] returns sum of integers, converting values into integer.
81
+ inject(0) {|t, x| t + x.to_i }
82
+ end
83
+
84
+ def sum_f
85
+ #; [!kplnt] returns sum of floats, converting values into float.
86
+ inject(0.0) {|t, x| t + x.to_f }
87
+ end
88
+
89
+ def avg
90
+ #; [!pvi8h] returnns average of numbers.
91
+ #; [!poidi] returns nil when no numbers.
92
+ i = 0
93
+ sum = inject(0) {|t, n| i += 1; t + n }
94
+ i == 0 ? nil : sum.to_f / i
95
+ end
96
+
97
+ def avg_i
98
+ #; [!btiat] returns average of numbers, converting values into integer.
99
+ #; [!892q9] returns nil when no numbers.
100
+ i = 0
101
+ sum = inject(0) {|t, x| i += 1; t + x.to_i }
102
+ i == 0 ? nil : sum.to_f / i
103
+ end
104
+
105
+ def avg_f
106
+ #; [!oqpmc] returns average of numbers, converting values into float.
107
+ #; [!9bckq] returns nil when no numbers.
108
+ i = 0
109
+ sum = inject(0) {|t, x| i += 1; t + x.to_f }
110
+ i == 0 ? nil : sum.to_f / i
111
+ end
112
+
113
+ def xsplit(pat=nil)
114
+ #; [!1pz77] splits each lines with pattern.
115
+ #; [!wte7b] if block given, use its result as index.
116
+ if block_given?
117
+ idx = yield
118
+ collect {|s| s.split(pat)[idx] }
119
+ else
120
+ collect {|s| s.split(pat) }
121
+ end
122
+ end
123
+
124
+ def sed(pat, str=nil, &block)
125
+ #; [!c7m34] replaces all patterns found in each line with str or block.
126
+ if block_given?
127
+ collect {|s| s.sub(pat, &block) }
128
+ else
129
+ collect {|s| s.sub(pat, str) }
130
+ end
131
+ end
132
+
133
+ def gsed(pat, str=nil, &block)
134
+ #; [!9lzjv] replaces first pattern found in each line with str or block.
135
+ if block_given?
136
+ collect {|s| s.gsub(pat, &block) }
137
+ else
138
+ collect {|s| s.gsub(pat, str) }
139
+ end
140
+ end
141
+
142
+ def paths(&block)
143
+ #; [!t55ce] collects Pathname objects when block argument is not passed.
144
+ #; [!yjkm5] yields Pathname objects when block argument is passed.
145
+ #; [!4kppy] self is Patname object in block argument.
146
+ require "pathname" unless defined?(Pathname)
147
+ if block_given?
148
+ collect {|s| x = Pathname(s); x.instance_exec(x, &block) }
149
+ else
150
+ collect {|s| Pathname(s) }
151
+ end
152
+ end
153
+
154
+ def edit(verbose=true, encoding='utf-8', &block)
155
+ edit_i(nil, verbose, encoding, &block)
156
+ end
157
+
158
+ def edit_i(suffix, verbose=true, encoding='utf-8', &block)
159
+ require "fileutils" unless defined?(FileUtils)
160
+ arity = block.arity
161
+ collect {|fpath|
162
+ fpath.strip!
163
+ msg = nil
164
+ if File.file?(fpath)
165
+ #; [!lpncu] creates backup file with suffix spedified.
166
+ if suffix && ! suffix.empty?
167
+ bkup_fpath = "#{fpath}#{suffix}"
168
+ FileUtils.mv(fpath, bkup_fpath)
169
+ FileUtils.cp(bkup_fpath, fpath)
170
+ end
171
+ #; [!ur9mj] opens file with utf-8 encoding.
172
+ File.open(fpath, 'r+', encoding: encoding) do |f|
173
+ s = f.read()
174
+ s1 = s + ""
175
+ s1.object_id != s.object_id or raise "** assertion failed"
176
+ #; [!qqegl] file content and file path are passed to block argument.
177
+ #; [!d8dxv] make content as self in block argument.
178
+ s2 = s.instance_exec(s, fpath, &block)
179
+ #; [!9g7re] edit file when content changed.
180
+ if s1 != s2
181
+ f.rewind()
182
+ f.truncate(0)
183
+ f.write(s2.to_s)
184
+ msg = "Edit: '#{fpath}'" if verbose
185
+ #; [!exzkz] don't edit file when content not changed.
186
+ else
187
+ msg = "NotChanged: '#{fpath}'" if verbose
188
+ end
189
+ end
190
+ else
191
+ #; [!k9d31] skips if file not exist.
192
+ #; [!6m49n] skips if file is not a file.
193
+ if ! File.exist?(fpath)
194
+ msg = "Skip: '#{fpath}' does not exist."
195
+ else
196
+ msg = "Skip: '#{fpath}' is not a file."
197
+ end
198
+ end
199
+ msg
200
+ }.reject {|x| x.nil? }
201
+ end
202
+
203
+ ## experimentals
204
+
205
+ def move_to(verbose=true, &block) #:experimental:
206
+ __copy_or_move_to("Move", verbose, false, false, "move_to", &block)
207
+ end
208
+
209
+ def move_to!(verbose=true, &block) #:experimental:
210
+ __copy_or_move_to("Move", verbose, true, false, "move_to!", &block)
211
+ end
212
+
213
+ def mkdir_and_move_to(verbose=true, &block) #:experimental:
214
+ __copy_or_move_to("Move", verbose, false, true, "mkdir_and_move_to", &block)
215
+ end
216
+
217
+ def mkdir_and_move_to!(verbose=true, &block) #:experimental:
218
+ __copy_or_move_to("Move", verbose, true, true, "mkdir_and_move_to!", &block)
219
+ end
220
+
221
+ def copy_to(verbose=true, &block) #:experimental:
222
+ __copy_or_move_to("Copy", verbose, false, false, "copy_to", &block)
223
+ end
224
+
225
+ def copy_to!(verbose=true, &block) #:experimental:
226
+ __copy_or_move_to("Copy", verbose, true, false, "copy_to!", &block)
227
+ end
228
+
229
+ def mkdir_and_copy_to(verbose=true, &block) #:experimental:
230
+ __copy_or_move_to("Copy", verbose, false, true, "mkdir_and_copy_to", &block)
231
+ end
232
+
233
+ def mkdir_and_copy_to!(verbose=true, &block) #:experimental:
234
+ __copy_or_move_to("Copy", verbose, true, true, "mkdir_and_copy_to!", &block)
235
+ end
236
+
237
+ def rename_as(verbose=true, &block) #:experimental:
238
+ __copy_or_rename_as("Rename", verbose, false, false, "rename_as", &block)
239
+ end
240
+
241
+ def rename_as!(verbose=true, &block) #:experimental:
242
+ __copy_or_rename_as("Rename", verbose, true, false, "rename_as!", &block)
243
+ end
244
+
245
+ def mkdir_and_rename_as(verbose=true, &block) #:experimental:
246
+ __copy_or_rename_as("Rename", verbose, false, true, "mkdir_and_rename_as", &block)
247
+ end
248
+
249
+ def mkdir_and_rename_as!(verbose=true, &block) #:experimental:
250
+ __copy_or_rename_as("Rename", verbose, true, true, "mkdir_and_rename_as!", &block)
251
+ end
252
+
253
+ def copy_as(verbose=true, &block) #:experimental:
254
+ __copy_or_rename_as("Copy", verbose, false, false, "copy_as", &block)
255
+ end
256
+
257
+ def copy_as!(verbose=true, &block) #:experimental:
258
+ __copy_or_rename_as("Copy", verbose, true, false, "copy_as!", &block)
259
+ end
260
+
261
+ def mkdir_and_copy_as(verbose=true, &block) #:experimental:
262
+ __copy_or_rename_as("Copy", verbose, false, true, "mkdir_and_copy_as", &block)
263
+ end
264
+
265
+ def mkdir_and_copy_as!(verbose=true, &block) #:experimental:
266
+ __copy_or_rename_as("Copy", verbose, true, true, "mkdir_and_copy_as!", &block)
267
+ end
268
+
269
+ private
270
+
271
+ def __copy_or_move_to(action, verbose_p, overwrite_p, mkdir_p, meth, &block)
272
+ #; [!n0ubo] block argument is required.
273
+ #; [!40se5] block argument is required.
274
+ #; [!k74dw] block argument is required.
275
+ #; [!z9yus] block argument is required.
276
+ block or
277
+ raise ArgumentError.new("#{meth}(): block argument required.")
278
+ require "fileutils" unless defined?(FileUtils)
279
+ existing = nil
280
+ collect {|fpath|
281
+ #; [!qqzqz] trims target file name.
282
+ fpath.strip!
283
+ #; [!nnud9] destination directory name is derived from target file name.
284
+ dirpath = fpath.instance_exec(fpath, &block)
285
+ #; [!ey3e4] if target directory name is nil or empty, skip moving file.
286
+ if ! dirpath || dirpath.empty?
287
+ msg = "Skip: target directory name is nil or empty (file: '#{fpath}')"
288
+ #; [!i5jt6] if destination directory exists, move file to it.
289
+ elsif dirpath == existing || File.directory?(dirpath)
290
+ msg = nil
291
+ #; [!azqgk] if there is a file that name is same as desination directory, skip.
292
+ elsif File.exist?(dirpath)
293
+ msg = "Skip: directory '#{dirpath}' not a directory"
294
+ #; [!b9d4m] if destination directory doesn't exist, creates it.
295
+ elsif mkdir_p
296
+ FileUtils.mkdir_p(dirpath)
297
+ msg = nil
298
+ #; [!rqu5q] if destinatio directory doesn't exist, skip.
299
+ else
300
+ msg = "Skip: directory '#{dirpath}' not exist"
301
+ end
302
+ #
303
+ if msg.nil?
304
+ new_fpath = File.join(dirpath, File.basename(fpath))
305
+ #; [!0gq9h] if destination file already exist, skip.
306
+ exist_p = File.exist?(new_fpath)
307
+ if exist_p && ! overwrite_p
308
+ msg = "Skip: destination file '#{new_fpath}' already exist."
309
+ #; [!ebdqh] overwrite destination file even if it exists.
310
+ else
311
+ #; [!fa5y0] copy files or directories into destination directory.
312
+ if action == "Copy"
313
+ FileUtils.cp_r(fpath, dirpath)
314
+ #; [!d9vxl] move files or directories into destination directory.
315
+ elsif action == "Move"
316
+ FileUtils.mv(fpath, dirpath)
317
+ else
318
+ raise "** unreachable"
319
+ end
320
+ existing = dirpath
321
+ #; [!n7a1q] prints target file and destination directory when verbose mode.
322
+ #; [!itsh0] use 'Move!' instead of 'Move' when overwriting existing file.
323
+ msg = "#{action}#{exist_p ? '!' : ''}: '#{fpath}' => '#{dirpath}'" if verbose_p
324
+ end
325
+ end
326
+ msg
327
+ }.reject {|s| s.nil? }
328
+ end
329
+
330
+ def __copy_or_rename_as(action, verbose_p, overwrite_p, mkdir_p, meth, &block)
331
+ #; [!ignfm] block argument is required.
332
+ block or
333
+ raise ArgumentError.new("#{meth}(): block argument required.")
334
+ require "fileutils" unless defined?(FileUtils)
335
+ existing = nil
336
+ collect {|fpath|
337
+ #; [!qqzqz] trims target file name.
338
+ fpath.strip!
339
+ #; [!nnud9] destination file name is derived from source file name.
340
+ new_fpath = fpath.instance_exec(fpath, &block)
341
+ #
342
+ overwritten = false
343
+ #; [!dkejf] if target directory name is nil or empty, skips renaming file.
344
+ if ! new_fpath || new_fpath.empty?
345
+ msg = "Skip: target file name is nil or empty (file: '#{fpath}')"
346
+ #
347
+ elsif File.exist?(new_fpath)
348
+ #; [!1yzjd] if target file or directory already exists, removes it before renaming file.
349
+ if overwrite_p
350
+ FileUtils.rm_rf(new_fpath)
351
+ overwritten = true
352
+ msg = nil
353
+ #; [!8ap57] if target file or directory already exists, skips renaming files.
354
+ else
355
+ msg = "Skip: target file '#{new_fpath}' already exists."
356
+ end
357
+ #
358
+ else
359
+ #; [!qhlc8] if directory of target file already exists, renames file.
360
+ dirpath = File.dirname(new_fpath)
361
+ if existing == dirpath || File.directory?(dirpath)
362
+ existing = dirpath
363
+ msg = nil
364
+ #; [!sh2ti] if directory of target file not exist, creates it.
365
+ elsif mkdir_p
366
+ FileUtils.mkdir_p(dirpath)
367
+ existing = dirpath
368
+ msg = nil
369
+ #; [!gg9w1] if directory of target file not exist, skips renaming files.
370
+ else
371
+ msg = "Skip: directory of target file '#{new_fpath}' not exist."
372
+ end
373
+ end
374
+ if msg.nil?
375
+ #; [!0txp4] copy files or directories.
376
+ if action == "Copy"
377
+ FileUtils.cp_r(fpath, new_fpath)
378
+ #; [!xi8u5] rename files or directories.
379
+ elsif action == "Rename"
380
+ FileUtils.move(fpath, new_fpath)
381
+ else
382
+ raise "** unreachable"
383
+ end
384
+ #; [!vt24y] prints source and destination file path when verbose mode.
385
+ #; [!gd9j9] use 'Rename!' instead of 'Rename' when overwriting existing file.
386
+ #; [!8warb] use 'Copy!' instead of 'Copy' when overwriting exsiting file.
387
+ msg = "#{action}#{overwritten ? '!': ''}: '#{fpath}' => '#{new_fpath}'" if verbose_p
388
+ end
389
+ msg
390
+ }.reject {|s| s.nil? }
391
+ end
392
+
393
+ end
394
+
395
+
396
+ class Enumerator::Lazy
397
+
398
+ alias __map map
399
+ def map(&block)
400
+ #; [!drgky] each item is available as self in block of map().
401
+ __map {|x| x.instance_exec(x, &block) }
402
+ end
403
+
404
+ alias __select select
405
+ def select(&block)
406
+ #; [!uhqz2] each item is available as self in block of map().
407
+ __select {|x| x.instance_exec(x, &block) }
408
+ end
409
+
410
+ end
411
+
412
+
413
+ module Gr8
414
+
415
+ VERSION = "$Release: 0.1.0 $".split()[1]
416
+
417
+ WEBSITE_URL = "https://kwatch.github.io/gr8/"
418
+
419
+ HELP = <<"END"
420
+ %{script} -- great command-line utility powered by Ruby
421
+
422
+ Usage:
423
+ %{script} [options] ruby-code
424
+
425
+ Options:
426
+ -h, --help : print help
427
+ --doc : open document with browser
428
+ -v, --version : print version
429
+ -r lib[,lib2,...] : require libraries
430
+ -F[regexp] : separate each line into fields
431
+ -C N : select column (1-origin)
432
+
433
+ Example:
434
+ $ cat data
435
+ Haruhi 100
436
+ Mikuru 80
437
+ Yuki 120
438
+ $ cat data | %{script}s 'map{|s| s.split()[1]}' ## self == $stdin.lazy
439
+ 100
440
+ 80
441
+ 120
442
+ $ cat data | %{script}s 'map{split()[1].to_i}.sum' ## map{self} == map{|s| s}
443
+ 300
444
+ $ cat data | %{script}s 'map{split[1]}.sum_i' ## .sum_i == map(&:to_i).sum
445
+ 300
446
+ $ cat data | %{script}s -C2 'sum_i' ## -C2 == map{split[1]}
447
+ 300
448
+
449
+ See #{WEBSITE_URL} for details and examples.
450
+
451
+ END
452
+
453
+
454
+ class EnumWrapper
455
+ include Enumerable
456
+
457
+ def initialize(enum, separator=nil, column=nil)
458
+ @_base = enum
459
+ @_separator = separator
460
+ @_column = column
461
+ end
462
+
463
+ def each
464
+ #; [!hloy1] splits each line into array.
465
+ #; [!c22km] chomps each lin before splitting.
466
+ #; [!m411f] selects column when column number specified.
467
+ sep = @_separator
468
+ col = @_column
469
+ if @_column
470
+ index = @_column - 1
471
+ @_base.each {|s| s.chomp!; yield s.split(sep)[index] }
472
+ else
473
+ @_base.each {|s| s.chomp!; yield s.split(sep) }
474
+ end
475
+ end
476
+
477
+ end
478
+
479
+
480
+ class App
481
+
482
+ def run(*args)
483
+ #
484
+ begin
485
+ opts = parse_options(args)
486
+ rescue ::OptionParser::ParseError => ex
487
+ #$stderr.puts "ERROR (#{script_name()}): #{ex.args.is_a?(Array) ? ex.args.join(' ') : ex.args}: #{ex.reason}"
488
+ $stderr.puts "ERROR (#{script_name()}): #{ex.message}"
489
+ return 1
490
+ end
491
+ #
492
+ output = handle_opts(opts)
493
+ if output
494
+ puts output
495
+ return 0
496
+ end
497
+ #
498
+ errmsg = validate_args(args)
499
+ if errmsg
500
+ $stderr.puts "ERROR (#{script_name()}): #{errmsg}"
501
+ return 1
502
+ end
503
+ #; [!8hk3g] option '-F': separates each line into array.
504
+ #; [!vnwu6] option '-C': select colum.
505
+ if opts[:separator] || opts[:column]
506
+ sep = opts[:separator]
507
+ stdin = EnumWrapper.new($stdin, sep == true ? nil : sep, opts[:column]).lazy
508
+ else
509
+ stdin = $stdin.lazy
510
+ define_singleton_methods_on(stdin)
511
+ end
512
+ #; [!r69d6] executes ruby code with $stdin.lazy as self.
513
+ code = args[0]
514
+ filename = "<#{script_name()}>"
515
+ val = stdin.instance_eval(code, filename)
516
+ #; [!hsvnd] prints nothing when result is nil.
517
+ #; [!eiaa6] prints each item when result is Enumerable.
518
+ #; [!6pfay] prints value when result is not nil nor Enumerable.
519
+ case val
520
+ when nil ; nil
521
+ when Enumerable ; val.each {|x| puts x }
522
+ else ; puts val
523
+ end
524
+ #; [!h5wln] returns 0 as status code when executed successfully.
525
+ return 0
526
+ end
527
+
528
+ def main(argv=ARGV)
529
+ #; [!w9kb8] exit with status code 0 when executed successfully.
530
+ #; [!nbag1] exit with status code 1 when execution failed.
531
+ args = argv.dup
532
+ status = run(*args)
533
+ exit status
534
+ end
535
+
536
+ private
537
+
538
+ def script_name
539
+ @script_name ||= File.basename($0)
540
+ end
541
+
542
+ def parse_options(args)
543
+ #; [!5efp5] returns Hash object containing command-line options.
544
+ opts = {}
545
+ parser = OptionParser.new
546
+ parser.on("-h", "--help") {|v| opts[:help] = true }
547
+ parser.on( "--doc") {|v| opts[:doc] = true }
548
+ parser.on("-v", "--version") {|v| opts[:version] = true }
549
+ parser.on("-r lib[,lib2,..]") {|v| opts[:require] = v }
550
+ parser.on("-F[sep]") do |v|
551
+ #; [!jt4y5] option '-F': separator is omissible.
552
+ #; [!jo4gm] option '-F': error when invalid regular expression.
553
+ begin
554
+ opts[:separator] = v.nil? ? true : Regexp.new(v)
555
+ rescue RegexpError
556
+ raise invalid_argument_error(v, "invalid regular expression")
557
+ end
558
+ end
559
+ #parser.on("-C N", Integer) {|v| opts[:column] = v }
560
+ parser.on("-C N") do |v|
561
+ #; [!7ruq0] option '-C': argument should be an integer.
562
+ #; [!6x3dp] option '-C': argument should be >= 1.
563
+ begin
564
+ opts[:column] = Integer(v)
565
+ rescue ArgumentError => ex
566
+ raise invalid_argument_error(v, "integer expected")
567
+ end
568
+ if opts[:column] <= 0
569
+ raise invalid_argument_error(v, "column number should be >= 1")
570
+ end
571
+ end
572
+ #; [!wdzss] modifies args.
573
+ parser.parse!(args)
574
+ return opts
575
+ end
576
+
577
+ def invalid_argument_error(optarg, reason)
578
+ err = OptionParser::InvalidArgument.new(optarg)
579
+ err.reason = reason
580
+ err
581
+ end
582
+
583
+ def handle_opts(opts)
584
+ #; [!33bj3] option '-h', '--help': prints help message.
585
+ if opts[:help]
586
+ return HELP % {script: script_name()}
587
+ end
588
+ #; [!7dvjg] option '--doc': opens website with browser.
589
+ if opts[:doc]
590
+ cmd = command_to_open_website()
591
+ system cmd
592
+ return cmd
593
+ end
594
+ #; [!2tfh5] option '-v', '--version': prints version string.
595
+ if opts[:version]
596
+ return VERSION
597
+ end
598
+ #; [!1s7wm] option '-r': requires libraries.
599
+ if opts[:require]
600
+ opts[:require].split(/,/).each do |libname|
601
+ libname.strip!
602
+ require libname
603
+ end
604
+ end
605
+ nil
606
+ end
607
+
608
+ def command_to_open_website()
609
+ url = WEBSITE_URL
610
+ case RUBY_PLATFORM
611
+ when /darwin/ ; "open #{url}"
612
+ when /linux/ ; "xdg-open #{url}"
613
+ when /bsd/ ; "xdg-open #{url}" # really?
614
+ when /win|mingw/i ; "start #{url}"
615
+ else ; "open #{url}" # or error?
616
+ end
617
+ end
618
+
619
+ def validate_args(args)
620
+ #; [!7wqyh] prints error when no argument.
621
+ #; [!bwiqv] prints error when too many argument.
622
+ if args.length == 0
623
+ return "argument required."
624
+ elsif args.length > 1
625
+ return "too many arguments."
626
+ end
627
+ nil
628
+ end
629
+
630
+ def define_singleton_methods_on(stdin)
631
+ (class << stdin; self; end).class_eval do
632
+ #; [!zcxh1] removes '\n' from each line automatically.
633
+ alias __each_orig each
634
+ def each
635
+ __each_orig {|s| s.chomp!; yield s }
636
+ end
637
+ alias __each_new each
638
+ #; [!i7npb] $1, $2, ... are available in grep() block argument.
639
+ #; [!vkt64] lines are chomped automatically in grep() if block is not given.
640
+ def grep(pattern, &block)
641
+ if pattern.is_a?(Regexp) && block
642
+ (class << self; self; end).class_eval { alias each __each_orig }
643
+ end
644
+ super(pattern, &block)
645
+ end
646
+ end
647
+ end
648
+
649
+ end
650
+
651
+
652
+ end
653
+
654
+
655
+ #if __FILE__ == $0
656
+ Gr8::App.new.main() unless $DONT_RUN_GR8_APP
657
+ #end