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 +7 -0
- data/CHANGES +13 -2
- data/Gemfile +4 -3
- data/VERSION +1 -1
- data/bin/{formcolumn → columnform} +4 -10
- data/bin/indentconv +41 -0
- data/bin/indentstat +24 -0
- data/bin/linesub +12 -19
- data/bin/percentpack +26 -0
- data/bin/zshescape +24 -0
- data/lib/tefil/columnformer.rb +40 -8
- data/lib/tefil/indentconverter.rb +24 -0
- data/lib/tefil/indentstatistics.rb +47 -0
- data/lib/tefil/linesubstituter.rb +17 -0
- data/lib/tefil/percentpacker.rb +23 -0
- data/lib/tefil/textfilterbase.rb +109 -0
- data/lib/tefil/zshescaper.rb +12 -0
- data/lib/tefil.rb +7 -78
- data/tefil.gemspec +95 -0
- data/test/columnformer/sample.txt +2 -0
- data/test/helper.rb +1 -0
- data/test/indentconv/sample0.txt +11 -0
- data/test/indentconv/sample1.txt +11 -0
- data/test/indentstat/indent4.txt +11 -0
- data/test/indentstat/sample0.txt +11 -0
- data/test/indentstat/sample1.txt +11 -0
- data/test/percentpack/sample.txt +1 -0
- data/test/test_columnformer.rb +26 -3
- data/test/test_indentconverter.rb +44 -0
- data/test/test_indentstatistics.rb +38 -0
- data/test/test_linesubstituter.rb +45 -0
- data/test/test_percentpacker.rb +29 -0
- data/test/test_textfilterbase.rb +182 -0
- data/test/test_zshescaper.rb +29 -0
- data/test/zshescape/sample.txt +2 -0
- metadata +70 -47
- data/bin/lineedit +0 -38
- data/bin/lineescapezsh +0 -30
- data/test/test_tefil.rb +0 -130
    
        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 | 
            -
            =  | 
| 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", "~>  | 
| 10 | 
            -
              gem "bundler", "~> 1. | 
| 11 | 
            -
              gem "jeweler", "~>  | 
| 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 | 
| 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. | 
| 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 | 
            -
             | 
| 14 | 
            +
            options = {}
         | 
| 14 15 | 
             
            op = OptionParser.new
         | 
| 15 | 
            -
            op. | 
| 16 | 
            -
             | 
| 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 | 
            -
             | 
| 20 | 
            -
             | 
| 23 | 
            +
            old_str = ARGV.shift
         | 
| 24 | 
            +
            new_str = ARGV.shift
         | 
| 21 25 |  | 
| 22 | 
            -
             | 
| 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 | 
            -
             | 
| 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)
         | 
    
        data/lib/tefil/columnformer.rb
    CHANGED
    
    | @@ -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 | 
            -
             | 
| 8 | 
            -
             | 
| 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 | 
| 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 | 
            -
                     | 
| 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 | 
            +
             | 
    
        data/lib/tefil.rb
    CHANGED
    
    | @@ -1,83 +1,12 @@ | |
| 1 1 | 
             
            #! /usr/bin/env ruby
         | 
| 2 2 | 
             
            # coding: utf-8
         | 
| 3 3 |  | 
| 4 | 
            -
             | 
| 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"
         |