gr8 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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