ru 0.0.3 → 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
  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