pcut 0.0.1

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.
data/.gemtest ADDED
File without changes
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2012-03-29
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,24 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ bin/pcut
7
+ lib/pcut.rb
8
+ lib/pcut/cli.rb
9
+ lib/pcut/line_parser.rb
10
+ lib/pcut/query.rb
11
+ lib/pcut/range_collector.rb
12
+ lib/pcut/range_index.rb
13
+ script/console
14
+ script/destroy
15
+ script/generate
16
+ spec/lib/pcut/cli_spec.rb
17
+ spec/lib/pcut/line_parser_spec.rb
18
+ spec/lib/pcut/query_spec.rb
19
+ spec/lib/pcut/range_collector_spec.rb
20
+ spec/lib/pcut/range_index_spec.rb
21
+ spec/spec.opts
22
+ spec/spec_helper.rb
23
+ tasks/rspec.rake
24
+ test
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on pcut, see http://pcut.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.rdoc ADDED
@@ -0,0 +1,46 @@
1
+ = pcut
2
+
3
+ * http://github.com/bonar/pcut
4
+
5
+ == DESCRIPTION:
6
+
7
+ Yet another cutting tool.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ == SYNOPSIS:
12
+
13
+ FIX (code sample of usage)
14
+
15
+ == REQUIREMENTS:
16
+
17
+ term-ansicolor
18
+
19
+ == INSTALL:
20
+
21
+ sudo gem install pcut
22
+
23
+ == LICENSE:
24
+
25
+ (The MIT License)
26
+
27
+ Copyright (c) 2012 Nakano Kyohei
28
+
29
+ Permission is hereby granted, free of charge, to any person obtaining
30
+ a copy of this software and associated documentation files (the
31
+ 'Software'), to deal in the Software without restriction, including
32
+ without limitation the rights to use, copy, modify, merge, publish,
33
+ distribute, sublicense, and/or sell copies of the Software, and to
34
+ permit persons to whom the Software is furnished to do so, subject to
35
+ the following conditions:
36
+
37
+ The above copyright notice and this permission notice shall be
38
+ included in all copies or substantial portions of the Software.
39
+
40
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
41
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
43
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
44
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
45
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
46
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/pcut'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'pcut' do
14
+ self.developer 'Nakano Kyohei (bonar)', 'bonar@me.com'
15
+ # self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
16
+ self.rubyforge_name = self.name # TODO this is default value
17
+ self.extra_deps = [
18
+ ['term-ansicolor','>= 1.0.7']
19
+ ]
20
+
21
+ end
22
+
23
+ require 'newgem/tasks'
24
+ Dir['tasks/**/*.rake'].each { |t| load t }
25
+
26
+ # TODO - want other tests/tasks run by default? Add them to the list
27
+ # remove_task :default
28
+ # task :default => [:spec, :features]
data/bin/pcut ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'optparse'
4
+ require 'rubygems'
5
+ require 'pcut'
6
+ require 'pcut/cli'
7
+
8
+ cli = Pcut::CLI.new
9
+
10
+ opt = OptionParser.new
11
+ opt.version = Pcut::VERSION
12
+
13
+ opt.on('-d DELIMITER', 'cut delimiter (tab is the default)') do |v|
14
+ cli.delimiter = v
15
+ cli.joiner = v
16
+ end
17
+ # opt.on('-j JOIN', 'specifies char used to join cut result') do
18
+ # end
19
+ opt.on('-f FIELD',
20
+ "FIELD specifies the list of field to pickup.",
21
+ "example)",
22
+ " single field: -f 2,3,4",
23
+ " range: -f 2- / -f -3",
24
+ " sub query: -f \"2.[/ /, 4].[/./, 2]\""
25
+ ) do |v|
26
+ fields = cli.parse_field(v)
27
+ cli.fields = fields
28
+ end
29
+ opt.on('-q QUOTE', '--quote QUOTE',
30
+ 'QUOTE specifies quoting characters. If quoting characters are specified,',
31
+ 'quoted strings are treated as one string (not separated).',
32
+ 'Variations:',
33
+ ' - D: double quote',
34
+ ' - S: single quote',
35
+ ' - [: []',
36
+ ' - (: ()',
37
+ 'You can specify multiple quotes like: "-q DS[("'
38
+ ) do |v|
39
+ cli.quote = v
40
+ end
41
+ opt.on('-k', '--keep-quote', 'not delete quote chars') do
42
+ cli.keep_quote = true
43
+ end
44
+ opt.on('-p', '--preview', 'show field number header') do
45
+ cli.preview = true
46
+ end
47
+ opt.on('-t', '--vertical', 'print cut result vertically') do
48
+ cli.vertical = true
49
+ end
50
+ opt.on('-n', '--no-color', 'turn colot output off') do
51
+ cli.color = false
52
+ end
53
+
54
+ begin
55
+ opt.parse!(ARGV)
56
+ rescue => e
57
+ $stderr.puts e.to_s
58
+ exit(1)
59
+ end
60
+
61
+ Signal.trap(:INT) { exit(0) }
62
+ cli.start(ARGV.shift)
63
+
data/lib/pcut.rb ADDED
@@ -0,0 +1,6 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module Pcut
5
+ VERSION = '0.0.1'
6
+ end
data/lib/pcut/cli.rb ADDED
@@ -0,0 +1,154 @@
1
+
2
+ require 'rubygems'
3
+ require 'term/ansicolor'
4
+ require 'pcut/line_parser'
5
+ require 'pcut/range_index'
6
+ require 'pcut/range_collector'
7
+ require 'pcut/query'
8
+
9
+ module Pcut
10
+
11
+ class CLI
12
+ include Term::ANSIColor
13
+
14
+ attr_accessor \
15
+ :delimiter,
16
+ :joiner,
17
+ :fields,
18
+ :quote,
19
+ :keep_quote,
20
+ :preview,
21
+ :vertical,
22
+ :color
23
+
24
+ def initialize
25
+ @delimiter = "\t"
26
+ @joiner = @delimiter
27
+ @fields = []
28
+ @quote = nil
29
+ @keep_quote = false
30
+ @preview = false
31
+ @vertical = false
32
+ @color = true
33
+ end
34
+
35
+ def parse_field(str)
36
+ targets = []
37
+
38
+ begin
39
+ field_parser = Pcut::LineParser.new
40
+ field_parser.set_delimiter(",")
41
+ field_parser.set_quote_guard("[")
42
+ field_parser.keep_quotes = true
43
+ fields = field_parser.parse(str)
44
+
45
+ fields.each do |field|
46
+ index = nil
47
+ queries = []
48
+
49
+ query_parser = Pcut::LineParser.new
50
+ query_parser.set_delimiter(".")
51
+ query_parser.set_quote_guard("[")
52
+ query_parser.keep_quotes = true
53
+
54
+ if field =~ /\./
55
+ result = query_parser.parse(field)
56
+ first = result.shift
57
+ index = Pcut::RangeIndex.parse(first)
58
+ result.each do |query_str|
59
+ queries << Pcut::Query.parse(query_str)
60
+ end
61
+ else
62
+ index = Pcut::RangeIndex.parse(field)
63
+ end
64
+ targets << [index, queries]
65
+ end
66
+ rescue => e
67
+ STDOUT.puts e.to_s
68
+ exit(1)
69
+ end
70
+ targets
71
+ end
72
+
73
+ def start(filepath)
74
+ begin
75
+ parser = Pcut::LineParser.new
76
+ parser.set_delimiter(@delimiter)
77
+ @quote.each_char { |c| parser.set_quote_guard(c) } if @quote
78
+ parser.keep_quotes = true if @keep_quote
79
+ rescue => e
80
+ STDOUT.puts e.to_s
81
+ exit(1)
82
+ end
83
+
84
+ io = nil
85
+ if filepath
86
+ begin
87
+ if File.directory?(filepath)
88
+ raise ArgumentError, "#{filepath} is directory"
89
+ end
90
+ io = File.open(filepath, 'r')
91
+ rescue => e
92
+ $stdout.puts e.to_s
93
+ exit(1)
94
+ end
95
+ end
96
+ io ||= STDIN
97
+ io.each_line do |line|
98
+ line.chomp!
99
+ buff = [] # selected fields
100
+ result = parser.parse(line)
101
+
102
+ joiner = @vertical ? "\n" : @joiner
103
+
104
+ # apply -f fields specification
105
+ # (value pickup)
106
+ if !@fields.empty?
107
+ @fields.each do |setting|
108
+ index = setting[0]
109
+ queries = setting[1]
110
+ value = Pcut::RangeCollector.collect(result, index).join(@joiner)
111
+ if queries.empty?
112
+ buff << index_labeL(index.index) + (value || "")
113
+ else
114
+ queries.each do |query|
115
+ last if value.nil?
116
+ sliced = Pcut::RangeCollector.collect(
117
+ value.split(query.delimiter), query.index)
118
+ value = sliced.join(query.delimiter)
119
+ end
120
+ buff << query_label(index.index, queries.size) + (value || "")
121
+ end
122
+ end
123
+ # no @fields is "all the fields"
124
+ else
125
+ result.each_with_index do |field, index|
126
+ buff << index_labeL(index + 1) + (field || "")
127
+ end
128
+ end
129
+
130
+ puts buff.join(joiner)
131
+ puts "" if @vertical
132
+ end
133
+ exit(0)
134
+ end
135
+
136
+ def index_labeL(index)
137
+ return "" unless @preview
138
+ (@color ? red : '') + "[" +
139
+ (@color ? yellow : '' ) + "#{index}" +
140
+ (@color ? red : '') + "]" + (@color ? reset : '')
141
+ end
142
+
143
+ def query_label(index, query_size)
144
+ return "" unless @preview
145
+ (@color ? red : '') + "[" +
146
+ (@color ? yellow : '' ) + "#{index}" +
147
+ (@color ? green : '' ) + "+#{query_size}" +
148
+ (@color ? red : '') + "]" + (@color ? reset : '')
149
+ end
150
+
151
+ end
152
+
153
+ end
154
+
@@ -0,0 +1,95 @@
1
+
2
+ module Pcut
3
+
4
+ class LineParser
5
+
6
+ DOUBLE_QUOTE = "\""
7
+ SINGLE_QUOTE = "'"
8
+
9
+ QUOTES = {
10
+ DOUBLE_QUOTE => [DOUBLE_QUOTE, DOUBLE_QUOTE],
11
+ SINGLE_QUOTE => [SINGLE_QUOTE, SINGLE_QUOTE],
12
+ "D" => [DOUBLE_QUOTE, DOUBLE_QUOTE], # shortcut
13
+ "S" => [SINGLE_QUOTE, SINGLE_QUOTE], # shortcut
14
+ "[" => ["[", "]"],
15
+ "(" => ["(", ")"],
16
+ "<" => ["<", ">"]
17
+ }
18
+ DELIMITER = "\t"
19
+
20
+ attr_reader :quote_guard
21
+ attr_accessor :keep_quotes
22
+
23
+ def initialize
24
+ @quote_guard = {}
25
+ @delimiter = DELIMITER
26
+ @keep_quotes = false # do not delete quoting chars
27
+ end
28
+
29
+ def set_quote_guard(quote)
30
+ unless QUOTES.has_key?(quote)
31
+ raise ArgumentError, "invalid quote: #{quote}"
32
+ end
33
+ @quote_guard[QUOTES[quote].first] = QUOTES[quote]
34
+ end
35
+
36
+ def set_delimiter(char)
37
+ if !char.is_a?(String) || char.size != 1
38
+ raise ArgumentError, "invalid delimiter: #{char.to_s}"
39
+ end
40
+ @delimiter = char
41
+ end
42
+
43
+ def parse(str)
44
+ buffer = nil
45
+ previous_char = nil
46
+ current_quote = nil
47
+ finalizer = nil
48
+ partials = []
49
+
50
+ str.each_char do |c|
51
+
52
+ # check starting quote
53
+ if @quote_guard.include?(c) &&
54
+ previous_char != "\\" # not escaped
55
+ starter = @quote_guard[c].first
56
+
57
+ if !current_quote && c == starter
58
+ current_quote = c
59
+ finalizer = @quote_guard[c].last
60
+ if @keep_quotes
61
+ buffer ||= ""
62
+ buffer += c
63
+ end
64
+ next
65
+ end
66
+ end
67
+
68
+ # check end of quote
69
+ if current_quote && c == finalizer
70
+ current_quote = nil
71
+ finalizer = nil
72
+ if @keep_quotes
73
+ buffer ||= ""
74
+ buffer += c
75
+ end
76
+ next
77
+ end
78
+
79
+ if !current_quote && c == @delimiter
80
+ partials << buffer
81
+ buffer = nil
82
+ elsif current_quote || c != @delimiter
83
+ buffer ||= ""
84
+ buffer += c
85
+ end
86
+ previous_char = c
87
+ end
88
+ partials << buffer if buffer
89
+ partials
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
data/lib/pcut/query.rb ADDED
@@ -0,0 +1,37 @@
1
+
2
+ require 'pcut/range_index'
3
+
4
+ module Pcut
5
+
6
+ class Query
7
+
8
+ FORMAT = %r{\A\s*\[\s*/\\?(.)/\s*,\s*([0-9\-]+)\s*\]\z}
9
+
10
+ attr_accessor :delimiter, :index
11
+
12
+ def initialize(delimiter, index)
13
+ if !delimiter.is_a?(String) || delimiter.size != 1
14
+ raise ArgumentError, "invalid delimiter: #{delimiter}"
15
+ end
16
+ if !index.is_a?(Pcut::RangeIndex)
17
+ raise ArgumentError, "index must be a RangeIndex: #{index}"
18
+ end
19
+
20
+ @delimiter = delimiter
21
+ @index = index
22
+ end
23
+
24
+ def self.parse(str)
25
+ unless str.is_a?(String)
26
+ raise ArgumentError, "#{str.to_s} is not a string"
27
+ end
28
+ unless str =~ FORMAT
29
+ raise ArgumentError, "invalid format: #{str}"
30
+ end
31
+ index = Pcut::RangeIndex.parse($2)
32
+ self.new($1, index)
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,31 @@
1
+
2
+ require 'pcut/range_index'
3
+
4
+ module Pcut
5
+
6
+ class RangeCollector
7
+
8
+ def self.collect(array, range_index)
9
+ unless array.is_a?(Array)
10
+ raise ArgumentError, "#{array.to_s} is not an array"
11
+ end
12
+ unless range_index.is_a?(Pcut::RangeIndex)
13
+ raise ArgumentError, "#{range_index.to_s} is not a range_index"
14
+ end
15
+
16
+ return [] if array.empty?
17
+ array_index = range_index.index - 1
18
+
19
+ if range_index.include_backward?
20
+ array[0..array_index]
21
+ elsif range_index.include_forward?
22
+ array[array_index..-1]
23
+ else
24
+ value = array[array_index]
25
+ value ? [value] : []
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,59 @@
1
+
2
+ module Pcut
3
+
4
+ class RangeIndex
5
+
6
+ FORMAT = %r/\A\s*(\-)?(\d+)(\-)?\s*\z/
7
+
8
+ attr_reader :index
9
+
10
+ def initialize(index, backward, forward)
11
+ unless index.is_a?(Fixnum)
12
+ raise ArgumentError, "invalid index: #{index}"
13
+ end
14
+ unless backward == true || backward == false
15
+ raise ArgumentError, "invalid backward spec: #{backward}"
16
+ end
17
+ unless forward == true || forward == false
18
+ raise ArgumentError, "invalid forward spec: #{forward}"
19
+ end
20
+
21
+ @index = index
22
+ @backward = backward
23
+ @forward = forward
24
+ end
25
+
26
+ def self.parse(str)
27
+ unless str.is_a?(String)
28
+ raise ArgumentError, "#{str.to_s} is not string"
29
+ end
30
+ unless str =~ FORMAT
31
+ raise ArgumentError, "invalid range index expression: #{str}"
32
+ end
33
+
34
+ backward = $1 ? true : false
35
+ forward = $3 ? true : false
36
+ index = $2.to_i
37
+
38
+ if backward && forward
39
+ raise ArgumentError, "backward, forward both specified: #{str}"
40
+ end
41
+ if 0 == index
42
+ raise ArgumentError, "zero cannot be specified: #{str}"
43
+ end
44
+
45
+ self.new(index, backward, forward)
46
+ end
47
+
48
+ def include_backward?
49
+ @backward
50
+ end
51
+
52
+ def include_forward?
53
+ @forward
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/pcut.rb'}"
9
+ puts "Loading pcut gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,50 @@
1
+
2
+ require 'pcut/cli'
3
+
4
+ describe Pcut::CLI do
5
+
6
+ before(:each) do
7
+ @cli = Pcut::CLI.new
8
+ end
9
+
10
+ describe 'parse_field' do
11
+
12
+ it 'can parse single index' do
13
+ targets = @cli.parse_field("3")
14
+ targets.size.should == 1
15
+ targets[0][0].index.should == 3
16
+ targets[0][1].should == []
17
+ end
18
+
19
+ it 'can parse multi index' do
20
+ targets = @cli.parse_field("-4,5-")
21
+ targets.size.should == 2
22
+ targets[0][0].index.should == 4
23
+ targets[0][1].should == []
24
+ targets[1][0].index.should == 5
25
+ targets[1][1].should == []
26
+ end
27
+
28
+ it 'can parse index with sub queries' do
29
+ targets = @cli.parse_field("-4,3.[/ /,4].[/,/,12].[/./,-2],5-")
30
+ targets.size.should == 3
31
+ targets[0][0].index.should == 4
32
+ targets[0][1].should == []
33
+
34
+ targets[1][0].index.should == 3
35
+ targets[1][1].size.should == 3
36
+ targets[1][1][0].delimiter.should == " "
37
+ targets[1][1][0].index.index.should == 4
38
+ targets[1][1][1].delimiter.should == ","
39
+ targets[1][1][1].index.index.should == 12
40
+ targets[1][1][2].delimiter.should == "."
41
+ targets[1][1][2].index.index.should == 2
42
+
43
+ targets[2][0].index.should == 5
44
+ targets[2][1].should == []
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
@@ -0,0 +1,223 @@
1
+
2
+ require 'pcut/line_parser'
3
+
4
+ describe Pcut::LineParser do
5
+
6
+ before(:each) do
7
+ @parser = Pcut::LineParser.new
8
+ end
9
+
10
+ it 'can parse plain line' do
11
+ result = @parser.parse("foo\tbar\tbuz")
12
+ result.size.should == 3
13
+ result[0].should == "foo"
14
+ result[1].should == "bar"
15
+ result[2].should == "buz"
16
+ end
17
+
18
+ it 'returns empty array for empty string' do
19
+ @parser.parse("").should == []
20
+ end
21
+
22
+ it 'returns one element array for string without delimiter' do
23
+ str = "string_without_delimiter"
24
+ @parser.parse(str).should == [str]
25
+ end
26
+
27
+ it 'can handle continuous delimiters' do
28
+ result = @parser.parse("one\t\ttwo\t\t\tthree")
29
+ result.size.should == 6
30
+ result[0].should == "one"
31
+ result[1].should == nil
32
+ result[2].should == "two"
33
+ result[3].should == nil
34
+ result[4].should == nil
35
+ result[5].should == "three"
36
+ end
37
+
38
+ it 'cares about pre/post delimiters' do
39
+ result = @parser.parse("\t\tfolk\tspoon\t\t\t")
40
+ result[0].should == nil
41
+ result[1].should == nil
42
+ result[2].should == "folk"
43
+ result[3].should == "spoon"
44
+ result[4].should == nil
45
+ result[5].should == nil
46
+ end
47
+
48
+ it 'can change delimiter' do
49
+ result = @parser.parse("foo\tbar\thoge,fuga")
50
+ result.should == ["foo", "bar", "hoge,fuga"]
51
+
52
+ @parser.set_delimiter(",")
53
+ result = @parser.parse("foo bar hoge,fuga")
54
+ result.should == ["foo bar hoge", "fuga"]
55
+ end
56
+
57
+ it 'raise error with invalid delimiter' do
58
+ lambda {
59
+ @parser.set_delimiter(nil)
60
+ }.should raise_error(ArgumentError)
61
+
62
+ lambda {
63
+ @parser.set_delimiter("")
64
+ }.should raise_error(ArgumentError)
65
+
66
+ lambda {
67
+ @parser.set_delimiter("ab")
68
+ }.should raise_error(ArgumentError)
69
+ end
70
+
71
+ describe 'quote guard' do
72
+
73
+ it 'can set_quote_guard' do
74
+ @parser.set_quote_guard(Pcut::LineParser::DOUBLE_QUOTE)
75
+ @parser.quote_guard.keys.sort.should == [
76
+ Pcut::LineParser::DOUBLE_QUOTE]
77
+
78
+ @parser.set_quote_guard(Pcut::LineParser::SINGLE_QUOTE)
79
+ @parser.quote_guard.keys.sort.should == [
80
+ Pcut::LineParser::DOUBLE_QUOTE,
81
+ Pcut::LineParser::SINGLE_QUOTE
82
+ ]
83
+ @parser.set_quote_guard("[")
84
+ @parser.quote_guard.keys.sort.should == [
85
+ Pcut::LineParser::DOUBLE_QUOTE,
86
+ Pcut::LineParser::SINGLE_QUOTE,
87
+ "["
88
+ ]
89
+ end
90
+
91
+ it 'understands quoter shortcuts' do
92
+ @parser.set_quote_guard("D")
93
+ @parser.set_delimiter(" ")
94
+ result = @parser.parse("foo \"bar baz\" hoge")
95
+ result.should == [
96
+ "foo",
97
+ "bar baz",
98
+ "hoge"
99
+ ]
100
+ @parser.set_quote_guard("S")
101
+ result = @parser.parse("foo 'bar baz' hoge")
102
+ result.should == [
103
+ "foo",
104
+ "bar baz",
105
+ "hoge"
106
+ ]
107
+ end
108
+
109
+ it 'can keep quotes' do
110
+ @parser.keep_quotes.should be_false
111
+
112
+ @parser.set_delimiter(" ")
113
+ @parser.set_quote_guard("[")
114
+ result = @parser.parse("foo bar [hoge fuga] baz")
115
+ result.should == ["foo", "bar", "hoge fuga", "baz"]
116
+
117
+ @parser.keep_quotes = true
118
+ result = @parser.parse("foo bar [hoge fuga] baz")
119
+ result.should == ["foo", "bar", "[hoge fuga]", "baz"]
120
+ end
121
+
122
+ it 'raises error if quote letter is invalid' do
123
+ lambda {
124
+ @parser.set_quote_guard("A")
125
+ }.should raise_error(ArgumentError)
126
+
127
+ lambda {
128
+ @parser.set_quote_guard()
129
+ }.should raise_error(ArgumentError)
130
+ end
131
+
132
+ it 'can parse line with quote guard' do
133
+ @parser.set_quote_guard(Pcut::LineParser::DOUBLE_QUOTE)
134
+ @parser.set_delimiter(" ")
135
+ @parser.parse('172cm 62kg "Nakano Kyohei" Male')\
136
+ .should == ['172cm', '62kg', 'Nakano Kyohei', 'Male']
137
+
138
+ @parser.set_quote_guard("(")
139
+ @parser.parse('Foo (Bar, Buz) Hoge')\
140
+ .should == ['Foo', 'Bar, Buz', 'Hoge']
141
+ end
142
+
143
+ it 'can parse line with multiple quote guard' do
144
+ @parser.set_quote_guard(Pcut::LineParser::DOUBLE_QUOTE)
145
+ @parser.set_delimiter(" ")
146
+ @parser.set_quote_guard("(")
147
+ @parser.set_quote_guard("[")
148
+ result = @parser.parse(
149
+ 'Bonar "Nakano Kyohei" [Software Engineer] (172 62)')
150
+ result = [
151
+ "Bonar",
152
+ "Nakano Kyohei",
153
+ "Software Engineer",
154
+ "172 62"
155
+ ]
156
+ end
157
+
158
+ it 'ignores quotes which is not specified' do
159
+ @parser.set_quote_guard("(")
160
+ @parser.set_delimiter(" ")
161
+ result = @parser.parse("\"Nakano Kyohei\" (172 62)")
162
+ result.should == [
163
+ '"Nakano',
164
+ 'Kyohei"',
165
+ '172 62'
166
+ ]
167
+ end
168
+
169
+ it 'treat quoting char as normal char in quoted section' do
170
+ @parser.set_quote_guard(Pcut::LineParser::DOUBLE_QUOTE)
171
+ @parser.set_quote_guard(Pcut::LineParser::SINGLE_QUOTE)
172
+ @parser.set_quote_guard("(")
173
+ @parser.set_delimiter(" ")
174
+
175
+ result = @parser.parse("123 \"Nakano 'aka bonar' Kyohei (32)\" engineer")
176
+ result.should == [
177
+ "123",
178
+ "Nakano 'aka bonar' Kyohei (32)",
179
+ "engineer"
180
+ ]
181
+ end
182
+
183
+ end
184
+
185
+ describe 'real world sample' do
186
+
187
+ before(:each) do
188
+ @sample1 = '127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326'
189
+ end
190
+
191
+ it 'case 1' do
192
+ @parser.set_delimiter(" ")
193
+ result = @parser.parse(@sample1)
194
+ result[0].should == '127.0.0.1'
195
+ result[1].should == '-'
196
+ result[2].should == 'frank'
197
+ result[3].should == '[10/Oct/2000:13:55:36'
198
+ result[4].should == '-0700]'
199
+ result[5].should == '"GET'
200
+ result[6].should == '/apache_pb.gif'
201
+ result[7].should == 'HTTP/1.0"'
202
+ result[8].should == '200'
203
+ result[9].should == '2326'
204
+ end
205
+
206
+ it 'case 1 with quoting' do
207
+ @parser.set_delimiter(" ")
208
+ @parser.set_quote_guard("\"")
209
+ @parser.set_quote_guard("[")
210
+ result = @parser.parse(@sample1)
211
+ result[0].should == '127.0.0.1'
212
+ result[1].should == '-'
213
+ result[2].should == 'frank'
214
+ result[3].should == '10/Oct/2000:13:55:36 -0700'
215
+ result[4].should == 'GET /apache_pb.gif HTTP/1.0'
216
+ result[5].should == '200'
217
+ result[6].should == '2326'
218
+ end
219
+
220
+ end
221
+
222
+ end
223
+
@@ -0,0 +1,45 @@
1
+
2
+ require 'pcut/query'
3
+
4
+ describe Pcut::Query do
5
+
6
+ it 'can be initialized with delimiter and range index' do
7
+ query = Pcut::Query.new(" ", Pcut::RangeIndex.parse("-2"))
8
+ query.delimiter.should == " "
9
+ query.index.index.should == 2
10
+ query.index.include_backward?.should be_true
11
+ query.index.include_forward?.should be_false
12
+ end
13
+
14
+ it 'can parse query string' do
15
+ query = Pcut::Query.parse(%q|[/ /, -23]|)
16
+ query.delimiter.should == " "
17
+ query.index.index.should == 23
18
+ query.index.include_backward?.should be_true
19
+ query.index.include_forward?.should be_false
20
+ end
21
+
22
+ it 'allows white speces on parsing' do
23
+ query = Pcut::Query.parse(%q|[ /// , 456- ]|)
24
+ query.delimiter.should == "/"
25
+ query.index.index.should == 456
26
+ query.index.include_backward?.should be_false
27
+ query.index.include_forward?.should be_true
28
+ end
29
+
30
+ it 'raises error on bad delimiter' do
31
+ # empty string
32
+ lambda { Pcut::Query.parse(%q|[//, -23]|) }.
33
+ should raise_error(ArgumentError, %r/invalid format/)
34
+ # 2 letters string
35
+ lambda { Pcut::Query.parse(%q|[/ /, -23]|) }.
36
+ should raise_error(ArgumentError, %r/invalid format/)
37
+ end
38
+
39
+ it 'raises error on bad index expression' do
40
+ lambda { Pcut::Query.parse(%q|[/ /, 0]|) }.
41
+ should raise_error(ArgumentError, %r/zero/)
42
+ end
43
+
44
+ it 'implements to_s'
45
+ end
@@ -0,0 +1,65 @@
1
+
2
+ require 'pcut/range_collector'
3
+
4
+ describe Pcut::RangeCollector do
5
+
6
+ before(:each) do
7
+ @array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
8
+ end
9
+
10
+ it 'collects values with range index' do
11
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("1")).
12
+ should == [1]
13
+
14
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("5")).
15
+ should == [5]
16
+
17
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("10")).
18
+ should == [10]
19
+
20
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("11")).
21
+ should == []
22
+ end
23
+
24
+ it 'collects values with backward range index' do
25
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("-1")).
26
+ should == [1]
27
+
28
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("-4")).
29
+ should == [1, 2, 3, 4]
30
+
31
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("-8")).
32
+ should == [1, 2, 3, 4, 5, 6, 7, 8]
33
+
34
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("-10")).
35
+ should == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
36
+
37
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("-11")).
38
+ should == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
39
+ end
40
+
41
+ it 'collects values with forward range index' do
42
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("1-")).
43
+ should == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
44
+
45
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("5-")).
46
+ should == [5, 6, 7, 8, 9, 10]
47
+
48
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("10-")).
49
+ should == [10]
50
+
51
+ Pcut::RangeCollector.collect(@array, Pcut::RangeIndex.parse("11-")).
52
+ should == []
53
+ end
54
+
55
+ it 'raises error on invalid input' do
56
+ lambda {
57
+ Pcut::RangeCollector.collect(nil, Pcut::RangeIndex.parse("5-"))
58
+ }.should raise_error(ArgumentError, %r/not an array/)
59
+
60
+ lambda { Pcut::RangeCollector.collect(nil, 5) }.
61
+ should raise_error(ArgumentError, %r/not an array/)
62
+ end
63
+
64
+ end
65
+
@@ -0,0 +1,76 @@
1
+
2
+ require 'pcut/range_index'
3
+
4
+ describe Pcut::RangeIndex do
5
+
6
+ it 'can create instance with new' do
7
+ index = Pcut::RangeIndex.new(1, true, false)
8
+ index.index.should == 1
9
+ index.include_backward?.should be_true
10
+ index.include_forward?.should be_false
11
+
12
+ index = Pcut::RangeIndex.new(2, false, true)
13
+ index.index.should == 2
14
+ index.include_backward?.should be_false
15
+ index.include_forward?.should be_true
16
+ end
17
+
18
+ it 'raises error on invalid index, backward or forward' do
19
+ lambda { Pcut::RangeIndex.new(nil, true, true) }.
20
+ should raise_error(ArgumentError, %r/invalid index/)
21
+
22
+ lambda { Pcut::RangeIndex.new(1, "true", true) }.
23
+ should raise_error(ArgumentError, %r/invalid backward/)
24
+
25
+ lambda { Pcut::RangeIndex.new(1, true, "true") }.
26
+ should raise_error(ArgumentError, %r/invalid forward/)
27
+ end
28
+
29
+ it 'can parse single index string' do
30
+ index = Pcut::RangeIndex.parse("123")
31
+ index.index.should == 123
32
+ index.include_backward?.should be_false
33
+ index.include_forward?.should be_false
34
+ end
35
+
36
+ it 'can parse index string including backward' do
37
+ index = Pcut::RangeIndex.parse("-92")
38
+ index.index.should == 92
39
+ index.include_backward?.should be_true
40
+ index.include_forward?.should be_false
41
+ end
42
+
43
+ it 'can parse index string including forward' do
44
+ index = Pcut::RangeIndex.parse("4-")
45
+ index.index.should == 4
46
+ index.include_backward?.should be_false
47
+ index.include_forward?.should be_true
48
+ end
49
+
50
+ it 'raises error on string including both backward and forward' do
51
+ lambda { index = Pcut::RangeIndex.parse("-5-") }.
52
+ should raise_error(ArgumentError, %r/both specified/)
53
+ end
54
+
55
+ it 'raises error on zero index' do
56
+ lambda { index = Pcut::RangeIndex.parse("0") }.
57
+ should raise_error(ArgumentError, %r/zero/)
58
+ end
59
+
60
+ it 'ignores white spaces arround the target' do
61
+ Pcut::RangeIndex.parse(" 321 ").index.should == 321
62
+ Pcut::RangeIndex.parse(" -72 ").index.should == 72
63
+ Pcut::RangeIndex.parse(" 90- ").index.should == 90
64
+ end
65
+
66
+ it 'raises error on invalid string' do
67
+ lambda { Pcut::RangeIndex.parse(nil) }.
68
+ should raise_error(ArgumentError, %r/not string/)
69
+
70
+ lambda { Pcut::RangeIndex.parse("1-1") }.
71
+ should raise_error(ArgumentError, %r/invalid range index expression/)
72
+ end
73
+
74
+ it 'implements to_s'
75
+
76
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'rspec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ gem 'rspec'
6
+ require 'rspec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'pcut'
data/tasks/rspec.rake ADDED
@@ -0,0 +1,20 @@
1
+ begin
2
+ require 'rspec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ require 'rspec'
6
+ end
7
+ begin
8
+ require 'rspec/core/rake_task'
9
+ rescue LoadError
10
+ puts <<-EOS
11
+ To use rspec for testing you must install rspec gem:
12
+ gem install rspec
13
+ EOS
14
+ exit(0)
15
+ end
16
+
17
+ RSpec::Core::RakeTask.new(:spec) do |t|
18
+ t.rspec_opts = ['--options=' "spec/spec.opts"]
19
+ end
20
+
data/test ADDED
@@ -0,0 +1 @@
1
+ 127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)"
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pcut
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Nakano Kyohei (bonar)
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-14 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: term-ansicolor
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 25
29
+ segments:
30
+ - 1
31
+ - 0
32
+ - 7
33
+ version: 1.0.7
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rdoc
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 19
45
+ segments:
46
+ - 3
47
+ - 10
48
+ version: "3.10"
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: newgem
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 5
60
+ segments:
61
+ - 1
62
+ - 5
63
+ - 3
64
+ version: 1.5.3
65
+ type: :development
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ name: hoe
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ hash: 7
76
+ segments:
77
+ - 3
78
+ - 0
79
+ version: "3.0"
80
+ type: :development
81
+ version_requirements: *id004
82
+ description: Yet another cutting tool.
83
+ email:
84
+ - bonar@me.com
85
+ executables:
86
+ - pcut
87
+ extensions: []
88
+
89
+ extra_rdoc_files:
90
+ - History.txt
91
+ - Manifest.txt
92
+ - PostInstall.txt
93
+ - README.rdoc
94
+ files:
95
+ - History.txt
96
+ - Manifest.txt
97
+ - PostInstall.txt
98
+ - README.rdoc
99
+ - Rakefile
100
+ - bin/pcut
101
+ - lib/pcut.rb
102
+ - lib/pcut/cli.rb
103
+ - lib/pcut/line_parser.rb
104
+ - lib/pcut/query.rb
105
+ - lib/pcut/range_collector.rb
106
+ - lib/pcut/range_index.rb
107
+ - script/console
108
+ - script/destroy
109
+ - script/generate
110
+ - spec/lib/pcut/cli_spec.rb
111
+ - spec/lib/pcut/line_parser_spec.rb
112
+ - spec/lib/pcut/query_spec.rb
113
+ - spec/lib/pcut/range_collector_spec.rb
114
+ - spec/lib/pcut/range_index_spec.rb
115
+ - spec/spec.opts
116
+ - spec/spec_helper.rb
117
+ - tasks/rspec.rake
118
+ - test
119
+ - .gemtest
120
+ homepage: http://github.com/bonar/pcut
121
+ licenses: []
122
+
123
+ post_install_message:
124
+ rdoc_options:
125
+ - --main
126
+ - README.rdoc
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ hash: 3
135
+ segments:
136
+ - 0
137
+ version: "0"
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ hash: 3
144
+ segments:
145
+ - 0
146
+ version: "0"
147
+ requirements: []
148
+
149
+ rubyforge_project: pcut
150
+ rubygems_version: 1.8.21
151
+ signing_key:
152
+ specification_version: 3
153
+ summary: Yet another cutting tool.
154
+ test_files: []
155
+