tefil 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f742434ea5277ddf6d1c079d8d5fcf9f1525cb0c
4
+ data.tar.gz: 50abba718b395336d1f00d6e3f75149374356f21
5
+ SHA512:
6
+ metadata.gz: 95669e49a633c958998c4117958e210306ba09831d9c2599f59259f1e436e68b41d6feeb35294842e5c52a46724bc650e7691456005cfbc7d74e64343b22e397
7
+ data.tar.gz: e4bf80f0b585940963a01cc13c65ae31c30f5b1b92354c6cd734b0f3cb288586753cb16bc5870af877693dfe226b3fc74871fdeda83695565630e202b6e6aae1
data/CHANGES CHANGED
@@ -1,6 +1,17 @@
1
- = vasputils changelog
1
+ = tefil changelog
2
2
 
3
- == Master
3
+ == Master (for 0.1.1)
4
+
5
+ == Version 0.1.0 [2015-07-07] released
6
+ * Add bin/packpercent .
7
+ * Add usage banner.
8
+ * Rename bin/formcolumn to columnform
9
+ * Add bin/indentstat .
10
+ * Add bin/indentconv .
11
+ * Module Tefil converted to class Tefil
12
+ * Delete require "tempfile" for Ruby 2.1
13
+ * Rewrite test to use capture_stdout
14
+ * Remove Tefil::TextFilterBase.filter (class method)
4
15
 
5
16
  == Version 0.0.3 [2013-05-10] released
6
17
  * Rename TextFilter to Tefil
data/Gemfile CHANGED
@@ -6,9 +6,10 @@ source "http://rubygems.org"
6
6
  # Add dependencies to develop your gem here.
7
7
  # Include everything needed to run rake, tests, features, etc.
8
8
  group :development do
9
- gem "rdoc", "~> 3.12"
10
- gem "bundler", "~> 1.3.5"
11
- gem "jeweler", "~> 1.8.3"
9
+ gem "rdoc", "~> 4.0.1"
10
+ gem "bundler", "~> 1.9.2"
11
+ gem "jeweler", "~> 2.0.1"
12
+ gem "capture_stdout", "~> 0.0.1"
12
13
  gem "simplecov", ">= 0"
13
14
  gem "builtinextension", ">= 0.1.0"
14
15
  #gem "psych", ">= 0"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 0.1.0
@@ -3,8 +3,6 @@
3
3
  #
4
4
  # USAGE: formcolumn [options] files ...
5
5
 
6
- INPUT_SEPARATOR = /\s+/
7
-
8
6
 
9
7
  require "pp"
10
8
  require "optparse"
@@ -14,18 +12,14 @@ require "tefil"
14
12
  # option analysis
15
13
  OPTIONS = {:separator => ' '}
16
14
  op = OptionParser.new
15
+ op.banner = [
16
+ "Usage: #{File.basename("#{__FILE__}")} [options] [files]",
17
+ ].join("\n")
17
18
  op.on("-o" , "--overwrite" , "Overwrite."){ OPTIONS[:overwrite] = true}
18
19
  op.on("-s char", "--separator=char", "Indicate separator."){|val| OPTIONS[:separator] = val}
19
20
  op.on("-l" , "--left-alline" , "Left alline."){ OPTIONS[:left] = true}
20
21
  op.parse!(ARGV)
21
22
 
22
- module Tefil
23
- def self.process_stream(in_io, out_io)
24
- rows = in_io.readlines.map { |line| line.strip.split(INPUT_SEPARATOR) }
25
- Tefil::ColumnFormer.form(rows, out_io, OPTIONS[:separator], OPTIONS[:left])
26
- end
27
- end
28
-
29
23
  OPTIONS[:overwrite] ||= false
30
24
 
31
- Tefil.run(ARGV, OPTIONS[:overwrite])
25
+ Tefil::ColumnFormer.new(OPTIONS).filter(ARGV)
data/bin/indentconv ADDED
@@ -0,0 +1,41 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require "pp"
5
+ require "optparse"
6
+ require "rubygems"
7
+ require "tefil"
8
+
9
+ # option analysis
10
+ OPTIONS = {}
11
+ op = OptionParser.new
12
+ op.banner = [
13
+ "Usage: #{File.basename("#{__FILE__}")} old_width new_width [options] [files]",
14
+ "Example: #{File.basename("#{__FILE__}")} 4 2 [options] [files]",
15
+ ].join("\n")
16
+ op.on("-o" , "--overwrite" , "Overwrite."){ OPTIONS[:overwrite] = true}
17
+ #op.on("-m", "--minimum", "Show only minimum indent (not zero)."){ OPTIONS[:minimum] = true}
18
+ #op.on("-l" , "--left-alline" , "Left alline."){ OPTIONS[:left] = true}
19
+ op.parse!(ARGV)
20
+
21
+ #数値として識別できない文字列(to_i で 0 になる)はタブにする。
22
+ num = ARGV.shift.to_i
23
+ if num == 0
24
+ old_char = "\t"
25
+ old_width = 1
26
+ else
27
+ old_char = ' '
28
+ old_width = num
29
+ end
30
+
31
+ num = ARGV.shift.to_i
32
+ if num == 0
33
+ new_char = "\t"
34
+ new_width = 1
35
+ else
36
+ new_char = ' '
37
+ new_width = num
38
+ end
39
+
40
+ f = Tefil::IndentConverter.new(old_char, old_width, new_char, new_width, OPTIONS)
41
+ f.filter(ARGV)
data/bin/indentstat ADDED
@@ -0,0 +1,24 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require "pp"
5
+ require "optparse"
6
+ require "rubygems"
7
+ require "tefil"
8
+
9
+
10
+ # option analysis
11
+ options = {}
12
+ op = OptionParser.new
13
+ #op.banner = [
14
+ # "Usage: #{File.basename("#{__FILE__}")} [options] [files]",
15
+ #].join("\n")
16
+ op.on("-o" , "--overwrite" , "Overwrite."){ options[:overwrite] = true}
17
+ op.on("-m", "--minimum", "Show minimum width (not zero). If no indent, show '0'."){ options[:minimum] = true}
18
+ op.parse!(ARGV)
19
+
20
+
21
+ options[:overwrite] ||= false
22
+
23
+ is = Tefil::IndentStatistics.new options
24
+ is.filter(ARGV)
data/bin/linesub CHANGED
@@ -6,31 +6,24 @@
6
6
  require "pp"
7
7
  require "optparse"
8
8
  require "rubygems"
9
+ require "tempfile"
9
10
  #gem "tefil"
10
11
  require "tefil.rb"
11
12
 
12
13
  ## option analysis
13
- OPTIONS = {}
14
+ options = {}
14
15
  op = OptionParser.new
15
- op.on("-o" , "--overwrite" , "Overwrite."){ OPTIONS[:overwrite] = true}
16
- op.on("-g" , "--global" , "Globally substitute."){ OPTIONS[:global] = true}
16
+ op.banner = [
17
+ "Usage: #{File.basename("#{__FILE__}")} old_str new_str [options] [files]",
18
+ ].join("\n")
19
+ op.on("-o" , "--overwrite" , "Overwrite."){ options[:overwrite] = true}
20
+ op.on("-g" , "--global" , "Globally substitute."){ options[:global] = true}
17
21
  op.parse!(ARGV)
18
22
 
19
- OLD_PATTERN = ARGV.shift
20
- NEW_STR = ARGV.shift
23
+ old_str = ARGV.shift
24
+ new_str = ARGV.shift
21
25
 
22
- module Tefil
23
- def self.process_stream(in_io, out_io)
24
- in_io.each do |line|
25
- if OPTIONS[:global]
26
- out_io.puts line.gsub(OLD_PATTERN, NEW_STR)
27
- else
28
- out_io.puts line.sub(OLD_PATTERN, NEW_STR)
29
- end
30
- end
31
- end
32
- end
26
+ options[:overwrite] ||= false
33
27
 
34
- OPTIONS[:overwrite] ||= false
35
-
36
- Tefil.run(ARGV, OPTIONS[:overwrite])
28
+ tf = Tefil::LineSubstituter.new(old_str, new_str, options)
29
+ tf.filter(ARGV)
data/bin/percentpack ADDED
@@ -0,0 +1,26 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+ #
4
+ # USAGE: linesub [options] str0 str1 files ...
5
+
6
+ require "pp"
7
+ require "optparse"
8
+ require "rubygems"
9
+ #gem "tefil"
10
+ require "tefil.rb"
11
+
12
+ ## option analysis
13
+ options = {}
14
+ op = OptionParser.new
15
+ op.banner = [
16
+ "Usage: #{File.basename("#{__FILE__}")} [options] [files]",
17
+ " E.g., echo '%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB' | packpercent" #サンプル
18
+ ].join("\n")
19
+ op.on("-o" , "--overwrite" , "Overwrite."){ options[:overwrite] = true}
20
+ op.parse!(ARGV)
21
+
22
+ options[:overwrite] ||= false
23
+ options[:overwrite]
24
+
25
+ tf = Tefil::PercentPacker.new(options)
26
+ tf.filter(ARGV)
data/bin/zshescape ADDED
@@ -0,0 +1,24 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+ #
4
+ # USAGE: lineescapezsh [options] files ...
5
+
6
+ require "pp"
7
+ require "optparse"
8
+ require "rubygems"
9
+ #gem "tefil"
10
+ require "tefil.rb"
11
+
12
+ ## option analysis
13
+ options = {}
14
+ op = OptionParser.new
15
+ op.banner = [
16
+ "Usage: #{File.basename("#{__FILE__}")} [options] [files]",
17
+ ].join("\n")
18
+ op.on("-o" , "--overwrite" , "Overwrite."){ options[:overwrite] = true}
19
+ op.parse!(ARGV)
20
+
21
+ options[:overwrite] ||= false
22
+
23
+ tf = Tefil::ZshEscaper.new(options)
24
+ tf.filter(ARGV)
@@ -1,18 +1,37 @@
1
1
  #! /usr/bin/env ruby
2
2
  # coding: utf-8
3
3
 
4
+ INPUT_SEPARATOR = /\s+/
5
+
6
+ class String
7
+ #http://www.techscore.com/blog/2012/12/25/
8
+ def mb_ljust(width, padding=' ')
9
+ output_width = each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+)
10
+ padding_size = [0, width - output_width].max
11
+ self + padding * padding_size
12
+ end
13
+
14
+ def mb_rjust(width, padding=' ')
15
+ output_width = each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+)
16
+ padding_size = [0, width - output_width].max
17
+ padding * padding_size + self
18
+ end
19
+ end
20
+
21
+
4
22
  #
5
23
  #
6
24
  #
7
- module Tefil::ColumnFormer
8
- def self.form(matrix, io = $stdout, separator = " ", left = false)
25
+ class Tefil::ColumnFormer < Tefil::TextFilterBase
26
+
27
+ def form(matrix, io = $stdout, separator = " ", left = false)
9
28
  #Obtain max length for each column.
10
29
  max_lengths = []
11
30
  matrix.each do |row|
12
31
  row.each_with_index do |item, index|
13
32
  item = item.to_s
14
33
  max_lengths[index] ||= 0
15
- size = item.size
34
+ size = print_size(item)
16
35
  max_lengths[index] = size if max_lengths[index] < size
17
36
  end
18
37
  end
@@ -20,15 +39,28 @@ module Tefil::ColumnFormer
20
39
  #Output
21
40
  matrix.each do |row|
22
41
  new_items = []
23
-
24
- form_left = ""
25
- form_left = "-" if left
26
-
27
42
  row.each_with_index do |item, index|
28
- new_items[index] = sprintf("%#{form_left}#{max_lengths[index]}s", item)
43
+ method = :mb_rjust
44
+ method = :mb_ljust if left
45
+ new_items[index] = item.send(method, max_lengths[index])
29
46
  end
30
47
  io.puts new_items.join(separator).sub(/ +$/, "")
31
48
  end
32
49
  end
50
+
51
+
52
+ private
53
+
54
+ def process_stream(in_io, out_io)
55
+ rows = in_io.readlines.map do |line|
56
+ line.strip.split(INPUT_SEPARATOR)
57
+ end
58
+ form(rows, out_io, OPTIONS[:separator], OPTIONS[:left])
59
+ end
60
+
61
+ def print_size(string)
62
+ string.each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+)
63
+ end
64
+
33
65
  end
34
66
 
@@ -0,0 +1,24 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ class Tefil::IndentConverter < Tefil::TextFilterBase
5
+ def initialize(old_char, old_width, new_char, new_width, options)
6
+ @old_width = old_width
7
+ @new_width = new_width
8
+ @old_char = old_char
9
+ @new_char = new_char
10
+ super(options)
11
+ end
12
+
13
+ def process_stream(in_io, out_io)
14
+ in_io.readlines.each do |line|
15
+ /^(#{@old_char}*)(.*)$/ =~ line
16
+ indent = $1
17
+ body = $2
18
+ new_indent = indent.size * @new_width / @old_width
19
+ out_io.puts "#{@new_char * new_indent}#{body}"
20
+ end
21
+ end
22
+ end
23
+
24
+
@@ -0,0 +1,47 @@
1
+ class Tefil::IndentStatistics < Tefil::TextFilterBase
2
+
3
+ HISTGRAM_LIMIT = 50
4
+
5
+ def initialize(options = {})
6
+ options[:smart_filename] = true
7
+ @minimum = options[:minimum]
8
+ super(options)
9
+ end
10
+
11
+ def process_stream(in_io, out_io)
12
+ frequencies = {}
13
+ in_io.readlines.each do |line|
14
+ #/^(\s*)/ =~ line #改行文字が含まれる。
15
+ /^( *)/ =~ line
16
+ width = $1.size
17
+ frequencies[width] ||= 0
18
+ frequencies[width] += 1
19
+ end
20
+
21
+ if @minimum
22
+ frequencies.delete(0)
23
+ output = frequencies.keys.min
24
+ output = 0 if frequencies.empty?
25
+ else
26
+ output = ''
27
+ output = "\n" if ARGV.size >= 2
28
+ output += self.histgram(frequencies)
29
+ end
30
+
31
+ out_io.puts output
32
+ end
33
+
34
+ def histgram(frequencies)
35
+ result = ''
36
+ max = frequencies.values.max
37
+ frequencies.keys.sort.each do |key|
38
+ num = frequencies[key]
39
+ num = num * HISTGRAM_LIMIT / max if max > HISTGRAM_LIMIT
40
+ result += sprintf("%2d|", key)
41
+ result += "*" * num
42
+ result += "\n"
43
+ end
44
+ result
45
+ end
46
+ end
47
+
@@ -0,0 +1,17 @@
1
+ class Tefil::LineSubstituter < Tefil::TextFilterBase
2
+ def initialize(old_str, new_str, options = {})
3
+ @old_str = old_str
4
+ @new_str = new_str
5
+ @global = options[:global]
6
+ super(options)
7
+ end
8
+
9
+ def process_stream(in_io, out_io)
10
+ in_io.each do |line|
11
+ method = :sub
12
+ method = :gsub if @global
13
+ out_io.puts line.send(method, @old_str, @new_str)
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,23 @@
1
+ class Tefil::PercentPacker < Tefil::TextFilterBase
2
+ def process_stream(in_io, out_io)
3
+ in_io.each do |line|
4
+ old_chars = line.split("")
5
+
6
+ new_str = ""
7
+ new_index = 0
8
+ old_index = 0
9
+ while old_index < old_chars.size
10
+ if old_chars[old_index] == "%"
11
+ new_str += [old_chars[(old_index +1) .. (old_index + 2)].join].pack("H*")
12
+ old_index += 2
13
+ else
14
+ new_str += old_chars[old_index]
15
+ end
16
+ old_index += 1
17
+ new_index += 1
18
+ end
19
+ out_io.print new_str
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,109 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+ #
4
+ #require "tefil"
5
+ #require "tempfile"
6
+ #require "tmpdir"
7
+
8
+ # Module that provide a framework for text filters.
9
+ #
10
+ # Provide basic functions for a command like below:
11
+ # Input:
12
+ # - Without indicating filenames like "command.rb",
13
+ # input data is eaten from STDIN.
14
+ # - With indicating filenames like "command.rb *.txt",
15
+ # input data is eaten from indicated files in order.
16
+ # When the file which is in the filenames does not exist,
17
+ # output report to STDERR.
18
+ #
19
+ # Output:
20
+ # - Without indicating "--overwrite" option,
21
+ # output to STDOUT even from many files.
22
+ # - With indicating "--overwrite" option,
23
+ # - With indicating input files,
24
+ # overwrite each file.
25
+ # - Without indicating input files,
26
+ # output to STDOUT in one stream.
27
+ #
28
+ # If indicated file(s) not found,
29
+ # this program notify on stderr and does not throw an exception.
30
+ #
31
+ class Tefil::TextFilterBase
32
+
33
+ class NotRedefinedMethodError < Exception; end
34
+ class TypeError < Exception; end
35
+
36
+ def initialize(options = {})
37
+ @overwrite = options[:overwrite]
38
+ @smart_filename = options[:smart_filename]
39
+ end
40
+
41
+ # 保持している入力ファイル対して、順に処理を実行する。
42
+ # filenames.size が 0 ならば STDIN からの入力を待つことになる。
43
+ #
44
+ # STDIN からの入力だった場合、出力先は必ず STDOUT になる。
45
+ # STDIN からの入力ではない、すなわちファイル入力であった場合、
46
+ # - overwrite_flag が false ならば STDOUT に出力する。
47
+ # - overwrite_flag が true ならば 個々のファイルに上書きする。
48
+ #
49
+ # Process of each file is defined in 'process_stream' method.
50
+ #
51
+ #def self.filter(filenames, options)
52
+ # self.class.new(options).filter(filenames)
53
+ #end
54
+
55
+ #line ごとのファイル名表示はここでは提供せず、したければ process_stream で作る。
56
+ #grep のように行数を表示するなど、複雑な表示をすることもありうる。
57
+ def filter(filenames)
58
+ @num_files = filenames.size
59
+ input_io = $stdin
60
+ output_io = $stdout
61
+ if filenames.size == 0
62
+ process_stream( input_io, output_io)
63
+ else
64
+ filenames.each do |filename|
65
+ @filename = filename
66
+ input_io = File.open(filename, "r")
67
+ output_io = Tempfile.new("tefil", "/tmp") if @overwrite
68
+ smart_filename(output_io)
69
+ begin
70
+ process_stream(input_io, output_io)
71
+ rescue ArgumentError, Errno::EISDIR
72
+ $stderr.puts $!
73
+ next
74
+ end
75
+
76
+ if @overwrite
77
+ output_io.open
78
+ File.open(filename, "w") do |overwrite_io|
79
+ output_io.each { |line| overwrite_io.puts(line) }
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ # @smart_filename が真, かつ
89
+ # @overwrite が 偽, かつ
90
+ # filter メソッドに渡されたファイル数が複数のときには
91
+ # 処理中のファイル名を output_io に出力する。
92
+ def smart_filename(output_io)
93
+ if @smart_filename && (! @overwrite) && (@num_files >= 2)
94
+ output_io.puts("#{@filename}:")
95
+ end
96
+ end
97
+
98
+
99
+ # Process a file.
100
+ # An argument 'in_io' indicates an io (file handle) for input.
101
+ # Another argument 'out_io' indicates an io (file handle) for output.
102
+ # This method must be redefined in a subclass or be overridden.
103
+ # If not redefined, raise an exception Tefil::NotRedefinedMethodError.
104
+ def process_stream(in_io, out_io)
105
+ raise NotRedefinedMethodError
106
+ end
107
+
108
+ end
109
+
@@ -0,0 +1,12 @@
1
+ gem "builtinextension"
2
+ require "string/escapezsh"
3
+
4
+ class Tefil::ZshEscaper < Tefil::TextFilterBase
5
+ def process_stream(in_io, out_io)
6
+ in_io.each do |line|
7
+ out_io.puts line.escape_zsh
8
+ end
9
+ end
10
+ end
11
+
12
+
data/lib/tefil.rb CHANGED
@@ -1,83 +1,12 @@
1
1
  #! /usr/bin/env ruby
2
2
  # coding: utf-8
3
3
 
4
- require "tempfile"
5
-
6
- # Module that provide a framework for text filters.
7
- #
8
- # Provide basic functions for a command like below:
9
- # Input:
10
- # - Without indicating filenames like "command.rb",
11
- # input data is eaten from STDIN.
12
- # - With indicating filenames like "command.rb *.txt",
13
- # input data is eaten from indicated files in order.
14
- # When the file which is in the filenames does not exist,
15
- # output report to STDERR.
16
- #
17
- # Output:
18
- # - Without indicating "--overwrite" option,
19
- # output to STDOUT even from many files.
20
- # - With indicating "--overwrite" option,
21
- # - With indicating input files,
22
- # overwrite each file.
23
- # - Without indicating input files,
24
- # output to STDOUT in one stream.
25
- #
26
- # If indicated file(s) not found,
27
- # this program notify on stderr and does not throw an exception.
28
- #
29
- module Tefil
30
-
31
- class NotRedefinedMethodError < Exception; end
32
- class TypeError < Exception; end
33
-
34
- # 保持している入力ファイル対して、順に処理を実行する。
35
- # filenames.size が 0 ならば STDIN からの入力を待つことになる。
36
- #
37
- # STDIN からの入力だった場合、出力先は必ず STDOUT になる。
38
- # STDIN からの入力ではない、すなわちファイル入力であった場合、
39
- # - overwrite_flag が false ならば STDOUT に出力する。
40
- # - overwrite_flag が true ならば 個々のファイルに上書きする。
41
- #
42
- # Process of each file is defined in 'process_stream' method.
43
- #
44
- def self.run(filenames, overwrite_flag = false)
45
- #p self; exit
46
- if filenames.size == 0
47
- self.process_stream( $stdin, $stdout )
48
- else
49
- #p filenames
50
- filenames.each do |filename|
51
- if overwrite_flag
52
- tempfile = Tempfile.new("tefil", "/tmp")
53
- File.open(filename, "r") do |input_file|
54
- self.process_stream(input_file, tempfile)
55
- end
56
- tempfile.close
57
- tempfile.open
58
- File.open(filename, "w") do |output_file|
59
- tempfile.each { |line| output_file.puts(line) }
60
- end
61
- else
62
- File.open(filename, "r") do |input_file|
63
- self.process_stream(input_file, $stdout)
64
- end
65
- end
66
- end
67
- end
68
- end
69
-
70
- private
71
-
72
- # Process a file.
73
- # An argument 'in_io' indicates an io (file handle) for input.
74
- # Another argument 'out_io' indicates an io (file handle) for output.
75
- # This method must be redefined in a subclass or be overridden.
76
- # If not redefined, raise an exception Tefil::NotRedefinedMethodError.
77
- def self.process_stream(in_io, out_io)
78
- raise NotRedefinedMethodError
79
- end
80
-
81
- end
4
+ module Tefil; end
5
+ require "tefil/textfilterbase"
82
6
 
83
7
  require "tefil/columnformer"
8
+ require "tefil/indentconverter"
9
+ require "tefil/indentstatistics"
10
+ require "tefil/linesubstituter"
11
+ require "tefil/percentpacker"
12
+ require "tefil/zshescaper"