tefil 0.0.3 → 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.
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"