lightning 0.2.1 → 0.3.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.
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