ru 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8a3f2a29229d4d84159e1fc9a1ec135a2cb00a93
4
- data.tar.gz: a6559f3dc45dc3cae09776036adffb04ea48677a
3
+ metadata.gz: 92351cd0d4f9df90afa8ea60e92f6abc6470c90e
4
+ data.tar.gz: 38256d881838535af9e122f2cdb270760c4ffca3
5
5
  SHA512:
6
- metadata.gz: ca1b9747691d20c5cfe6af9167f8923f72dffe3e6cd79da88fb92c3a13f5eef8cd477e3eb9be34e700e39791a4ef9bdbd2f17da66d64030b1e6a7a51cd0b0ae7
7
- data.tar.gz: 17dcc47340dfb7d84c5447f47816903d60901bf874e00d30a76658434e58c0d16267e0acd0a9bd74521aa3139509ca2fd77c03a59fabf0cc38aa9b2e99dede29
6
+ metadata.gz: 812326331d26647a18e8e9ce823f1b30bdd6a60eb91fa2940b4c07eaea916626e1562a65adfdde2fb380bd8549eabbfec7e444071861f30e91d5ac2a16184fe6
7
+ data.tar.gz: 950c4e17a04764fbc845b04390cd7cfbe1a52855ef1569f0bdf90430ffa6e91910cb60df8c236e464672f4ef1fcf9334f2ad3145dd4bafa6e275af9d771e48f8
data/README.md CHANGED
@@ -98,6 +98,34 @@ The code argument is run as if it has `$stdin.each_line.map(&:chomp).` prepended
98
98
 
99
99
  In addition to the methods provided by Ruby Core and Active Support, Ru provides other methods for performing transformations, like `each_line`, `files`, and `grep`. See [Methods](#methods) for more.
100
100
 
101
+ ### Saving commands
102
+
103
+ You can save commands for future use using `save`:
104
+
105
+ ```bash
106
+ $ ru save sum 'map(:to_i).sum'
107
+ Saved command: sum is 'map(:to_i).sum'
108
+ ```
109
+
110
+ And run them later using `run`:
111
+
112
+ ```bash
113
+ $ echo "2\n3" | ru run sum
114
+ 5
115
+ $ ru run sum myfile
116
+ 5
117
+ ```
118
+
119
+ ### Options
120
+
121
+ #### -h, --help
122
+
123
+ Print a help page.
124
+
125
+ #### -v, --version
126
+
127
+ Print the installed version of Ru.
128
+
101
129
  Examples
102
130
  --------
103
131
 
data/bin/ru CHANGED
@@ -3,21 +3,7 @@
3
3
  require_relative '../lib/ru'
4
4
 
5
5
  begin
6
- args = ARGV
7
- command = args.shift
8
- stdin = nil
9
- # If the command starts with a '!', run it without stdin
10
- if command.present? && command.start_with?('! ')
11
- command = command[2..-1]
12
- # If args is empty and an option (e.g. -h, --help, -v, --version) isn't present, read from stdin
13
- elsif args.empty? && !command.try(:start_with?, '-')
14
- stdin = STDIN.read
15
- end
16
- process = Ru::Process.new({
17
- command: command,
18
- args: args,
19
- stdin: stdin
20
- })
6
+ process = Ru::Process.new
21
7
  puts process.run
22
8
  rescue => e
23
9
  STDERR.puts e.message
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../
3
3
  specs:
4
- ru (0.0.3)
4
+ ru (0.1.0)
5
5
  activesupport
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../
3
3
  specs:
4
- ru (0.0.3)
4
+ ru (0.1.0)
5
5
  activesupport
6
6
 
7
7
  GEM
@@ -0,0 +1,56 @@
1
+ require 'pathname'
2
+
3
+ module Ru
4
+ class CommandManager
5
+ class InvalidCodeError < ArgumentError; end
6
+ class InvalidNameError < ArgumentError; end
7
+
8
+ def save(name, code)
9
+ raise InvalidCodeError.new("Invalid code. Code cannot be blank.") if code.blank?
10
+ validate_name(name)
11
+ commands = get_commands
12
+ commands[name] = code
13
+ save_commands(commands)
14
+ end
15
+
16
+ def get(name)
17
+ validate_name(name)
18
+ commands = get_commands
19
+ commands[name]
20
+ end
21
+
22
+ private
23
+
24
+ def save_commands(commands)
25
+ lines = []
26
+ commands.each do |name, code|
27
+ lines << "#{name},#{code}"
28
+ end
29
+ content = lines.join("\n")
30
+ ::File.open(path, 'w') { |file| file.write(content) }
31
+ end
32
+
33
+ def get_commands
34
+ if ::File.exists?(path)
35
+ commands = {}
36
+ ::File.readlines(path).each do |line|
37
+ pieces = line.chomp.split(',', 2)
38
+ commands[pieces[0]] = pieces[1]
39
+ end
40
+ commands
41
+ else
42
+ {}
43
+ end
44
+ end
45
+
46
+ def validate_name(name)
47
+ if name !~ /^[\w_]+$/
48
+ raise InvalidNameError.new("Invalid command name '#{name}'. Command names may only contain alphanumerics and underscores.")
49
+ end
50
+ end
51
+
52
+ def path
53
+ Pathname.new(::File.expand_path('~')).join('.ru_commands')
54
+ end
55
+ end
56
+ end
@@ -1,8 +1,7 @@
1
1
  require 'erb'
2
2
 
3
- # TODO: If more options are added, we should use OptionParser
4
3
  module Ru
5
- class Options
4
+ class OptionPrinter
6
5
  def exists?(option_key)
7
6
  options[option_key].present?
8
7
  end
@@ -15,10 +14,8 @@ module Ru
15
14
 
16
15
  def options
17
16
  {
18
- '-h' => :get_help,
19
- '--help' => :get_help,
20
- '-v' => :get_version,
21
- '--version' => :get_version
17
+ help: :get_help,
18
+ version: :get_version
22
19
  }
23
20
  end
24
21
 
@@ -1,30 +1,64 @@
1
+ require 'optparse'
2
+
1
3
  module Ru
2
4
  class Process
3
5
  def initialize(options={})
4
- @command = options[:command]
5
- @args = options[:args]
6
- @stdin = options[:stdin]
7
- if @command.kind_of?(String) && @command.start_with?('[')
8
- @command = 'to_stdout' + @command
9
- end
10
- @options = Options.new
6
+ @command_manager = CommandManager.new
7
+ @option_printer = OptionPrinter.new
11
8
  end
12
9
 
13
10
  def run
14
- if @command.nil?
15
- STDERR.puts @options.run('--help')
11
+ output = process_options
12
+ return output if output
13
+
14
+ args = ARGV
15
+ first_arg = args.shift
16
+
17
+ if first_arg == 'save'
18
+ name = args[0]
19
+ code = args[1]
20
+ @command_manager.save(name, code)
21
+ return "Saved command: #{name} is '#{code}'"
22
+ elsif first_arg == 'run'
23
+ name = args.shift
24
+ @code = @command_manager.get(name)
25
+ if @code.blank?
26
+ STDERR.puts "Unable to find command '#{name}'"
27
+ exit 1
28
+ return
29
+ end
30
+ elsif first_arg.blank?
31
+ STDERR.puts @option_printer.run(:help)
16
32
  exit 1
33
+ return
34
+ else
35
+ @code = first_arg
17
36
  end
18
- if @options.exists?(@command)
19
- return @options.run(@command)
20
- end
21
- paths = @args
22
- if @stdin.blank? && paths.present?
23
- @stdin = paths.map { |path| ::File.open(path).read }.join("\n")
24
- end
37
+
38
+ @stdin = get_stdin(args) unless @code.start_with?('! ')
39
+ @code = prepare_code(@code) if @code
40
+
25
41
  lines = @stdin.present? ? @stdin.split("\n") : []
26
42
  array = Ru::Array.new(lines)
27
- output = array.instance_eval(@command) || @stdin
43
+ output = array.instance_eval(@code) || @stdin
44
+ output = prepare_output(output)
45
+ output
46
+ end
47
+
48
+ private
49
+
50
+ def prepare_code(code)
51
+ if code.kind_of?(String)
52
+ if code.start_with?('[')
53
+ code = 'to_stdout' + code
54
+ elsif code.start_with?('! ')
55
+ code = code[2..-1]
56
+ end
57
+ end
58
+ code
59
+ end
60
+
61
+ def prepare_output(output)
28
62
  if output.respond_to?(:to_dotsch_output)
29
63
  output = output.to_dotsch_output
30
64
  end
@@ -33,5 +67,38 @@ module Ru
33
67
  end
34
68
  output.to_s
35
69
  end
70
+
71
+ def process_options
72
+ @options = get_options
73
+ @options.each do |option, value|
74
+ if @option_printer.exists?(option)
75
+ return @option_printer.run(option)
76
+ end
77
+ end
78
+ nil
79
+ end
80
+
81
+ def get_options
82
+ options = {}
83
+ OptionParser.new do |opts|
84
+ opts.on("-h", "--help", "Print help") do |help|
85
+ options[:help] = true
86
+ end
87
+
88
+ opts.on("-v", "--version", "Print version") do |version|
89
+ options[:version] = true
90
+ end
91
+ end.parse!
92
+ options
93
+ end
94
+
95
+ def get_stdin(args)
96
+ paths = args
97
+ if paths.present?
98
+ paths.map { |path| ::File.open(path).read }.join("\n")
99
+ else
100
+ STDIN.read
101
+ end
102
+ end
36
103
  end
37
104
  end
@@ -1,3 +1,3 @@
1
1
  module Ru
2
- VERSION = '0.0.3'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -8,7 +8,7 @@ describe 'misc examples' do
8
8
  context "summing integers" do
9
9
  it "sums" do
10
10
  lines = (1..10).to_a.map(&:to_s)
11
- out = run(lines, 'map(:to_i).sum')
11
+ out = run('map(:to_i).sum', lines)
12
12
  out.should == '55'
13
13
  end
14
14
  end
@@ -17,7 +17,7 @@ describe 'misc examples' do
17
17
  context "printing the nth line" do
18
18
  it "prints" do
19
19
  lines = (1..10).to_a.map(&:to_s)
20
- out = run(lines, '[4]')
20
+ out = run('[4]', lines)
21
21
  out.should == '5'
22
22
  end
23
23
  end
@@ -26,7 +26,7 @@ describe 'misc examples' do
26
26
  context "sorting an Apache access log by response time" do
27
27
  it "sorts" do
28
28
  file = fixture_path('files', 'access.log')
29
- out = run('', 'map { |line| [line[/(\d+)( ".+"){2}$/, 1].to_i, line] }.sort.reverse.map(:join, " ")', file)
29
+ out = run(['map { |line| [line[/(\d+)( ".+"){2}$/, 1].to_i, line] }.sort.reverse.map(:join, " ")', file])
30
30
  out.should == <<-EOF.strip
31
31
  584912 66.249.64.14 - - [18/Sep/2004:11:07:48 +1000] "GET /file.txt HTTP/1.0" 200 584912 "-" "Googlebot/2.1"
32
32
  6433 66.249.64.14 - - [18/Sep/2004:11:07:48 +1000] "GET / HTTP/1.0" 200 6433 "-" "Googlebot/2.1"
@@ -8,7 +8,7 @@ describe 'sed examples' do
8
8
  context "centering lines" do
9
9
  it "centers" do
10
10
  lines = %w{john paul george} + [' ringo ']
11
- out = run(lines, 'each_line.strip.center(10)')
11
+ out = run('each_line.strip.center(10)', lines)
12
12
  out.should == " john \n paul \n george \n ringo "
13
13
  end
14
14
  end
@@ -16,7 +16,7 @@ describe 'sed examples' do
16
16
  context "increment a number" do
17
17
  it "increments" do
18
18
  lines = ('5'..'10').to_a
19
- out = run(lines, '(each_line.to_i+1)')
19
+ out = run('(each_line.to_i+1)', lines)
20
20
  out.should == ('6'..'11').to_a.join("\n")
21
21
  end
22
22
  end
@@ -24,7 +24,7 @@ describe 'sed examples' do
24
24
  context "reverse characters of lines" do
25
25
  it "reverses" do
26
26
  lines = %w{john paul george ringo}
27
- out = run(lines, 'each_line.reverse')
27
+ out = run('each_line.reverse', lines)
28
28
  out.should == "nhoj\nluap\negroeg\nognir"
29
29
  end
30
30
  end
@@ -32,7 +32,7 @@ describe 'sed examples' do
32
32
  context "numbering lines" do
33
33
  it "numbers" do
34
34
  lines = %w{john paul george ringo}
35
- out = run(lines, 'map.with_index { |line, index| "#{(index+1).to_s.rjust(6)} #{line}" }')
35
+ out = run('map.with_index { |line, index| "#{(index+1).to_s.rjust(6)} #{line}" }', lines)
36
36
  out.should == " 1 john\n 2 paul\n 3 george\n 4 ringo"
37
37
  end
38
38
  end
@@ -40,7 +40,7 @@ describe 'sed examples' do
40
40
  context "counting lines" do
41
41
  it "counts" do
42
42
  lines = %w{john paul george ringo}
43
- out = run(lines, 'length')
43
+ out = run('length', lines)
44
44
  out.should == "4"
45
45
  end
46
46
  end
@@ -48,7 +48,7 @@ describe 'sed examples' do
48
48
  context "printing the first lines" do
49
49
  it "prints" do
50
50
  lines = %w{john paul george ringo}
51
- out = run(lines, '[0,2]')
51
+ out = run('[0,2]', lines)
52
52
  out.should == "john\npaul"
53
53
  end
54
54
  end
@@ -56,7 +56,7 @@ describe 'sed examples' do
56
56
  context "printing the last lines" do
57
57
  it "prints" do
58
58
  lines = %w{john paul george ringo}
59
- out = run(lines, '[2..-1]')
59
+ out = run('[2..-1]', lines)
60
60
  out.should == "george\nringo"
61
61
  end
62
62
  end
@@ -64,7 +64,7 @@ describe 'sed examples' do
64
64
  context "make duplicate lines unique" do
65
65
  it "dedupes" do
66
66
  lines = %w{john john paul george george george ringo}
67
- out = run(lines, 'uniq')
67
+ out = run('uniq', lines)
68
68
  out.should == "john\npaul\ngeorge\nringo"
69
69
  end
70
70
  end
@@ -72,7 +72,7 @@ describe 'sed examples' do
72
72
  context "print duplicated lines of input" do
73
73
  it "prints" do
74
74
  lines = %w{john john paul george george george ringo}
75
- out = run(lines, 'select { |line| self.count(line) > 1 }')
75
+ out = run('select { |line| self.count(line) > 1 }', lines)
76
76
  out.should == "john\njohn\ngeorge\ngeorge\ngeorge"
77
77
  end
78
78
  end
@@ -80,7 +80,7 @@ describe 'sed examples' do
80
80
  context "remove all duplicated lines" do
81
81
  it "removes" do
82
82
  lines = %w{john john paul george george george ringo}
83
- out = run(lines, 'select { |line| self.count(line) == 1 }')
83
+ out = run('select { |line| self.count(line) == 1 }', lines)
84
84
  out.should == "paul\nringo"
85
85
  end
86
86
  end
@@ -88,7 +88,7 @@ describe 'sed examples' do
88
88
  context "squeezing blank lines" do
89
89
  it "squeezes" do
90
90
  lines = "john\n\npaul\ngeorge\n\n\nringo"
91
- out = run(lines, 'to_s.squeeze("\n")')
91
+ out = run('to_s.squeeze("\n")', lines)
92
92
  out.should == "john\npaul\ngeorge\nringo"
93
93
  end
94
94
  end
@@ -7,7 +7,7 @@ describe Ru::Process do
7
7
  describe "#run" do
8
8
  it "runs []" do
9
9
  lines = %w{john paul george ringo}
10
- out = run(lines, '[1,2]')
10
+ out = run('[1,2]', lines)
11
11
  out.should == "paul\ngeorge"
12
12
  end
13
13
 
@@ -16,7 +16,7 @@ describe Ru::Process do
16
16
  fixture_path('files', 'bar.txt'),
17
17
  fixture_path('files', 'foo.txt')
18
18
  ]
19
- out = run(paths, 'files')
19
+ out = run('files', paths)
20
20
  out.should == "bar.txt\nfoo.txt"
21
21
  end
22
22
 
@@ -25,7 +25,7 @@ describe Ru::Process do
25
25
  fixture_path('files', 'bar.txt'),
26
26
  fixture_path('files', 'foo.txt')
27
27
  ]
28
- out = run(paths, "files.format('l')")
28
+ out = run("files.format('l')", paths)
29
29
  lines = out.split("\n")
30
30
  lines.length.should == 2
31
31
  lines.each do |line|
@@ -36,59 +36,123 @@ describe Ru::Process do
36
36
 
37
37
  it "runs grep" do
38
38
  lines = %w{john paul george ringo}
39
- out = run(lines, "grep(/o[h|r]/)")
39
+ out = run("grep(/o[h|r]/)", lines)
40
40
  out.should == "john\ngeorge"
41
41
  end
42
42
 
43
43
  it "runs map with two arguments" do
44
44
  lines = %w{john paul george ringo}
45
- out = run(lines, 'map(:[], 0)')
45
+ out = run('map(:[], 0)', lines)
46
46
  out.should == %w{j p g r}.join("\n")
47
47
  end
48
48
 
49
49
  it "runs sort" do
50
50
  lines = %w{john paul george ringo}
51
- out = run(lines, 'sort')
51
+ out = run('sort', lines)
52
52
  out.should == lines.sort.join("\n")
53
53
  end
54
54
 
55
55
  it "takes files as arguments" do
56
- out = run('', 'to_s', fixture_path('files', 'foo.txt'))
56
+ out = run(['to_s', fixture_path('files', 'foo.txt')])
57
57
  out.should == "foo\nfoo\nfoo"
58
58
  end
59
59
 
60
+ it "runs code prepended by '! '" do
61
+ out = run('! 2 + 3')
62
+ out.should == '5'
63
+ end
64
+
65
+ context "no arguments" do
66
+ it "prints help" do
67
+ STDERR.should_receive(:puts) do |out|
68
+ out.should include('Ruby in your shell!')
69
+ end
70
+ Ru::Process.any_instance.should_receive(:exit).with(1)
71
+ run('')
72
+ end
73
+ end
74
+
60
75
  context "an undefined method" do
61
76
  it "raises a NoMethodError" do
62
77
  lines = %w{john paul george ringo}
63
- expect { out = run(lines, 'foo') }.to raise_error(NoMethodError)
78
+ expect { out = run('foo', lines) }.to raise_error(NoMethodError)
79
+ end
80
+ end
81
+
82
+ describe "command management" do
83
+ describe "run" do
84
+ it "runs the command" do
85
+ Ru::CommandManager.any_instance.stub(:get_commands).and_return({ 'sum' => 'map(:to_i).sum' })
86
+ out = run(['run', 'sum'], "2\n3")
87
+ out.should == '5'
88
+ end
89
+
90
+ context "no command name" do
91
+ it "raises an InvalidNameError" do
92
+ expect { run(['run']) }.to raise_error(Ru::CommandManager::InvalidNameError)
93
+ end
94
+ end
95
+ end
96
+
97
+ describe "save" do
98
+ it "saves the command" do
99
+ Ru::CommandManager.any_instance.should_receive(:save_commands)
100
+ run(['save', 'foo', 'map(:to_i).sum'])
101
+ end
102
+
103
+ context "no code" do
104
+ it "raises an InvalidCodeError" do
105
+ expect { run(['save', 'foo']) }.to raise_error(Ru::CommandManager::InvalidCodeError)
106
+ end
107
+ end
108
+
109
+ context "invalid command name" do
110
+ it "raises an InvalidNameError" do
111
+ expect { run(['save', 'foo-bar', 'map(:to_i).sum']) }.to raise_error(Ru::CommandManager::InvalidNameError)
112
+ end
113
+ end
64
114
  end
65
115
  end
66
116
 
67
117
  describe "options" do
68
118
  context "-h" do
69
119
  it "shows help" do
70
- out = run('', '--help')
120
+ out = run('--help')
71
121
  out.should include('Ruby in your shell!')
72
122
  end
73
123
  end
74
124
 
75
125
  context "--help" do
76
126
  it "shows help" do
77
- out = run('', '-h')
127
+ out = run('-h')
128
+ out.should include('Ruby in your shell!')
129
+ end
130
+ end
131
+
132
+ context "help with a second argument" do
133
+ it "shows help" do
134
+ out = run(['--help', 'foo'])
78
135
  out.should include('Ruby in your shell!')
79
136
  end
80
137
  end
81
138
 
82
139
  context "-v" do
83
140
  it "shows the version" do
84
- out = run('', '--version')
141
+ out = run('-v')
85
142
  out.should == Ru::VERSION
86
143
  end
87
144
  end
88
145
 
89
146
  context "--version" do
90
147
  it "shows the version" do
91
- out = run('', '--version')
148
+ out = run('--version')
149
+ out.should == Ru::VERSION
150
+ end
151
+ end
152
+
153
+ context "version with a second argument" do
154
+ it "shows the version" do
155
+ out = run(['--version', 'foo'])
92
156
  out.should == Ru::VERSION
93
157
  end
94
158
  end
@@ -1,6 +1,6 @@
1
1
  module FixturesHelper
2
2
  def fixture_path(*args)
3
3
  directory = Pathname.new(File.dirname(File.absolute_path(__FILE__)))
4
- directory.join('..', 'fixtures', *args)
4
+ directory.join('..', 'fixtures', *args).to_s
5
5
  end
6
6
  end
@@ -1,14 +1,18 @@
1
1
  module ProcessHelper
2
- def run(stdin, *args)
2
+ def run(args, stdin=nil)
3
+ if args.kind_of?(String)
4
+ args = [args]
5
+ end
6
+ stub_const('ARGV', args)
7
+
3
8
  if stdin.kind_of?(Array)
4
9
  stdin = stdin.join("\n")
5
10
  end
6
- command = args.shift
7
- process = Ru::Process.new({
8
- command: command,
9
- args: args,
10
- stdin: stdin
11
- })
11
+ stdin_double = double
12
+ stdin_double.stub(:read).and_return(stdin)
13
+ stub_const('STDIN', stdin_double)
14
+
15
+ process = Ru::Process.new
12
16
  process.run
13
17
  end
14
18
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ru
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Benner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-14 00:00:00.000000000 Z
11
+ date: 2014-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -76,9 +76,10 @@ files:
76
76
  - gemfiles/activesupport_4.gemfile.lock
77
77
  - lib/ru.rb
78
78
  - lib/ru/array.rb
79
+ - lib/ru/command_manager.rb
79
80
  - lib/ru/file.rb
80
81
  - lib/ru/iterator.rb
81
- - lib/ru/options.rb
82
+ - lib/ru/option_printer.rb
82
83
  - lib/ru/process.rb
83
84
  - lib/ru/version.rb
84
85
  - ru.gemspec