pcut 0.0.1

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