ned 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23d27cc1a81863250fe7b264e9199fb794c8d3e62fe879dab529f3201019e2f2
4
- data.tar.gz: 6a1124febf6502f5a54693617da98eb869a0c181ae90aa678ec670937b26879d
3
+ metadata.gz: 160b7291a491361041677df083e0b86804f07b8cd8915c6bf0d488bf080caaa3
4
+ data.tar.gz: 69e4cfa1448cf5e8b0f106d4e4782c498423aca84d3953b43618f6708f3fecf8
5
5
  SHA512:
6
- metadata.gz: 99fd8ede92eea52aed8a2308f6e05487219769c06a92104e7fe43fdbb96ea33aa833b598bbe5305a6741758657a7c0b16cfe01c7d96a38727c4166a83ec3fc78
7
- data.tar.gz: 5f800186643b9638f32cdee823b64694fd0b4a60041af701e178d9c68967c63849726b0c29758899a483906c8a148103dda11495489d5219b9ae2949dff8e59d
6
+ metadata.gz: ec4eb7adade48c639e707e71ed1ef1cddf47415390af025990ec69530b4733bbd2989280ea6fdae1783efe37391f7a667c4eb7fcbf9775440a905a8a44e88455
7
+ data.tar.gz: e898818ec2422271f845da80aca56389a637833e60e0247a96ed53569df00d66b93952332801b3a00778a110510b077d8fb183caf63fa3d2dee32a5e3f0d1761
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ned (0.0.1)
4
+ ned (0.0.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,24 +1,99 @@
1
1
  # Ned
2
2
 
3
3
  ```
4
- A stream editor.
5
-
6
- Usage: ned [file] [options ...]
7
-
8
- Options:
9
- --help Print this message.
10
- --version Print version.
11
- -a, --append string Append the specified string to each line.
12
- -b, --backward Invert all input lines.
13
- -g, --grep pattern Search using the specified pattern.
14
- -h, --head [num] Print the first n lines. Defaults to 10.
15
- -i, --index start[..end] Print only the specified lines. Use -1 etc to index from end.
16
- -j, --join [string] Join input lines, optionally with the specified string.
17
- -o, --only pattern Search using the specified pattern. Only print matching part of line.
18
- -p, --prepend string Prepend the specified string to each line.
19
- -q, --quote [string] Quote each line, optionally with the specified string.
20
- -r, --replace pattern replacement Regex replace.
21
- -s, --sort ["num"|"alpha"] Sort. Defaults to alphabetical.
22
- -t, --tail [num] Print the last n lines. Defaults to 10.
23
- -u, --uniq Unique.
4
+ Ned, a stream editor.
5
+
6
+ Usage: ned [[ned_arg ...] --] [[command] [: command ...]]
7
+
8
+ OPTIONS
9
+ -i, --inplace Edit each file in place by processing each file independently.
10
+ -h, --help [COMMAND] Print this help.
11
+ -v, --version Print Ned version.
12
+
13
+ COMMANDS
14
+
15
+ append (a)
16
+
17
+ Append the specified string to each input line.
18
+
19
+ Usage: ned append STRING
20
+
21
+ backward (b)
22
+
23
+ Reverse input lines.
24
+
25
+ Usage: ned backward
26
+
27
+ head (h)
28
+
29
+ Print only the first input lines.
30
+
31
+ Usage: ned head [--num NUM]
32
+
33
+ Options:
34
+ -n, --num NUM Number of input lines to print. Defaults to 10.
35
+
36
+ index (i)
37
+
38
+ Print only the specified line(s). Use -1 etc to index from end.
39
+
40
+ Usage: ned index START [END]
41
+
42
+ join (j)
43
+
44
+ Join input lines, optionally with the specified delimiter.
45
+
46
+ Usage: ned join [--delimiter STRING]
47
+
48
+ Options:
49
+ -d, --delimiter STRING Join using the specified delimiter.
50
+
51
+ prepend (p)
52
+
53
+ Prepend the specified string to each input line.
54
+
55
+ Usage: ned prepend STRING
56
+
57
+ quote (q)
58
+
59
+ Wrap each input line in quotes.
60
+
61
+ Usage: ned quote [--quote STRING]
62
+
63
+ Options:
64
+ -q, --quote STRING Quote using the specified string.
65
+
66
+ sort (s)
67
+
68
+ Sort input lines.
69
+
70
+ Usage: ned sort [--numeric]
71
+
72
+ Options:
73
+ -n, --numeric Sort numerically.
74
+ -r, --reverse Reverse the sort order.
75
+
76
+ tail (t)
77
+
78
+ Print only the last input lines.
79
+
80
+ Usage: ned tail [--num NUM]
81
+
82
+ Options:
83
+ -n, --num NUM Number of input lines to print. Defaults to 10.
84
+
85
+ uniq (u)
86
+
87
+ Filter out repeated input lines.
88
+
89
+ Usage: ned uniq [--count]
90
+
91
+ Options:
92
+ -c, --count Precede each output line with the number of times the line occurred in the input, followed by a space.
93
+ ```
94
+
95
+ ## Installation
96
+
97
+ ```
98
+ gem install ned
24
99
  ```
data/bin/ned CHANGED
@@ -4,4 +4,4 @@ $LOAD_PATH.unshift "#{__dir__}/../lib"
4
4
 
5
5
  require 'ned'
6
6
 
7
- exit Ned.run
7
+ exit Ned::Main.new.run(ARGV, STDIN, STDOUT, STDERR)
data/lib/ned/command.rb CHANGED
@@ -1,18 +1,141 @@
1
1
  module Ned
2
2
  class Command
3
- attr_reader :config, :params
3
+ class ParseError < StandardError; end
4
4
 
5
- def initialize(config, params)
6
- @config = config
7
- @params = params
5
+ class << self
6
+ def config(name, args)
7
+ @config ||= { }
8
+
9
+ if args.empty?
10
+ @config[name]
11
+ elsif args.size == 1
12
+ @config[name] = args[0]
13
+ nil
14
+ else
15
+ raise "invalid args: #{args}"
16
+ end
17
+ end
18
+
19
+ def long_name(*args)
20
+ config(:long_name, args)
21
+ end
22
+
23
+ def short_name(*args)
24
+ config(:short_name, args)
25
+ end
26
+
27
+ def description(*args)
28
+ config(:description, args)
29
+ end
30
+
31
+ def option_parser(&block)
32
+ if block_given?
33
+ @option_parser = block
34
+ else
35
+ @option_parser
36
+ end
37
+ end
38
+
39
+ def help
40
+ OptionParser.new do |opts|
41
+ option_parser.call(opts)
42
+ end.help
43
+ end
44
+ end
45
+
46
+ def initialize(input)
47
+ @input = input
48
+ end
49
+
50
+ def options
51
+ @options ||= {}
52
+ end
53
+
54
+ def parse(args)
55
+ OptionParser.new do |opts|
56
+ opts.on('-h', '--help', 'Print this help.') do |help|
57
+ help
58
+ end
59
+
60
+ opts.on('-v', '--version', 'Print Ned version.') do |version|
61
+ version
62
+ end
63
+
64
+ instance_exec(opts, &self.class.option_parser)
65
+ end.parse!(args, into: options)
66
+ end
67
+
68
+ def execute
69
+ result = @input.execute
70
+ return nil if result.nil?
71
+
72
+ if result.is_a? Array
73
+ if require_flat_map?
74
+ result.flat_map do |line|
75
+ execute_internal(line)
76
+ end
77
+ else
78
+ result.map do |line|
79
+ execute_internal(line)
80
+ end
81
+ end
82
+ else
83
+ execute_internal(result)
84
+ end
85
+ end
86
+
87
+ 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
93
+ else
94
+ lines = @input.execute_all
95
+ lines.each do |line|
96
+ execute_internal(line)
97
+ end
98
+ lines
99
+ end
100
+ end
101
+
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
+ def execute_internal(line)
111
+ line
112
+ end
113
+ end
114
+
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
+ end
124
+
125
+ def ensure_trailing_newline
126
+ @lines[-1].ensure_trailing_newline if @lines[-1]
8
127
  end
9
128
 
10
- def all?
11
- config.all?(params)
129
+ def load_lines
130
+ unless @lines
131
+ @lines = @input.execute_all
132
+ @lines = execute_internal(@lines)
133
+ end
134
+ @lines
12
135
  end
13
136
 
14
- def execute(context)
15
- config.execute(context, *params)
137
+ def execute_internal(lines)
138
+ lines
16
139
  end
17
140
  end
18
141
  end
@@ -0,0 +1,27 @@
1
+ module Ned
2
+ class CommandRegistry
3
+ @all = []
4
+ @names = { }
5
+
6
+ def self.add(command)
7
+ if @names.key?(command.long_name)
8
+ raise "duplicate command name \"#{command.long_name}\""
9
+ end
10
+
11
+ if @names.key?(command.short_name)
12
+ raise "duplicate command name \"#{command.short_name}\""
13
+ end
14
+ @names[command.long_name] = command
15
+ @names[command.short_name] = command
16
+ @all << command
17
+ end
18
+
19
+ def self.find(name)
20
+ @names[name]
21
+ end
22
+
23
+ def self.all
24
+ @all
25
+ end
26
+ end
27
+ end
@@ -1,15 +1,29 @@
1
- # frozen_string_literal: true
2
-
3
- Ned::Config.add(
4
- name: 'append',
5
- description: 'Append the specified string to each line.',
6
- short: '-a',
7
- long: '--append',
8
- params: 'string',
9
- validator: Ned::Validators.num(1),
10
- execute: lambda do |context, string|
11
- context.lines = context.lines.flat_map do |line|
12
- "#{line}#{string}".split("\n")
1
+ module Ned
2
+ class Append < Command
3
+ long_name 'append'
4
+ short_name 'a'
5
+
6
+ option_parser do |opts|
7
+ opts.banner = <<~EOF
8
+ Append the specified string to each input line.
9
+
10
+ Usage: ned append STRING
11
+ EOF
12
+ end
13
+
14
+ def parse(args)
15
+ super
16
+
17
+ raise OptionParser::ParseError.new("missing string argument") if args.size == 0
18
+ @append = args.shift
19
+ require_flat_map if @append.index("\n")
20
+ end
21
+
22
+ def execute_internal(line)
23
+ line.insert_before_newline(@append)
24
+ require_flat_map? ? line.split(/(?<=\n)/) : line
13
25
  end
26
+
27
+ Ned::CommandRegistry.add(Append)
14
28
  end
15
- )
29
+ end
@@ -1,14 +1,21 @@
1
- # frozen_string_literal: true
2
-
3
- Ned::Config.add(
4
- name: 'backward',
5
- description: 'Invert all input lines.',
6
- short: '-b',
7
- long: '--backward',
8
- params: nil,
9
- validator: Ned::Validators.none(),
10
- execute: lambda do |context|
11
- context.lines.reverse!
12
- end,
13
- all: true
14
- )
1
+ module Ned
2
+ class Backward < Ned::AllCommand
3
+ long_name 'backward'
4
+ short_name 'b'
5
+
6
+ option_parser do |opts|
7
+ opts.banner = <<~EOF
8
+ Reverse input lines.
9
+
10
+ Usage: ned backward
11
+ EOF
12
+ end
13
+
14
+ def execute_internal(lines)
15
+ ensure_trailing_newline
16
+ lines.reverse!
17
+ end
18
+
19
+ Ned::CommandRegistry.add(Backward)
20
+ end
21
+ end
@@ -1,21 +1,36 @@
1
- # frozen_string_literal: true
2
-
3
- Ned::Config.add(
4
- name: 'head',
5
- description: 'Print the first n lines. Defaults to 10.',
6
- short: '-h',
7
- long: '--head',
8
- params: '[num]',
9
- validator: Ned::Validators.num(0, 1) do |num|
10
- if num
11
- Integer(num) rescue nil
12
- else
13
- []
1
+ module Ned
2
+ class Head < Command
3
+ long_name 'head'
4
+ short_name 'h'
5
+
6
+ option_parser do |opts|
7
+ opts.banner = <<~EOF
8
+ Print only the first input lines.
9
+
10
+ Usage: ned head [--num NUM]
11
+
12
+ Options:
13
+ EOF
14
+
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
19
+
20
+ num
21
+ end
14
22
  end
15
- end,
16
- execute: lambda do |context, num=10|
17
- context.lines = context.lines.flat_map do |line|
18
- context.start_index < num ? [line] : []
23
+
24
+ def execute_internal(line)
25
+ @index ||= 0
26
+ @index += 1
27
+ if @index <= options.fetch(:num, 10)
28
+ line
29
+ else
30
+ nil
31
+ end
19
32
  end
33
+
34
+ Ned::CommandRegistry.add(Head)
20
35
  end
21
- )
36
+ end
@@ -1,27 +1,36 @@
1
- # frozen_string_literal: true
2
-
3
- Ned::Config.add(
4
- name: 'index',
5
- description: 'Print only the specified lines. Use -1 etc to index from end.',
6
- short: '-i',
7
- long: '--index',
8
- params: 'start[..end]',
9
- validator: Ned::Validators.num(1) do |param|
10
- if param.index('..')
11
- parts = param.split('..')
12
- next unless parts.size == 2
13
-
14
- [Integer(parts[0]), Integer(parts[1])] rescue nil
15
- else
16
- Integer(param) rescue nil
1
+ module Ned
2
+ class Index < AllCommand
3
+ long_name 'index'
4
+ short_name 'i'
5
+
6
+ option_parser do |opts|
7
+ opts.banner = <<~EOF
8
+ Print only the specified line(s). Use -1 etc to index from end.
9
+
10
+ Usage: ned index START [END]
11
+ EOF
17
12
  end
18
- end,
19
- execute: lambda do |context, start, ending = nil|
20
- context.lines = (ending.nil? ? context.lines[start..start] : context.lines[start..ending]) || []
21
- end,
22
- all: lambda do |start, ending = nil|
23
- true
24
- # todo
25
- #start < 0 || (!ending.nil? && ending < 0)
13
+
14
+ def parse(args)
15
+ if index = args.index { |arg| Integer(arg) rescue false }
16
+ @start = Integer(args.delete_at(index))
17
+
18
+ if index = args.index { |arg| Integer(arg) rescue false }
19
+ @end = Integer(args.delete_at(index))
20
+ end
21
+ end
22
+
23
+ @end ||= @start
24
+
25
+ super
26
+
27
+ raise OptionParser::ParseError.new("missing index argument") unless @start
28
+ end
29
+
30
+ def execute_internal(lines)
31
+ lines[@start..@end] || []
32
+ end
33
+
34
+ Ned::CommandRegistry.add(Index)
26
35
  end
27
- )
36
+ end
@@ -1,14 +1,41 @@
1
- # frozen_string_literal: true
2
-
3
- Ned::Config.add(
4
- name: 'join',
5
- description: 'Join input lines, optionally with the specified string.',
6
- short: '-j',
7
- long: '--join',
8
- params: '[string]',
9
- validator: Ned::Validators.num(0, 1),
10
- execute: lambda do |context, string=''|
11
- context.lines = context.lines.join(string).split("\n")
12
- end,
13
- all: true
14
- )
1
+ module Ned
2
+ class Join < Ned::AllCommand
3
+ long_name 'join'
4
+ short_name 'j'
5
+
6
+ option_parser do |opts|
7
+ opts.banner = <<~EOF
8
+ Join input lines, optionally with the specified delimiter.
9
+
10
+ Usage: ned join [--delimiter STRING]
11
+
12
+ Options:
13
+ EOF
14
+
15
+ opts.on("-d", '--delimiter STRING', 'Join using the specified delimiter.') do |delimiter|
16
+ raise OptionParser::ParseError.new('duplicate flag') if options[:delimiter]
17
+
18
+ @has_newline = delimiter.index("\n") != nil
19
+ delimiter
20
+ end
21
+ end
22
+
23
+ def execute_internal(lines)
24
+ if lines[-1] && lines[-1][-1] == "\n"
25
+ trailing_newline = "\n"
26
+ lines.each { |line| line.slice!(-1) }
27
+ else
28
+ trailing_newline = ''
29
+ lines[0..-2].each { |line| line.slice!(-1) }
30
+ end
31
+
32
+ if @has_newline
33
+ lines.join(options.fetch(:delimiter, '')).insert(-1, trailing_newline).split(/(?<=\n)/)
34
+ else
35
+ [lines.join(options.fetch(:delimiter, '')).insert(-1, trailing_newline)]
36
+ end
37
+ end
38
+
39
+ Ned::CommandRegistry.add(Join)
40
+ end
41
+ end
@@ -1,15 +1,29 @@
1
- # frozen_string_literal: true
2
-
3
- Ned::Config.add(
4
- name: 'prepend',
5
- description: 'Prepend the specified string to each line.',
6
- short: '-p',
7
- long: '--prepend',
8
- params: 'string',
9
- validator: Ned::Validators.num(1),
10
- execute: lambda do |context, string|
11
- context.lines = context.lines.flat_map do |line|
12
- "#{string}#{line}".split("\n")
1
+ module Ned
2
+ class Prepend < Command
3
+ long_name 'prepend'
4
+ short_name 'p'
5
+
6
+ option_parser do |opts|
7
+ opts.banner = <<~EOF
8
+ Prepend the specified string to each input line.
9
+
10
+ Usage: ned prepend STRING
11
+ EOF
12
+ end
13
+
14
+ def parse(args)
15
+ super
16
+
17
+ raise OptionParser::ParseError.new("missing string argument") if args.size == 0
18
+ @prepend = args.shift
19
+ require_flat_map if @prepend.index("\n")
20
+ end
21
+
22
+ def execute_internal(line)
23
+ line.insert(0, @prepend)
24
+ require_flat_map? ? line.split(/(?<=\n)/) : line
13
25
  end
26
+
27
+ Ned::CommandRegistry.add(Prepend)
14
28
  end
15
- )
29
+ end
@@ -0,0 +1,13 @@
1
+ module Ned
2
+ class Print < Ned::Command
3
+ def initialize(input, output)
4
+ super(input)
5
+ @output = output
6
+ end
7
+
8
+ def execute_internal(line)
9
+ @output.print line
10
+ line
11
+ end
12
+ end
13
+ end
@@ -1,15 +1,31 @@
1
- # frozen_string_literal: true
2
-
3
- Ned::Config.add(
4
- name: 'quote',
5
- description: 'Quote each line, optionally with the specified string.',
6
- short: '-q',
7
- long: '--quote',
8
- params: '[string]',
9
- validator: Ned::Validators.num(0, 1),
10
- execute: lambda do |context, char='"'|
11
- context.lines = context.lines.flat_map do |line|
12
- "#{char}#{line}#{char}".split("\n")
1
+ module Ned
2
+ class Quote < Ned::Command
3
+ long_name 'quote'
4
+ short_name 'q'
5
+
6
+ option_parser do |opts|
7
+ opts.banner = <<~EOF
8
+ Wrap each input line in quotes.
9
+
10
+ Usage: ned quote [--quote STRING]
11
+
12
+ Options:
13
+ EOF
14
+
15
+ opts.on('-q', '--quote STRING', 'Quote using the specified string.') do |quote|
16
+ raise OptionParser::ParseError.new('duplicate flag') if options[:quote]
17
+
18
+ require_flat_map if quote.index("\n")
19
+ quote
20
+ end
21
+ end
22
+
23
+ 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
13
27
  end
28
+
29
+ Ned::CommandRegistry.add(Quote)
14
30
  end
15
- )
31
+ end