lightning 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/CHANGELOG.rdoc +9 -0
  2. data/README.rdoc +53 -125
  3. data/Rakefile +14 -40
  4. data/bin/lightning +4 -0
  5. data/bin/lightning-complete +1 -10
  6. data/bin/lightning-translate +4 -0
  7. data/lib/lightning.rb +36 -50
  8. data/lib/lightning/bolt.rb +53 -26
  9. data/lib/lightning/builder.rb +87 -0
  10. data/lib/lightning/commands.rb +92 -69
  11. data/lib/lightning/commands/bolt.rb +63 -0
  12. data/lib/lightning/commands/core.rb +57 -0
  13. data/lib/lightning/commands/function.rb +76 -0
  14. data/lib/lightning/commands/shell_command.rb +38 -0
  15. data/lib/lightning/commands_util.rb +75 -0
  16. data/lib/lightning/completion.rb +72 -28
  17. data/lib/lightning/completion_map.rb +42 -39
  18. data/lib/lightning/config.rb +92 -57
  19. data/lib/lightning/function.rb +70 -0
  20. data/lib/lightning/generator.rb +77 -43
  21. data/lib/lightning/generators.rb +53 -0
  22. data/lib/lightning/generators/misc.rb +12 -0
  23. data/lib/lightning/generators/ruby.rb +32 -0
  24. data/lib/lightning/util.rb +70 -0
  25. data/lib/lightning/version.rb +3 -0
  26. data/test/bolt_test.rb +16 -28
  27. data/test/builder_test.rb +54 -0
  28. data/test/commands_test.rb +98 -0
  29. data/test/completion_map_test.rb +31 -54
  30. data/test/completion_test.rb +106 -36
  31. data/test/config_test.rb +22 -56
  32. data/test/function_test.rb +90 -0
  33. data/test/generator_test.rb +73 -0
  34. data/test/lightning.yml +26 -34
  35. data/test/test_helper.rb +80 -15
  36. metadata +42 -20
  37. data/VERSION.yml +0 -4
  38. data/bin/lightning-full_path +0 -18
  39. data/bin/lightning-install +0 -7
  40. data/lib/lightning/bolts.rb +0 -12
  41. data/lightning.yml.example +0 -87
  42. data/lightning_completions.example +0 -147
  43. data/test/lightning_test.rb +0 -58
@@ -0,0 +1,98 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ context "Commands:" do
4
+ # this test seems to run much longer than expected i.e. 0.02
5
+ # rr and raising?
6
+ test "run_command handles unexpected error" do
7
+ mock($stderr).puts(/^Error: Unexpected/)
8
+ mock(Commands).complete(anything) { raise "Unexpected" }
9
+ run_command :complete
10
+ end
11
+
12
+ test "complete defaults to ARGV if no ENV['COMP_LINE']" do
13
+ mock(Completion).complete('o-a Col', anything)
14
+ capture_stdout { run_command(:complete, ['o-a', 'Col']) }
15
+ end
16
+
17
+ test "complete prints usage for no arguments" do
18
+ capture_stdout { run_command(:complete, []) }.should =~ /^Usage/
19
+ end
20
+
21
+ test "complete prints error for invalid command" do
22
+ capture_stdout { run_command(:complete, ['invalid','invalid']) }.should =~ /^#Error.*Please/m
23
+ end
24
+
25
+ test "translate prints usage for no arguments" do
26
+ capture_stdout { run_command(:translate, []) }.should =~ /^Usage/
27
+ end
28
+
29
+ test "translate prints error for invalid command" do
30
+ capture_stdout { run_command(:translate, %w{invalid blah}) }.should =~ /#Error/
31
+ end
32
+
33
+ test "command prints usage with -h" do
34
+ capture_stdout { run_command :install, ['-h'] }.should =~ /^Usage/
35
+ end
36
+
37
+ context "first install" do
38
+ before_all {
39
+ @old_config = Lightning.config
40
+ Lightning.config = Lightning::Config.new({})
41
+ @old_functions = Lightning.functions
42
+ Lightning.functions = nil
43
+
44
+ mock(Lightning.config).save.times(2)
45
+ mock(Commands).first_install? { true }.times(2)
46
+ stub.instance_of(Generator).call_generator { [] }
47
+ mock(File).open(anything, 'w')
48
+ @stdout = capture_stdout { run_command :install }
49
+ }
50
+
51
+ assert "generates default bolts" do
52
+ Generator::DEFAULT_GENERATORS.all? {|e| Lightning.config[:bolts].key?(e) }
53
+ end
54
+
55
+ assert "default bolts are global" do
56
+ Generator::DEFAULT_GENERATORS.all? {|e| Lightning.config[:bolts][e]['global'] }
57
+ end
58
+
59
+ test "builds 8 default functions" do
60
+ expected = %w{cd-gem cd-local_ruby cd-ruby cd-wild echo-gem echo-local_ruby echo-ruby echo-wild}
61
+ Lightning.functions.keys.sort.should == expected
62
+ end
63
+
64
+ test "prints correct install message" do
65
+ @stdout.should =~ /^Created.*lightningrc\nCreated.*functions\.sh for bash/m
66
+ end
67
+
68
+ after_all { Lightning.config = @old_config; Lightning.functions = @old_functions }
69
+ end
70
+
71
+ context "run" do
72
+ test "with no command prints usage" do
73
+ mock(Commands).print_help
74
+ Commands.run []
75
+ end
76
+
77
+ test "with aliased command executes correct command" do
78
+ mock(Commands).run_command('bolt', [])
79
+ Commands.run ['b']
80
+ end
81
+
82
+ test "with -h prints usage" do
83
+ mock(Commands).print_help
84
+ Commands.run ['-h']
85
+ end
86
+
87
+ test "with invalid command prints messaged and usage" do
88
+ mock(Commands).print_help
89
+ capture_stdout { Commands.run ['blah'] }.should =~ /Command 'blah'/
90
+ end
91
+
92
+ test "passes -h as command argument" do
93
+ mock(Commands).print_command_help.never
94
+ mock(Commands).complete(['blah', '-h'])
95
+ Commands.run(['complete', 'blah', '-h'])
96
+ end
97
+ end
98
+ end
@@ -1,58 +1,35 @@
1
1
  require File.join(File.dirname(__FILE__), 'test_helper')
2
2
 
3
- class Lightning::CompletionMapTest < Test::Unit::TestCase
3
+ context "CompletionMap" do
4
+ def create_map(path_hash, new_options={})
5
+ stub(Dir).glob('blah/*', File::FNM_DOTMATCH) { path_hash.values }
6
+ @completion_map = CompletionMap.new('blah/*', new_options)
7
+ end
8
+
9
+ test "creates basic map" do
10
+ expected_map = {"path1"=>"/dir1/path1", "path2"=>"/dir1/path2"}
11
+ create_map(expected_map)
12
+ @completion_map.map.should == expected_map
13
+ end
14
+
15
+ test "ignores paths from Lightning.ignore_paths" do
16
+ CompletionMap.ignore_paths = ['path1', 'dir2', '\.\.?$']
17
+ expected_map = {"path1"=>"/dir1/path1", "path2"=>"/dir1/path2", 'path3'=>'/dir2/path3', '.'=>'/dir1/path4/.'}
18
+ create_map(expected_map)
19
+ @completion_map.map.should == slice_hash(expected_map, 'path2')
20
+ CompletionMap.ignore_paths = nil
21
+ end
22
+
23
+ test "creates map with duplicates" do
24
+ expected_map = {"path1///dir3"=>"/dir3/path1", "path2"=>"/dir1/path2", "path1///dir1"=>"/dir1/path1", "path1///dir2"=>"/dir2/path1"}
25
+ create_map(expected_map)
26
+ @completion_map.map.should == expected_map
27
+ end
4
28
 
5
- context "CompletionMap" do
6
- def create_map(path_hash, new_options={})
7
- Dir.stub!(:glob) { path_hash.values }
8
- @completion_map = Lightning::CompletionMap.new('blah', new_options)
9
- end
10
-
11
- test "creates basic map" do
12
- expected_map = {"path1"=>"/dir1/path1", "path2"=>"/dir1/path2"}
13
- create_map(expected_map)
14
- assert_equal expected_map, @completion_map.map
15
- end
16
-
17
- test "ignores paths from Lightning.ignore_paths" do
18
- Lightning.stub!(:ignore_paths, :return=>['path1'])
19
- expected_map = {"path1"=>"/dir1/path1", "path2"=>"/dir1/path2"}
20
- create_map(expected_map)
21
- assert_equal expected_map.slice('path2'), @completion_map.map
22
- end
23
-
24
- test "creates map with duplicates" do
25
- expected_map = {"path1//dir3"=>"/dir3/path1", "path2"=>"/dir1/path2", "path1//dir1"=>"/dir1/path1", "path1//dir2"=>"/dir2/path1"}
26
- create_map(expected_map)
27
- assert_equal expected_map, @completion_map.map
28
- end
29
-
30
- test "fetches correct path completion" do
31
- map = {"path1"=>"/dir1/path1", "path2"=>"/dir1/path2"}
32
- create_map(map)
33
- assert_equal '/dir1/path1', @completion_map['path1']
34
- end
35
-
36
- test "creates alias map" do
37
- create_map({}, :aliases=>{'path3'=>'/dir1/path3'}, :global_aliases=>{'path2'=>'/dir1/path2'})
38
- assert_equal({"path2"=>"/dir1/path2", "path3"=>"/dir1/path3"}, @completion_map.alias_map)
39
- end
40
-
41
- test "fetches correct alias completion" do
42
- create_map({}, :aliases=>{'path3'=>'/dir1/path3'})
43
- assert_equal '/dir1/path3', @completion_map['path3']
44
- end
45
-
46
- test "fetches correct global alias completion" do
47
- map = {"path1"=>"/dir1/path1", "path2"=>"/dir1/path2"}
48
- create_map(map, :global_aliases=>{'path3'=>'/dir1/path3'})
49
- assert_equal '/dir1/path3', @completion_map['path3']
50
- end
51
-
52
- test "keys include aliases" do
53
- map = {"path2"=>"/dir1/path2"}
54
- create_map(map, :global_aliases=>{'path3'=>'/dir1/path3'})
55
- assert_arrays_equal ['path2','path3'], @completion_map.keys
56
- end
29
+ test "ignores duplicate paths created by overlapping globs" do
30
+ mock(Dir).glob('/usr/**', File::FNM_DOTMATCH) { ['/usr/lib/path1', '/usr/lib/path2'] }
31
+ mock(Dir).glob('/usr/lib/*', File::FNM_DOTMATCH) { ['/usr/lib/path1'] }
32
+ @completion_map = CompletionMap.new('/usr/**', '/usr/lib/*')
33
+ @completion_map.map.should == {'path1'=>'/usr/lib/path1', 'path2'=>'/usr/lib/path2'}
57
34
  end
58
- end
35
+ end
@@ -1,49 +1,119 @@
1
1
  require File.join(File.dirname(__FILE__), 'test_helper')
2
2
 
3
- class Lightning::CompletionTest < Test::Unit::TestCase
4
- context "Completion" do
5
- before(:each) {
6
- @key = 'blah';
7
- Lightning.bolts[@key].stub!(:completions, :return=>%w{at ap blah})
8
- Lightning.config[:complete_regex] = true
9
- }
10
- test "from script matches" do
11
- Lightning.config[:complete_regex] = false
12
- assert_arrays_equal %w{at ap}, Lightning::Completion.complete('cd-test a', @key)
3
+ context "Completion" do
4
+ before {
5
+ @command = 'blah';
6
+ cmd = Function.new 'name'=>@command, 'bolt'=>Bolt.new('blah')
7
+ stub(cmd).completions { ['at', 'ap', 'blah.rb', 'has space'] }
8
+ Lightning.functions[@command] = cmd
9
+ }
10
+
11
+ def tab(input, expected, complete_regex=false)
12
+ Lightning.config[:complete_regex] = complete_regex
13
+ mock(Commands).puts(expected)
14
+ run_command :complete, [@command, 'cd-test '+ input]
15
+ end
16
+
17
+ test "from script matches" do
18
+ tab 'a', %w{at ap}
19
+ end
20
+
21
+ test "ending with space matches everything" do
22
+ tab 'a ', ["at", "ap", "blah.rb", "has\\ space"]
23
+ end
24
+
25
+ test "has no matches" do
26
+ tab 'zaza', []
27
+ end
28
+
29
+ test "has no matches for a local directory" do
30
+ tab 'bling/ok', []
31
+ end
32
+
33
+ test "with multiple words matches last word" do
34
+ tab '-r b', ['blah.rb']
35
+ end
36
+
37
+ test "with multiple words matches quoted last word" do
38
+ tab '-r "b"', ['blah.rb']
39
+ end
40
+
41
+ test "with multiple words matches shell escaped last word" do
42
+ tab 'lib has\\ ', ['has\\ space']
43
+ end
44
+
45
+ test "in nonexistent subdirectory errors properly" do
46
+ tab 'at/', Completion.error_array("Nonexistent directory.")
47
+ end
48
+
49
+ test "in bolt subdirectory matches" do
50
+ mock(Dir).entries('at') { ['..', '.', 'f1']}
51
+ tab 'at/', ['at/f1']
52
+ end
53
+
54
+ test "in nested bolt subdirectory matches" do
55
+ mock(Dir).entries('at/the') { ['f1']}
56
+ tab 'at/the/', ['at/the/f1']
57
+ end
58
+
59
+ test "for directory in bolt subdirectory matches and appends / " do
60
+ stub(File).directory? { true }
61
+ mock(Dir).entries('at/the') { %w{ab lib}}
62
+ tab 'at/the/l', ['at/the/lib/']
63
+ end
64
+
65
+ test "for file in bolt subdirectory matches" do
66
+ mock(Dir).entries('at/the') { %w{ab ge fe fi fo}}
67
+ tab 'at/the/f', ['at/the/fe', 'at/the/fi', 'at/the/fo']
68
+ end
69
+
70
+ test "in bolt file's superdirectory matches" do
71
+ mock(File).expand_path('blah.rb/..') { '/dir' }
72
+ mock(Dir).entries('/dir') { ['f1', 'f2'] }
73
+ tab 'blah.rb/../', ['blah.rb/../f1', 'blah.rb/../f2']
74
+ end
75
+
76
+ test "in bolt file's superdirectory's subdirectory matches" do
77
+ mock(File).expand_path('blah.rb/../sub') { '/dir/sub' }
78
+ mock(Dir).entries('/dir/sub') { ['f1', 'f2'] }
79
+ tab 'blah.rb/../sub/', ['blah.rb/../sub/f1', 'blah.rb/../sub/f2']
80
+ end
81
+
82
+ context "with a regex" do
83
+ test "matches starting letters" do
84
+ tab 'a', %w{at ap}, true
13
85
  end
14
-
15
- test "for basic case matches" do
16
- Lightning.config[:complete_regex] = false
17
- @completion = Lightning::Completion.new('cd-test a', @key)
18
- assert_arrays_equal %w{at ap}, @completion.matches
86
+
87
+ test "and asterisk matches" do
88
+ tab '[ab]*', %w{at ap blah.rb}, true
19
89
  end
20
-
21
- test "with test flag matches" do
22
- Lightning.config[:complete_regex] = false
23
- @completion = Lightning::Completion.new('cd-test -test a', @key)
24
- assert_arrays_equal %w{at ap}, @completion.matches
90
+
91
+ test "with space matches" do
92
+ tab 'has', ['has\\ space']
25
93
  end
26
-
27
- test "with complete_regex on matches" do
28
- Lightning.config[:complete_regex] = true
29
- @completion = Lightning::Completion.new('cd-test *', @key)
30
- assert_arrays_equal %w{at ap blah}, @completion.matches
94
+
95
+ test "with typed space matches" do
96
+ tab 'has\\ ', ['has\\ space']
31
97
  end
32
-
33
- test "with invalid regex is rescued" do
34
- Lightning.config[:complete_regex] = true
35
- @completion = Lightning::Completion.new('cd-test []', @key)
36
- assert !@completion.matches.grep(/Error/).empty?
98
+
99
+ test "which is invalid errors gracefully" do
100
+ tab '[]', Completion.error_array('Invalid regular expression.'), true
37
101
  end
38
102
  end
39
-
103
+ end
104
+
105
+ context "Completion misc" do
40
106
  test "blob_to_regex converts * to .*" do
41
- @lc = Lightning::Completion.new('blah', @key)
42
- assert_equal '.*a.*blah', @lc.blob_to_regex('*a*blah')
107
+ @lc = Completion.new('blah', nil)
108
+ @lc.blob_to_regex('*a*blah').should == '.*a.*blah'
43
109
  end
44
-
110
+
45
111
  test "blob_to_regex doesn't modify .*" do
46
- @lc = Lightning::Completion.new('blah', @key)
47
- assert_equal '.*blah.*', @lc.blob_to_regex('.*blah.*')
112
+ @lc = Completion.new('blah', nil)
113
+ @lc.blob_to_regex('.*blah.*').should == '.*blah.*'
114
+ end
115
+
116
+ test "Completion error array must be more than one element to display and not complete error" do
117
+ Completion.error_array("testing").size.should > 1
48
118
  end
49
119
  end
data/test/config_test.rb CHANGED
@@ -1,62 +1,28 @@
1
1
  require File.join(File.dirname(__FILE__), 'test_helper')
2
2
 
3
- class Lightning::ConfigTest < Test::Unit::TestCase
4
- context "A config" do
5
- before(:all) {
6
- @config = Lightning::Config.create
7
- }
8
-
9
- should "be a hash" do
10
- assert @config.is_a?(Hash)
11
- end
12
-
13
- should "have keys that are symbols" do
14
- assert @config.keys.all? {|e| e.is_a?(Symbol)}
15
- end
16
-
17
- should "have read supported keys" do
18
- supported_keys = [:generated_file, :commands, :ignore_paths, :paths, :shell, :complete_regex]
19
- assert_arrays_equal supported_keys, @config.keys
20
- end
21
-
22
- should "have a generated_file key which is a string" do
23
- assert @config[:generated_file].is_a?(String)
24
- end
25
-
26
- should "have a commands key which is an array" do
27
- assert @config[:commands].is_a?(Array)
28
- end
29
-
30
- should "have a command with valid keys" do
31
- assert @config[:commands][0].slice('name', 'map_to', 'description').values.all? {|e| e.is_a?(String)}
32
- assert @config[:commands][0]['paths'].is_a?(Array)
33
- end
34
-
35
- should "have a paths key which is a hash" do
36
- assert @config[:paths].is_a?(Hash)
37
- end
38
-
39
- should "have an ignore_paths key which is an array" do
40
- assert @config[:ignore_paths].is_a?(Array)
41
- end
3
+ context "A config" do
4
+ before_all {
5
+ @config = Lightning::Config.new
6
+ }
7
+
8
+ assert "has keys that are symbols" do
9
+ @config.keys.all? {|e| e.is_a?(Symbol)}
10
+ end
11
+
12
+ assert "has read supported keys" do
13
+ supported_keys = [:source_file, :ignore_paths, :bolts, :complete_regex]
14
+ supported_keys.all? {|e| @config.key?(e) }
15
+ end
16
+
17
+ assert "has a source_file key which is a string" do
18
+ @config[:source_file].is_a?(String)
19
+ end
20
+
21
+ assert "has a bolts key which is a hash" do
22
+ @config[:bolts].is_a?(Hash)
42
23
  end
43
24
 
44
- context ":configure_commands_and_paths" do
45
- test "generates bolt key if none exists" do
46
- config = {:commands=>[{'name'=>'c1', 'map_to'=>'s1'}]}
47
- assert ! Lightning::Config.configure_commands_and_paths(config)[:commands][0]['bolt_key'].nil?
48
- end
49
-
50
- test "adds generated bolt key to config.paths if it didn't exist" do
51
- config = {:commands=>[{'name'=>'c1', 'map_to'=>'s1', 'paths'=>['*']}]}
52
- new_config = Lightning::Config.configure_commands_and_paths(config)
53
- bolt_key = new_config[:commands][0]['bolt_key']
54
- assert new_config[:paths].has_key?(bolt_key)
55
- end
56
-
57
- test "adds reference to bolt key if command.paths is a string" do
58
- config = {:commands=>[{'name'=>'c1', 'map_to'=>'s1', 'paths'=>'blah'}]}
59
- assert_equal 'blah', Lightning::Config.configure_commands_and_paths(config)[:commands][0]['bolt_key']
60
- end
25
+ assert "has an ignore_paths key which is an array" do
26
+ @config[:ignore_paths].is_a?(Array)
61
27
  end
62
28
  end
@@ -0,0 +1,90 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ context "Function" do
4
+ def create_function(attributes={})
5
+ # bolt, path and aliases depend on test/lightning.yml
6
+ @fn = Function.new({'name'=>'blah', 'bolt'=>Bolt.new('app'), 'desc'=>'blah'}.merge(attributes))
7
+ @fn.completion_map.map = {'path1'=>'/dir/path1','path2'=>'/dir/path2',
8
+ 'path3'=>'/dir/path3', 'file 1'=>'/dir/file 1'}
9
+ end
10
+
11
+ def translate(input, *expected)
12
+ Lightning.functions['blah'] = @fn
13
+ mock(Commands).puts(expected.join("\n"))
14
+ run_command :translate, ['blah'] + input.split(' ')
15
+ end
16
+
17
+ before_all do
18
+ create_function
19
+ @map = @fn.completion_map
20
+ end
21
+
22
+ test "has correct completions" do
23
+ assert_arrays_equal %w{a1 a2}+['file 1']+%w{path1 path2 path3}, @fn.completions
24
+ end
25
+
26
+ test "has bolt's globs" do
27
+ @fn.globs.should.not.be.empty?
28
+ @fn.globs.should == @fn.bolt.globs
29
+ end
30
+
31
+ test "has bolt's aliases" do
32
+ @fn.aliases.should.not.be.empty?
33
+ @fn.aliases.should == @fn.bolt.aliases
34
+ end
35
+
36
+ test "can have a desc" do
37
+ @fn.desc.should.not.be.empty?
38
+ end
39
+
40
+ test "translates a completion" do
41
+ translate 'path1', @map['path1']
42
+ end
43
+
44
+ test "translates multiple completions separately" do
45
+ translate 'path1 path2', @map['path1'], @map['path2']
46
+ end
47
+
48
+ test "translates instant multiple completions (..)" do
49
+ translate 'path.. blah a1', @map['path1'], @map['path2'], @map['path3'], 'blah', @map['a1']
50
+ end
51
+
52
+ test "translates instant multiple completions containing spaces" do
53
+ translate 'file..', @map['file 1']
54
+ end
55
+
56
+ test "translates non-completion to same string" do
57
+ translate 'blah', 'blah'
58
+ end
59
+
60
+ test "translates completion anywhere amongst non-completions" do
61
+ translate '-r path1', "-r", "#{@map['path1']}"
62
+ translate '-r path1 doc/', "-r", "#{@map['path1']}", "doc/"
63
+ end
64
+
65
+ test "translates completion embedded in subdirectory completion" do
66
+ translate '-r path1/sub/dir', "-r", "#{@map['path1']}/sub/dir"
67
+ end
68
+
69
+ test "translates completion with a superdirectory" do
70
+ mock(File).expand_path("#{@map['path1']}/../file1") { '/dir/file1' }
71
+ translate 'path1/../file1', '/dir/file1'
72
+ end
73
+
74
+ test "translates completion over alias" do
75
+ translate 'path3', '/dir/path3'
76
+ end
77
+
78
+ test "translates alias" do
79
+ translate 'a1', @map['a1']
80
+ end
81
+
82
+ after_all { Lightning.config[:aliases] = {}}
83
+
84
+ context "function attributes:" do
85
+ test "post_path added after each translation" do
86
+ create_function 'post_path'=>'/rdoc/index.html'
87
+ translate '-r path1 path2', "-r", "/dir/path1/rdoc/index.html", "/dir/path2/rdoc/index.html"
88
+ end
89
+ end
90
+ end