ned 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 160b7291a491361041677df083e0b86804f07b8cd8915c6bf0d488bf080caaa3
4
- data.tar.gz: 69e4cfa1448cf5e8b0f106d4e4782c498423aca84d3953b43618f6708f3fecf8
3
+ metadata.gz: ac2bb61f75a90fed9ba2ec389e0c3e7fe7e49e9405c4882f5f7f7116daff1c9e
4
+ data.tar.gz: 8d85fe67d13d2b655f31f40bcc6f67b4a61aa4204cdf1b0f0d1b7cda701084c1
5
5
  SHA512:
6
- metadata.gz: ec4eb7adade48c639e707e71ed1ef1cddf47415390af025990ec69530b4733bbd2989280ea6fdae1783efe37391f7a667c4eb7fcbf9775440a905a8a44e88455
7
- data.tar.gz: e898818ec2422271f845da80aca56389a637833e60e0247a96ed53569df00d66b93952332801b3a00778a110510b077d8fb183caf63fa3d2dee32a5e3f0d1761
6
+ metadata.gz: eb1802ef8d556fc19ceae4d93b1bf2ac2906d73386adee200b24043787bb7a29b07a1209fccfcf996d7472af84e83a418a819075c95ecae83ce9d40e6aecbf90
7
+ data.tar.gz: 3cc3810a8506518cb2e40bcc4d1dfda713cc21a0c46a05ba9057722639d5d6c76c559cd551f0e305b05d212eb3a712c1b08373f080b1df37b9c405ec87fe14e4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ned (0.0.2)
4
+ ned (0.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -7,6 +7,7 @@ Usage: ned [[ned_arg ...] --] [[command] [: command ...]]
7
7
 
8
8
  OPTIONS
9
9
  -i, --inplace Edit each file in place by processing each file independently.
10
+ -s, --separate Process input files one at a time rather than as one stream.
10
11
  -h, --help [COMMAND] Print this help.
11
12
  -v, --version Print Ned version.
12
13
 
@@ -24,29 +25,55 @@ backward (b)
24
25
 
25
26
  Usage: ned backward
26
27
 
27
- head (h)
28
+ cut (c)
28
29
 
29
- Print only the first input lines.
30
+ Print selected portions of each line.
31
+
32
+ Usage: ned cut [--delimiter STRING | --pattern PATTERN] [--join STRING] --field (START|START..END) ...
33
+
34
+ Options:
35
+ -d, --delimiter STRING Separate input fields by the specified string. Defaults to space.
36
+ -p, --pattern PATTERN Separate input fields by the specified pattern instead of a delimiter.
37
+ -j, --join STRING Join output fields with the specified string. Defaults to space.
38
+ -f, --field INDEX_OR_RANGE Include the specified index or range in the output.
39
+
40
+ eval (e)
41
+
42
+ Evaluate the specified ruby string.
43
+
44
+ Usage: ned eval STRING
45
+
46
+ Variables:
47
+ l: the current line or lines
48
+ i: the current index (zero-based)
49
+
50
+ grep (g)
51
+
52
+ Print lines matching the specified pattern.
30
53
 
31
- Usage: ned head [--num NUM]
54
+ Usage: ned grep [--only] PATTERN
32
55
 
33
56
  Options:
34
- -n, --num NUM Number of input lines to print. Defaults to 10.
57
+ -o, --only Only print the matching portion of each line.
58
+ -v, --invert Print lines not matching the specified pattern.
59
+
60
+ head (h)
61
+
62
+ Print only the first input lines.
63
+
64
+ Usage: ned head [NUM]
35
65
 
36
66
  index (i)
37
67
 
38
- Print only the specified line(s). Use -1 etc to index from end.
68
+ Print only the lines indicated by the specified indices and ranges.
39
69
 
40
- Usage: ned index START [END]
70
+ Usage: ned index INDEX_OR_RANGE ...
41
71
 
42
72
  join (j)
43
73
 
44
74
  Join input lines, optionally with the specified delimiter.
45
75
 
46
- Usage: ned join [--delimiter STRING]
47
-
48
- Options:
49
- -d, --delimiter STRING Join using the specified delimiter.
76
+ Usage: ned join [STRING]
50
77
 
51
78
  prepend (p)
52
79
 
@@ -56,12 +83,9 @@ prepend (p)
56
83
 
57
84
  quote (q)
58
85
 
59
- Wrap each input line in quotes.
60
-
61
- Usage: ned quote [--quote STRING]
86
+ Wrap each input line in quotes, or the specified string.
62
87
 
63
- Options:
64
- -q, --quote STRING Quote using the specified string.
88
+ Usage: ned quote [STRING]
65
89
 
66
90
  sort (s)
67
91
 
@@ -77,10 +101,7 @@ tail (t)
77
101
 
78
102
  Print only the last input lines.
79
103
 
80
- Usage: ned tail [--num NUM]
81
-
82
- Options:
83
- -n, --num NUM Number of input lines to print. Defaults to 10.
104
+ Usage: ned tail [NUM]
84
105
 
85
106
  uniq (u)
86
107
 
data/lib/ned/command.rb CHANGED
@@ -28,6 +28,14 @@ module Ned
28
28
  config(:description, args)
29
29
  end
30
30
 
31
+ def require_all
32
+ @require_all = true
33
+ end
34
+
35
+ def require_all?
36
+ @require_all
37
+ end
38
+
31
39
  def option_parser(&block)
32
40
  if block_given?
33
41
  @option_parser = block
@@ -45,6 +53,8 @@ module Ned
45
53
 
46
54
  def initialize(input)
47
55
  @input = input
56
+ @require_all = true if self.class.require_all?
57
+ @first = true
48
58
  end
49
59
 
50
60
  def options
@@ -66,64 +76,56 @@ module Ned
66
76
  end
67
77
 
68
78
  def execute
69
- result = @input.execute
70
- return nil if result.nil?
79
+ if @require_all
80
+ load_lines.empty? ? nil : load_lines.shift
81
+ else
82
+ return nil if @next.nil? && !@first
71
83
 
72
- if result.is_a? Array
73
- if require_flat_map?
74
- result.flat_map do |line|
75
- execute_internal(line)
76
- end
84
+ if @first
85
+ @first = false
86
+ current = @input.execute
87
+ @next = @input.execute
77
88
  else
78
- result.map do |line|
79
- execute_internal(line)
89
+ current = @next
90
+ @next = @input.execute
91
+ end
92
+
93
+ if current.is_a? Array
94
+ current.flat_map do |line|
95
+ result = execute_internal(line)
96
+ result.is_a?(Array) ? result : [result]
80
97
  end
98
+ else
99
+ execute_internal(current)
81
100
  end
82
- else
83
- execute_internal(result)
84
101
  end
85
102
  end
86
103
 
87
104
  def execute_all
88
- if require_flat_map?
89
- lines = @input.execute_all.flat_map do |line|
90
- execute_internal(line)
91
- end
92
- lines
105
+ if @require_all
106
+ load_lines
107
+ @lines.shift(@lines.size)
93
108
  else
94
109
  lines = @input.execute_all
95
- lines.each do |line|
96
- execute_internal(line)
110
+ lines = lines.each_with_index.flat_map do |line, i|
111
+ @next = lines[i + 1]
112
+ result = execute_internal(line)
113
+ result.is_a?(Array) ? result : [result]
97
114
  end
98
115
  lines
99
116
  end
100
117
  end
101
118
 
102
- def require_flat_map(require_flat_map = true)
103
- @require_flat_map = require_flat_map
104
- end
105
-
106
- def require_flat_map?
107
- @require_flat_map || false
108
- end
109
-
110
119
  def execute_internal(line)
111
120
  line
112
121
  end
113
- end
114
122
 
115
- class AllCommand < Command
116
- def execute
117
- load_lines.empty? ? nil : load_lines.shift
118
- end
119
-
120
- def execute_all
121
- load_lines
122
- @lines.shift(@lines.size)
123
+ def require_all
124
+ @require_all = true
123
125
  end
124
126
 
125
- def ensure_trailing_newline
126
- @lines[-1].ensure_trailing_newline if @lines[-1]
127
+ def require_all?
128
+ @require_all
127
129
  end
128
130
 
129
131
  def load_lines
@@ -134,8 +136,8 @@ module Ned
134
136
  @lines
135
137
  end
136
138
 
137
- def execute_internal(lines)
138
- lines
139
+ def peek
140
+ require_all? ? @lines[0] : @next
139
141
  end
140
142
  end
141
143
  end
@@ -16,12 +16,12 @@ module Ned
16
16
 
17
17
  raise OptionParser::ParseError.new("missing string argument") if args.size == 0
18
18
  @append = args.shift
19
- require_flat_map if @append.index("\n")
19
+ @require_split = !!@append.index("\n")
20
20
  end
21
21
 
22
22
  def execute_internal(line)
23
23
  line.insert_before_newline(@append)
24
- require_flat_map? ? line.split(/(?<=\n)/) : line
24
+ @require_split ? line.split(/(?<=\n)/) : line
25
25
  end
26
26
 
27
27
  Ned::CommandRegistry.add(Append)
@@ -1,8 +1,10 @@
1
1
  module Ned
2
- class Backward < Ned::AllCommand
2
+ class Backward < Ned::Command
3
3
  long_name 'backward'
4
4
  short_name 'b'
5
5
 
6
+ require_all
7
+
6
8
  option_parser do |opts|
7
9
  opts.banner = <<~EOF
8
10
  Reverse input lines.
@@ -12,7 +14,7 @@ module Ned
12
14
  end
13
15
 
14
16
  def execute_internal(lines)
15
- ensure_trailing_newline
17
+ lines[-1].ensure_trailing_newline if lines[-1]
16
18
  lines.reverse!
17
19
  end
18
20
 
@@ -0,0 +1,81 @@
1
+ module Ned
2
+ class Cut < Ned::Command
3
+ long_name 'cut'
4
+ short_name 'c'
5
+
6
+ option_parser do |opts|
7
+ opts.banner = <<~EOF
8
+ Print selected portions of each line.
9
+
10
+ Usage: ned cut [--delimiter STRING | --pattern PATTERN] [--join STRING] --field (START|START..END) ...
11
+
12
+ Options:
13
+ EOF
14
+
15
+ opts.on('-d', '--delimiter STRING', 'Separate input fields by the specified string. Defaults to space.') do |delimiter|
16
+ raise OptionParser::ParseError.new('duplicate flag') if options[:delimiter]
17
+
18
+ delimiter
19
+ end
20
+
21
+ opts.on('-p', '--pattern PATTERN', 'Separate input fields by the specified pattern instead of a delimiter.') do |pattern|
22
+ raise OptionParser::ParseError.new('duplicate flag') if options[:pattern]
23
+
24
+ Regexp.new(pattern)
25
+ end
26
+
27
+ opts.on('-j', '--join STRING', 'Join output fields with the specified string. Defaults to space.') do |join|
28
+ raise OptionParser::ParseError.new('duplicate flag') if options[:join]
29
+
30
+ join
31
+ end
32
+
33
+ opts.on('-f', '--field INDEX_OR_RANGE', 'Include the specified index or range in the output.') do |index_or_range|
34
+ match = index_or_range.match(/(.*)[.][.](.*)/)
35
+ if match
36
+ start = Integer(match[1]) rescue nil
37
+ ending = Integer(match[2]) rescue nil
38
+ range = start..ending if start && ending
39
+ else
40
+ index = Integer(index_or_range) rescue nil
41
+ range = index..index if index
42
+ end
43
+
44
+ raise OptionParser::InvalidArgument.new(index_or_range) unless range
45
+
46
+ options[:ranges] ||= []
47
+ options[:ranges] << range
48
+ end
49
+ end
50
+
51
+ def parse(args)
52
+ super
53
+
54
+ raise OptionParser::ParseError.new('specify only one of --delimiter and --pattern') if options[:delimiter] && options[:pattern]
55
+ raise OptionParser::ParseError.new('you must specify one or more field indices or ranges') unless options[:ranges]
56
+
57
+ if options[:delimiter]
58
+ options[:join] ||= options[:delimiter]
59
+ else
60
+ options[:join] ||= ' '
61
+ end
62
+
63
+ @require_split = !!options[:join]&.index("\n")
64
+ end
65
+
66
+ def execute_internal(line)
67
+ ending = line[-1] == "\n" ? line.slice!(-1) : ''
68
+
69
+ columns = line.split(options[:pattern] || options[:delimiter])
70
+ result = options[:ranges].flat_map do |range|
71
+ columns[range]
72
+ end.join(options[:join])
73
+
74
+ result << ending
75
+
76
+ @require_split ? result.split(/(?<=\n)/) : result
77
+ end
78
+
79
+ Ned::CommandRegistry.add(Cut)
80
+ end
81
+ end
@@ -0,0 +1,57 @@
1
+ module Ned
2
+ class Eval < Command
3
+ long_name 'eval'
4
+ short_name 'e'
5
+
6
+ option_parser do |opts|
7
+ opts.banner = <<~EOF
8
+ Evaluate the specified ruby string.
9
+
10
+ Usage: ned eval STRING
11
+
12
+ Variables:
13
+ l: the current line or lines
14
+ i: the current index (zero-based)
15
+ EOF
16
+ end
17
+
18
+ def parse(args)
19
+ super
20
+
21
+ raise OptionParser::ParseError.new("missing string argument") if args.size == 0
22
+ @code = args.shift
23
+ end
24
+
25
+ def execute_internal(l)
26
+ @i ||= -1
27
+ @i += 1
28
+ i = @i
29
+
30
+ if l[-1] == "\n"
31
+ ending = "\n"
32
+ l.slice!(-1)
33
+ else
34
+ ending = ''
35
+ end
36
+
37
+ result = instance_eval(@code)
38
+ if result.is_a? Array
39
+ result = result.flat_map { |line| split_on_newline(line.to_s.ensure_trailing_newline) }
40
+ result[-1][-1] = ending if result[-1]
41
+ result
42
+ else
43
+ split_on_newline(result.to_s.insert(-1, ending))
44
+ end
45
+ end
46
+
47
+ def split_on_newline(line_or_lines)
48
+ if line_or_lines.is_a?(Array)
49
+ line_or_lines.flat_map { |line| line.to_s.split(/(?<=\n)/) }
50
+ else
51
+ line_or_lines.to_s.split(/(?<=\n)/)
52
+ end
53
+ end
54
+
55
+ Ned::CommandRegistry.add(Eval)
56
+ end
57
+ end
@@ -0,0 +1,50 @@
1
+ module Ned
2
+ class Grep < Ned::Command
3
+ long_name 'grep'
4
+ short_name 'g'
5
+
6
+ option_parser do |opts|
7
+ opts.banner = <<~EOF
8
+ Print lines matching the specified pattern.
9
+
10
+ Usage: ned grep [--only] PATTERN
11
+
12
+ Options:
13
+ EOF
14
+
15
+ opts.on('-o', '--only', 'Only print the matching portion of each line.') do |only|
16
+ raise OptionParser::ParseError.new('duplicate flag') if options[:only]
17
+
18
+ only
19
+ end
20
+
21
+ opts.on('-v', '--invert', 'Print lines not matching the specified pattern.') do |invert|
22
+ raise OptionParser::ParseError.new('duplicate flag') if options[:invert]
23
+
24
+ invert
25
+ end
26
+ end
27
+
28
+ def parse(args)
29
+ super
30
+
31
+ raise OptionParser::ParseError.new("missing pattern argument") if args.size == 0
32
+
33
+ @pattern = Regexp.new(args.shift)
34
+ end
35
+
36
+ def execute_internal(line)
37
+ line.slice!(-1) if line[-1] == "\n"
38
+
39
+ if options[:invert]
40
+ @pattern.match(line) ? [] : [line << "\n"]
41
+ elsif options[:only]
42
+ line.scan(@pattern).map(&:ensure_trailing_newline)
43
+ else
44
+ @pattern.match(line) ? [line << "\n"] : []
45
+ end
46
+ end
47
+
48
+ Ned::CommandRegistry.add(Grep)
49
+ end
50
+ end
@@ -7,24 +7,25 @@ module Ned
7
7
  opts.banner = <<~EOF
8
8
  Print only the first input lines.
9
9
 
10
- Usage: ned head [--num NUM]
11
-
12
- Options:
10
+ Usage: ned head [NUM]
13
11
  EOF
12
+ end
14
13
 
15
- opts.on('-n', '--num NUM', Integer, 'Number of input lines to print. Defaults to 10.') do |num|
16
- raise OptionParser::ParseError.new('duplicate flag') if options[:num]
17
-
18
- raise OptionParser::ParseError.new("invalid num: #{num}") if num < 0
14
+ def parse(args)
15
+ super
19
16
 
20
- num
17
+ if arg = args.shift
18
+ @num = Integer(arg) rescue nil
19
+ raise OptionParser::InvalidArgument.new(arg) unless @num
20
+ else
21
+ @num = 10
21
22
  end
22
23
  end
23
24
 
24
25
  def execute_internal(line)
25
26
  @index ||= 0
26
27
  @index += 1
27
- if @index <= options.fetch(:num, 10)
28
+ if @index <= @num
28
29
  line
29
30
  else
30
31
  nil
@@ -1,34 +1,52 @@
1
1
  module Ned
2
- class Index < AllCommand
2
+ class Index < Command
3
3
  long_name 'index'
4
4
  short_name 'i'
5
5
 
6
+ require_all
7
+
6
8
  option_parser do |opts|
7
9
  opts.banner = <<~EOF
8
- Print only the specified line(s). Use -1 etc to index from end.
10
+ Print only the lines indicated by the specified indices and ranges.
9
11
 
10
- Usage: ned index START [END]
12
+ Usage: ned index INDEX_OR_RANGE ...
11
13
  EOF
12
14
  end
13
15
 
14
16
  def parse(args)
15
- if index = args.index { |arg| Integer(arg) rescue false }
16
- @start = Integer(args.delete_at(index))
17
+ @ranges = []
18
+
19
+ i = 0
20
+ while arg = args[i]
21
+ range = nil
22
+ match = arg.match(/(.*)[.][.](.*)/)
23
+ if match
24
+ start = Integer(match[1]) rescue nil
25
+ ending = Integer(match[2]) rescue nil
26
+ range = start..ending if start && ending
27
+ else
28
+ index = Integer(arg) rescue nil
29
+ range = index..index if index
30
+ end
17
31
 
18
- if index = args.index { |arg| Integer(arg) rescue false }
19
- @end = Integer(args.delete_at(index))
32
+ if range
33
+ args.slice!(i)
34
+ @ranges << range
35
+ else
36
+ i += 1
20
37
  end
21
38
  end
22
39
 
23
- @end ||= @start
24
-
25
40
  super
26
41
 
27
- raise OptionParser::ParseError.new("missing index argument") unless @start
42
+ raise OptionParser::InvalidArgument.new(args[0]) unless args.empty?
43
+ raise OptionParser::ParseError.new("you must specify one or more indices or ranges") if @ranges.empty?
28
44
  end
29
45
 
30
46
  def execute_internal(lines)
31
- lines[@start..@end] || []
47
+ @ranges.flat_map do |range|
48
+ lines[range]
49
+ end
32
50
  end
33
51
 
34
52
  Ned::CommandRegistry.add(Index)
@@ -1,23 +1,23 @@
1
1
  module Ned
2
- class Join < Ned::AllCommand
2
+ class Join < Ned::Command
3
3
  long_name 'join'
4
4
  short_name 'j'
5
5
 
6
+ require_all
7
+
6
8
  option_parser do |opts|
7
9
  opts.banner = <<~EOF
8
10
  Join input lines, optionally with the specified delimiter.
9
11
 
10
- Usage: ned join [--delimiter STRING]
11
-
12
- Options:
12
+ Usage: ned join [STRING]
13
13
  EOF
14
+ end
14
15
 
15
- opts.on("-d", '--delimiter STRING', 'Join using the specified delimiter.') do |delimiter|
16
- raise OptionParser::ParseError.new('duplicate flag') if options[:delimiter]
16
+ def parse(args)
17
+ super
17
18
 
18
- @has_newline = delimiter.index("\n") != nil
19
- delimiter
20
- end
19
+ @delimiter = args.shift || ''
20
+ @has_newline = @delimiter.index("\n") != nil
21
21
  end
22
22
 
23
23
  def execute_internal(lines)
@@ -30,9 +30,9 @@ module Ned
30
30
  end
31
31
 
32
32
  if @has_newline
33
- lines.join(options.fetch(:delimiter, '')).insert(-1, trailing_newline).split(/(?<=\n)/)
33
+ lines.join(@delimiter).insert(-1, trailing_newline).split(/(?<=\n)/)
34
34
  else
35
- [lines.join(options.fetch(:delimiter, '')).insert(-1, trailing_newline)]
35
+ [lines.join(@delimiter).insert(-1, trailing_newline)]
36
36
  end
37
37
  end
38
38
 
@@ -16,12 +16,12 @@ module Ned
16
16
 
17
17
  raise OptionParser::ParseError.new("missing string argument") if args.size == 0
18
18
  @prepend = args.shift
19
- require_flat_map if @prepend.index("\n")
19
+ @require_split = !!@prepend.index("\n")
20
20
  end
21
21
 
22
22
  def execute_internal(line)
23
23
  line.insert(0, @prepend)
24
- require_flat_map? ? line.split(/(?<=\n)/) : line
24
+ @require_split ? line.split(/(?<=\n)/) : line
25
25
  end
26
26
 
27
27
  Ned::CommandRegistry.add(Prepend)
@@ -5,25 +5,23 @@ module Ned
5
5
 
6
6
  option_parser do |opts|
7
7
  opts.banner = <<~EOF
8
- Wrap each input line in quotes.
8
+ Wrap each input line in quotes, or the specified string.
9
9
 
10
- Usage: ned quote [--quote STRING]
11
-
12
- Options:
10
+ Usage: ned quote [STRING]
13
11
  EOF
12
+ end
14
13
 
15
- opts.on('-q', '--quote STRING', 'Quote using the specified string.') do |quote|
16
- raise OptionParser::ParseError.new('duplicate flag') if options[:quote]
14
+ def parse(args)
15
+ super
17
16
 
18
- require_flat_map if quote.index("\n")
19
- quote
20
- end
17
+ @quote = args.shift || '"'
18
+ @require_split = !!@quote.index("\n")
21
19
  end
22
20
 
23
21
  def execute_internal(line)
24
- line.insert(0, options.fetch(:quote, '"'))
25
- line.insert_before_newline(options.fetch(:quote, '"'))
26
- require_flat_map? ? line.split(/(?<=\n)/) : line
22
+ line.insert(0, @quote)
23
+ line.insert_before_newline(@quote)
24
+ @require_split ? line.split(/(?<=\n)/) : line
27
25
  end
28
26
 
29
27
  Ned::CommandRegistry.add(Quote)
@@ -31,7 +31,7 @@ module Ned
31
31
  lines.concat(current.readlines)
32
32
  current.close
33
33
  current = @inputs.shift
34
- lines[-1].ensure_trailing_newline if !current.nil? && lines[-1]
34
+ lines[-1].ensure_trailing_newline if !!current && lines[-1]
35
35
  end
36
36
 
37
37
  lines
@@ -1,10 +1,12 @@
1
1
  module Ned
2
- class Sort < Ned::AllCommand
2
+ class Sort < Ned::Command
3
3
  DIGIT_MATCH = /^ *((?:0|-?[1-9][0-9]*)(?:[.][0-9]+)?)(.*)/
4
4
 
5
5
  long_name 'sort'
6
6
  short_name 's'
7
7
 
8
+ require_all
9
+
8
10
  option_parser do |opts|
9
11
  opts.banner = <<~EOF
10
12
  Sort input lines.
@@ -28,7 +30,8 @@ module Ned
28
30
  end
29
31
 
30
32
  def execute_internal(lines)
31
- ensure_trailing_newline
33
+ lines[-1].ensure_trailing_newline if lines[-1]
34
+
32
35
  if options.fetch(:numeric, false)
33
36
  sortable = lines.map do |line|
34
37
  match = line.match(DIGIT_MATCH)
@@ -1,29 +1,31 @@
1
1
  module Ned
2
- class Tail < AllCommand
2
+ class Tail < Command
3
3
  long_name 'tail'
4
4
  short_name 't'
5
5
 
6
+ require_all
7
+
6
8
  option_parser do |opts|
7
9
  opts.banner = <<~EOF
8
10
  Print only the last input lines.
9
11
 
10
- Usage: ned tail [--num NUM]
11
-
12
- Options:
12
+ Usage: ned tail [NUM]
13
13
  EOF
14
+ end
14
15
 
15
- opts.on('-n', '--num NUM', Integer, 'Number of input lines to print. Defaults to 10.') do |num|
16
- raise OptionParser::ParseError.new('duplicate flag') if options[:num]
17
-
18
- raise OptionParser::ParseError.new("invalid num: #{num}") if num < 0
16
+ def parse(args)
17
+ super
19
18
 
20
- num
19
+ if arg = args.shift
20
+ @num = Integer(arg) rescue nil
21
+ raise OptionParser::InvalidArgument.new(arg) unless @num
22
+ else
23
+ @num = 10
21
24
  end
22
25
  end
23
26
 
24
27
  def execute_internal(lines)
25
- num = options.fetch(:num, 10)
26
- num < lines.length ? lines[-num..-1] : lines
28
+ @num < lines.length ? lines[-@num..-1] : lines
27
29
  end
28
30
 
29
31
  Ned::CommandRegistry.add(Tail)
@@ -1,5 +1,5 @@
1
1
  module Ned
2
- class Uniq < Ned::AllCommand
2
+ class Uniq < Ned::Command
3
3
  long_name 'uniq'
4
4
  short_name 'u'
5
5
 
@@ -19,34 +19,28 @@ module Ned
19
19
  end
20
20
  end
21
21
 
22
- def execute_internal(lines)
23
- ensure_trailing_newline
24
-
25
- previous = nil
26
- count = 1
27
- lines.each_with_index.flat_map do |line, i|
28
- result = []
29
- if previous && line != previous
30
- if options.fetch(:count, false)
31
- previous.insert(0, "#{count} ")
32
- result << previous
33
- else
34
- result << previous
35
- end
36
- count = 1
37
- elsif previous
38
- count += 1
39
- end
40
- previous = line
41
- if i == lines.length - 1
42
- if options.fetch(:count, false)
43
- line.insert(0, "#{count} ")
44
- result << line
45
- else
46
- result << line
47
- end
48
- end
49
- result
22
+ def parse(args)
23
+ super
24
+ @show_count = options.fetch(:count, false)
25
+ end
26
+
27
+ def execute_internal(line)
28
+ if peek.nil?
29
+ # last line
30
+ line.ensure_trailing_newline
31
+ else
32
+ # maybe last line, can't easily tell
33
+ peek.ensure_trailing_newline
34
+ end
35
+
36
+ @count = (@count || 0) + 1
37
+
38
+ if line != peek
39
+ line.insert(0, "#{@count} ") if @show_count
40
+ @count = 0
41
+ line
42
+ else
43
+ []
50
44
  end
51
45
  end
52
46
 
data/lib/ned/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ned
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/ned.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'optparse'
2
+ require 'tempfile'
2
3
 
3
4
  require 'ned/command'
4
5
  require 'ned/command_registry'
@@ -9,33 +10,13 @@ require 'ned/version'
9
10
  Dir["#{__dir__}/ned/commands/**/*.rb"].sort.each { |f| require f }
10
11
 
11
12
  # todo
12
- # - uniq shouldn't be an all command
13
- # - split into files
14
- # - commands shouldn't have to implement execute_all, put this in the base or use a wrapper or something
15
- # - implement help
16
- # - add --version
17
- # - write tests
18
- # - add tail
19
- # - add head
13
+ # - Add ability to check whether there are more lines.
20
14
  # - add replace
21
- # - add grep
22
- # - add grep -o or only
23
- # - add grep -v or except
24
- # - add index
25
- # - add join
26
15
  # - add count or something like wc
27
16
  # - implement inline editing
28
17
  # - implement highlighting
29
- # - implement append
30
- # - add ability for a command to turn one line into many
31
- # - add ability for a command to turn one line into zero
32
- # - handle newlines in quote, replace, etc.
33
- # - add numeric sort
34
- # - add reverse sort
35
18
  # - maybe add append and prepend
36
19
  # - figure out how to distrute as gem without bundle
37
- # - use OptionParser on main args.
38
- # - uniq should not need to be an AllCommand. Add ability to check whether there are more lines.
39
20
  # - add support for a ~/.ned/ dir
40
21
  # - implement highlight
41
22
  # - add color
@@ -47,6 +28,15 @@ Dir["#{__dir__}/ned/commands/**/*.rb"].sort.each { |f| require f }
47
28
  # - add support for to_set in uniq
48
29
  # - add support for overriding existing commands
49
30
  # - index shouldn't always be an all command. It should be based on the indices.
31
+ # - add a way to operate on input files separately, one at a time.
32
+ # - maybe add --all to eval to operate on all lines at once
33
+ # - write more tests:
34
+ # - '\:'
35
+ # - duplicate names
36
+ # - eval
37
+ # - no short name or no long name
38
+ # - maybe Command should provide an option to ensure the trailing newline is preserved rather than each command doing it alone.
39
+ # - add delete to delete strings
50
40
  module Ned
51
41
  class Main
52
42
  def initialize
@@ -67,6 +57,12 @@ module Ned
67
57
  inplace
68
58
  end
69
59
 
60
+ opts.on('-s', '--separate', 'Process input files one at a time rather than as one stream.') do |separate|
61
+ raise OptionParser::ParseError.new('duplicate flag') if @options[:separate]
62
+
63
+ separate
64
+ end
65
+
70
66
  opts.on('-h', '--help [COMMAND]', 'Print this help.') do |help|
71
67
  help || true
72
68
  end
@@ -139,14 +135,12 @@ module Ned
139
135
  return 0
140
136
  end
141
137
 
142
- raise 'in-place not implemented' if @options[:inplace]
143
-
144
138
  inputs = ned_args.map do |current|
145
139
  if current == '-'
146
140
  input
147
141
  else
148
142
  unless File.exist? current
149
- error.puts "fatal: file not found \"#{current}\""
143
+ error.puts "fatal: file not found: \"#{current}\""
150
144
  return 1
151
145
  end
152
146
  File.new(current)
@@ -192,19 +186,77 @@ module Ned
192
186
  return 1
193
187
  end
194
188
 
195
- if inputs.index(input) && @in_place
189
+ if @options[:inplace] && inputs.index(input)
196
190
  error.puts "fatal: option -i specified with stdin"
197
191
  return 1
198
192
  end
199
193
 
194
+ if @options[:inplace]
195
+ inputs.each do |i|
196
+ dup = command_args.map { |a| a.dup }
197
+ result, command = create_command(dup, [i], output, error)
198
+
199
+ return result if result
200
+
201
+ if inputs.index(input) && !has_stdin
202
+ error.puts 'fatal: can\'t read from stdin'
203
+ return 1
204
+ end
205
+
206
+ temp = Tempfile.new(File.expand_path(i))
207
+ print = Ned::Print.new(command, temp)
208
+
209
+ while line = print.execute; end
210
+
211
+ temp.rewind
212
+ IO.copy_stream(temp, File.expand_path(i))
213
+ temp.unlink
214
+ end
215
+ elsif @options[:separate]
216
+ inputs.each do |i|
217
+ dup = command_args.map { |a| a.dup }
218
+ result, command = create_command(dup, [i], output, error)
219
+
220
+ return result if result
221
+
222
+ if inputs.index(input) && !has_stdin
223
+ error.puts 'fatal: can\'t read from stdin'
224
+ return 1
225
+ end
226
+
227
+ print = Ned::Print.new(command, output)
228
+
229
+ while line = print.execute; end
230
+ end
231
+ else
232
+ result, command = create_command(command_args, inputs, output, error)
233
+
234
+ return result if result
235
+
236
+ if inputs.index(input) && !has_stdin
237
+ error.puts 'fatal: can\'t read from stdin'
238
+ return 1
239
+ end
240
+
241
+ print = Ned::Print.new(command, output)
242
+
243
+ while line = print.execute; end
244
+ end
245
+
246
+ 0
247
+ end
248
+
249
+ def create_command(command_args, inputs, output, error)
200
250
  previous = Ned::Read.new(inputs)
201
251
  command_args.each do |args|
202
252
  command = Ned::CommandRegistry.find(args.shift).new(previous)
203
253
  begin
204
254
  command.parse(args)
205
- raise OptionParser::ParseError.new("invalid arguments: #{args}") unless args.empty?
255
+ raise OptionParser::InvalidArgument.new(args[0]) unless args.empty?
206
256
  rescue OptionParser::ParseError => e
207
257
  error.puts "fatal: error while parsing options for #{command.class.long_name}: #{e.message}"
258
+ error.puts
259
+ error.puts command.class.help
208
260
  return 1
209
261
  end
210
262
 
@@ -221,16 +273,7 @@ module Ned
221
273
  previous = command
222
274
  end
223
275
 
224
- if inputs.index(input) && !has_stdin
225
- error.puts 'fatal: can\'t read from stdin'
226
- return 1
227
- end
228
-
229
- print = Ned::Print.new(previous, output)
230
-
231
- while line = print.execute; end
232
-
233
- 0
276
+ [nil, previous]
234
277
  end
235
278
  end
236
279
  end
data/scripts/release CHANGED
@@ -12,6 +12,8 @@ if [ -n "$(git status --porcelain)" ]; then
12
12
  exit 1;
13
13
  fi
14
14
 
15
+ git pull
16
+
15
17
  NEW_VERSION=$1
16
18
  CURRENT_VERSION=$(grep VERSION lib/ned/version.rb | cut -d'"' -f 2)
17
19
 
@@ -28,4 +30,5 @@ gem build
28
30
  gem push ned-$NEW_VERSION.gem
29
31
  bundle install
30
32
  git commit -a -m "v$NEW_VERSION Release"
33
+ git push
31
34
  open "https://github.com/nicholasdower/ned/releases/new?title=v$NEW_VERSION%20Release&tag=v$NEW_VERSION&target=$(git rev-parse HEAD)"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ned
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Dower
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-07 00:00:00.000000000 Z
11
+ date: 2022-06-09 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A stream editor
14
14
  email:
@@ -28,6 +28,9 @@ files:
28
28
  - lib/ned/command_registry.rb
29
29
  - lib/ned/commands/append.rb
30
30
  - lib/ned/commands/backward.rb
31
+ - lib/ned/commands/cut.rb
32
+ - lib/ned/commands/eval.rb
33
+ - lib/ned/commands/grep.rb
31
34
  - lib/ned/commands/head.rb
32
35
  - lib/ned/commands/index.rb
33
36
  - lib/ned/commands/join.rb
@@ -50,7 +53,6 @@ files:
50
53
  - scripts/generate_readme
51
54
  - scripts/release
52
55
  - scripts/test
53
- - spec_old/file_spec.rb
54
56
  homepage: https://github.com/nicholasdower/ned
55
57
  licenses:
56
58
  - MIT
@@ -1,29 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'files' do
4
- test_failure 'no input', args: '-s', stdin: nil, stderr: 'fatal: no input'
5
-
6
- test_failure 'no such file', args: 'foo -s', stdin: nil, stderr: 'fatal: no such file: foo'
7
-
8
- test_failure 'multiple files', args: 'spec/support/file spec/support/file -s', stdin: nil, stderr: 'fatal: more than one input not yet supported'
9
-
10
- test_failure 'no input, standard in as file arg', args: '- -s', stdin: nil, stderr: 'fatal: no input'
11
-
12
- test_failure 'standard in as file arg multiple times', args: '- - -s', stdin: '', stderr: "fatal: can't read from standard in twice, silly"
13
-
14
- context 'all lines read' do
15
- test_success 'standard in', stdin: "two\none\n", args: '-s', stdout: "one\ntwo\n"
16
-
17
- test_success 'standard in as file arg', stdin: "two\none\n", args: '- -s', stdout: "one\ntwo\n"
18
-
19
- test_success 'single file', stdin: nil, args: 'spec/support/file -s', stdout: "one\nthree\ntwo\n"
20
- end
21
-
22
- context 'lines read one at a time' do
23
- test_success 'standard in', stdin: "two\none\n", args: '-a 1', stdout: "two1\none1\n"
24
-
25
- test_success 'standard in as file arg', stdin: "two\none\n", args: '- -a 1', stdout: "two1\none1\n"
26
-
27
- test_success 'single file', stdin: nil, args: 'spec/support/file -a 1', stdout: "one1\ntwo1\nthree1\n"
28
- end
29
- end