tabtab 0.9.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 (77) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +76 -0
  3. data/PostInstall.txt +10 -0
  4. data/README.rdoc +385 -0
  5. data/Rakefile +25 -0
  6. data/bin/install_tabtab +10 -0
  7. data/bin/tabtab +10 -0
  8. data/examples/tabtab.sh +7 -0
  9. data/features/aliases_for_completions.feature +23 -0
  10. data/features/development.feature +19 -0
  11. data/features/different_shells_installation.feature +26 -0
  12. data/features/discovered_gem_app_completions.feature +37 -0
  13. data/features/external_app_completions.feature +24 -0
  14. data/features/file_completions.feature +17 -0
  15. data/features/hide_short_flags.feature +19 -0
  16. data/features/steps/cli.rb +63 -0
  17. data/features/steps/common.rb +211 -0
  18. data/features/steps/completions.rb +15 -0
  19. data/features/steps/configuration.rb +20 -0
  20. data/features/steps/env.rb +17 -0
  21. data/features/steps/gems.rb +18 -0
  22. data/features/steps/shells.rb +3 -0
  23. data/lib/dev_definitions/gem.rb +54 -0
  24. data/lib/dev_definitions/rake.rb +23 -0
  25. data/lib/dev_definitions/script-generate.rb +8 -0
  26. data/lib/dev_definitions/script-server.rb +14 -0
  27. data/lib/install_tabtab/cli.rb +139 -0
  28. data/lib/tabtab.rb +10 -0
  29. data/lib/tabtab/cli.rb +116 -0
  30. data/lib/tabtab/completions.rb +6 -0
  31. data/lib/tabtab/completions/external.rb +39 -0
  32. data/lib/tabtab/completions/file.rb +23 -0
  33. data/lib/tabtab/completions/gems.rb +44 -0
  34. data/lib/tabtab/definitions.rb +28 -0
  35. data/lib/tabtab/definitions/base.rb +146 -0
  36. data/lib/tabtab/definitions/command.rb +47 -0
  37. data/lib/tabtab/definitions/default.rb +41 -0
  38. data/lib/tabtab/definitions/flag.rb +38 -0
  39. data/lib/tabtab/definitions/root.rb +70 -0
  40. data/lib/tabtab/framework_testing.rb +11 -0
  41. data/lib/tabtab/local_config.rb +16 -0
  42. data/lib/tabtab/test/assertions.rb +6 -0
  43. data/lib/tabtab_definitions/cucumber.rb +19 -0
  44. data/lib/tabtab_definitions/github.rb +50 -0
  45. data/lib/tabtab_definitions/newgem.rb +27 -0
  46. data/lib/tabtab_definitions/rails.rb +15 -0
  47. data/lib/tabtab_definitions/rubyforge.rb +17 -0
  48. data/script/console +10 -0
  49. data/script/destroy +14 -0
  50. data/script/generate +14 -0
  51. data/spec/definition_spec.rb +334 -0
  52. data/spec/external_spec.rb +38 -0
  53. data/spec/fixtures/bin/test_app +11 -0
  54. data/spec/fixtures/gems/multi_app/History.txt +2 -0
  55. data/spec/fixtures/gems/multi_app/Manifest.txt +7 -0
  56. data/spec/fixtures/gems/multi_app/Rakefile +25 -0
  57. data/spec/fixtures/gems/multi_app/bin/test_app +11 -0
  58. data/spec/fixtures/gems/multi_app/lib/multi_app.rb +6 -0
  59. data/spec/fixtures/gems/multi_app/lib/tabtab_definitions/some_app.rb +5 -0
  60. data/spec/fixtures/gems/multi_app/multi_app-0.0.1.gem +0 -0
  61. data/spec/fixtures/gems/multi_app/multi_app.gemspec +38 -0
  62. data/spec/fixtures/gems/my_app/History.txt +2 -0
  63. data/spec/fixtures/gems/my_app/Manifest.txt +7 -0
  64. data/spec/fixtures/gems/my_app/Rakefile +25 -0
  65. data/spec/fixtures/gems/my_app/bin/test_app +11 -0
  66. data/spec/fixtures/gems/my_app/lib/my_app.rb +6 -0
  67. data/spec/fixtures/gems/my_app/lib/tabtab_definitions.rb +5 -0
  68. data/spec/fixtures/gems/my_app/my_app-0.0.1.gem +0 -0
  69. data/spec/fixtures/gems/my_app/my_app.gemspec +38 -0
  70. data/spec/framework_testing_spec.rb +55 -0
  71. data/spec/install_tabtab_cli_spec.rb +139 -0
  72. data/spec/spec.opts +1 -0
  73. data/spec/spec_helper.rb +14 -0
  74. data/spec/tabtab_cli_spec.rb +145 -0
  75. data/tasks/rspec.rake +21 -0
  76. data/website/images/tabtab.png +0 -0
  77. metadata +167 -0
@@ -0,0 +1,15 @@
1
+ Given /^env variable \$PATH includes fixture executables folder$/ do
2
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + "/../../spec/fixtures/bin")}:#{ENV['PATH']}"
3
+ end
4
+
5
+ Then /^I should see a full list of options for 'test_app'$/ do
6
+ actual_output = File.read(File.join(@tmp_root, "executable.out"))
7
+ expected_output = %w[--extra --help -h -x].join("\n")
8
+ expected_output.should == actual_output.strip
9
+ end
10
+
11
+ Then /^I should not see any short form flags$/ do
12
+ actual_output = File.read(File.join(@tmp_root, "executable.out"))
13
+ expected_output = %w[--extra --help].join("\n")
14
+ expected_output.should == actual_output.strip
15
+ end
@@ -0,0 +1,20 @@
1
+ Given /^disable short flags$/ do
2
+ in_home_folder do
3
+ config = YAML.load(File.read('.tabtab.yml'))
4
+ config['shortflags'] = 'disable'
5
+ File.open('.tabtab.yml', 'w') do |f|
6
+ f << config.to_yaml
7
+ end
8
+ end
9
+ end
10
+
11
+ Given /^alias '(.*)' to existing '(.*)'$/ do |alias_name, app_name|
12
+ in_home_folder do
13
+ config = YAML.load(File.read('.tabtab.yml'))
14
+ config['aliases'] ||= {}
15
+ config['aliases'][alias_name] = app_name
16
+ File.open('.tabtab.yml', 'w') do |f|
17
+ f << config.to_yaml
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ gem 'cucumber'
2
+ require 'cucumber'
3
+ gem 'rspec'
4
+ require 'spec'
5
+ gem 'mocha'
6
+ require 'mocha'
7
+
8
+ Spec::Runner.configure do |config|
9
+ config.mock_with :mocha
10
+ end
11
+
12
+ require 'yaml'
13
+
14
+ require File.dirname(__FILE__) + "/../../lib/tabtab"
15
+ require File.dirname(__FILE__) + "/../../lib/install_tabtab/cli"
16
+
17
+
@@ -0,0 +1,18 @@
1
+ Given %r{^a user's RubyGems gem cache} do
2
+ Given "a safe folder"
3
+ Given "env variable $GEM_HOME set to '#{gem_install_dir}'"
4
+ end
5
+
6
+ def gem_install_dir
7
+ install_dir = File.expand_path(File.join(@home_path), ".gem/ruby/1.8")
8
+ end
9
+
10
+ Given /^a RubyGem '(.*)' with autocompletions$/ do |gem_name|
11
+ @stdout = File.expand_path(File.join(@tmp_root, "geminstall.txt"))
12
+ @stderr = File.expand_path(File.join(@tmp_root, "geminstall.err"))
13
+ FileUtils.chdir(File.join(File.dirname(__FILE__) + "/../../spec/fixtures/gems/#{gem_name}")) do
14
+ system "rake gemspec > #{@stdout} 2> #{@stderr}"
15
+ system "gem build #{gem_name}.gemspec > #{@stdout} 2> #{@stderr}"
16
+ system "gem install --install-dir #{gem_install_dir} #{gem_name}*.gem > #{@stdout} 2> #{@stderr}"
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ Given /^a bash shell$/ do
2
+ Given "env variable $SHELL set to '/bin/bash'"
3
+ end
@@ -0,0 +1,54 @@
1
+ TabTab::Definition.register('gem') do |c|
2
+ c.flags :version, :v
3
+ c.flags :h
4
+
5
+ c.command :build do
6
+ Dir['**/*.gemspec']
7
+ end
8
+ c.command :check do |check|
9
+ check.flags :a, :alien
10
+ check.flags :t, :test
11
+ end
12
+ c.command :contents do
13
+ `gem list --local --no-versions --no-details`.split(/\n/)[3..-1]
14
+ end
15
+ c.command :dependency do
16
+ `gem list --local --no-versions --no-details`.split(/\n/)[3..-1]
17
+ end
18
+ c.command :install do |install|
19
+ # TODO this is very slow and may require caching somehow?!
20
+ install.default do
21
+ `gem list --remote --no-versions --no-details`.split(/\n/)[3..-1]
22
+ end
23
+ end
24
+ c.command :outdated do |outdated|
25
+ outdated.flags :local, :l
26
+ outdated.flags :remote, :r
27
+ outdated.flags :source
28
+ outdated.flags :platform do
29
+ Gem.platforms.map { |platform| platform.to_s }
30
+ end
31
+ end
32
+ c.command :specification do |spec|
33
+ spec.default do
34
+ `gem list --local --no-versions --no-details`.split(/\n/)[3..-1]
35
+ end
36
+ spec.flags :local, :l
37
+ spec.flags :remote, :r
38
+ spec.flags :source
39
+ spec.flags :platform do
40
+ Gem.platforms.map { |platform| platform.to_s }
41
+ end
42
+ end
43
+ c.command :uninstall do |uninstall|
44
+ uninstall.default do
45
+ `gem list --local --no-versions --no-details`.split(/\n/)[3..-1]
46
+ end
47
+ end
48
+ c.command :help do
49
+ ['commands'] + TabTab::Definition['gem'].contents.select do |definition|
50
+ definition.definition_type == :command
51
+ end.map { |definition| definition.name }
52
+ end
53
+ end
54
+
@@ -0,0 +1,23 @@
1
+ TabTab::Definition.register('rake', :import => true) do |c|
2
+ def rake_silent_tasks
3
+ if File.exists?(dotcache = File.join(File.expand_path('~'), ".raketabs-#{Dir.pwd.hash}"))
4
+ File.read(dotcache)
5
+ else
6
+ tasks = `rake --silent --tasks`
7
+ File.open(dotcache, 'w') { |f| f.puts tasks }
8
+ tasks
9
+ end
10
+ end
11
+
12
+ c.default do |current|
13
+ tasks = (rake_silent_tasks.split("\n")[1..-1] || []).map { |line| line.split[1] }
14
+ if current =~ /^([-\w:]+:)/
15
+ upto_last_colon = $1
16
+ p upto_last_colon
17
+ tasks = tasks.map { |t| (t =~ /^#{Regexp.escape upto_last_colon}([-\w:]+)$/) ? "#{$1}" : t }
18
+ end
19
+ tasks
20
+ end
21
+ c.flags :silence, :s
22
+ c.flags :trace, :t
23
+ end
@@ -0,0 +1,8 @@
1
+ TabTab::Definition.register('script/generate', :import => true) do |c|
2
+ c.default do
3
+ generator_help = `script/generate`
4
+ generator_help.scan(/^\ +\w+:\s(.*)$/).map do |list|
5
+ list.first.strip.split(/,[\s\n]*/)
6
+ end.flatten
7
+ end
8
+ end
@@ -0,0 +1,14 @@
1
+ TabTab::Definition.register('script/server', :import => true) do |c|
2
+ c.flag :environment, :e do
3
+ Dir['config/environments/*.rb'].map { |env| env.gsub(/^.*environments\//,'').gsub(/.rb$/,'') }
4
+ end
5
+ # script/server -p TABTAB -> generated first available port 3000, 3001, 3002
6
+ c.flag :port, :p do
7
+ port = 3000
8
+ until `netstat -an | grep "^tcp" | grep #{port}`.strip.empty? || port > 3010
9
+ port += 1
10
+ end
11
+ port > 3010 ? [] : [port.to_s]
12
+ end
13
+ end
14
+
@@ -0,0 +1,139 @@
1
+ require 'optparse'
2
+ require 'yaml'
3
+ require 'rubygems'
4
+
5
+ # TODO extract into BashCompletion.install ...
6
+ # TODO support non-Bash shells
7
+ module InstallTabTab
8
+ class CLI
9
+ include TabTab::LocalConfig
10
+
11
+ attr_reader :options
12
+ attr_reader :development_cli
13
+
14
+ def self.execute(stdout, arguments=[])
15
+ self.new.execute(stdout, arguments)
16
+ end
17
+
18
+ def execute(stdout, arguments=[])
19
+ $stdout = stdout
20
+ parse_options(arguments)
21
+ @to_file = []
22
+ install_externals
23
+ install_for_files
24
+ install_from_gems
25
+ @file = File.open(File.join(home, ".tabtab.bash"), "w")
26
+ @to_file.each { |line| @file << "#{line}\n" }
27
+ @file.close
28
+ end
29
+
30
+ def parse_options(arguments)
31
+ @options = {}
32
+ OptionParser.new do |opts|
33
+ opts.banner = "Usage: #{$0} [options]"
34
+
35
+ opts.on("-d", "--development", "Generate .tabtab.bash to use local bin/tabtab instead of RubyGems tabtab") do |v|
36
+ options[:development_cli] = v
37
+ end
38
+ end.parse!(arguments)
39
+ end
40
+
41
+ def install_externals
42
+ return unless externals = config['external'] || config['externals']
43
+ for app_name_or_hash in externals
44
+ if app_name_or_hash.is_a?(String) || app_name_or_hash.is_a?(Symbol)
45
+ install_cmd_and_aliases(app_name_or_hash.to_s, "--external")
46
+ elsif app_name_or_hash.is_a?(Hash)
47
+ app_name_or_hash.each do |flag, app_list|
48
+ app_list.each do |app_name|
49
+ install_cmd_and_aliases(app_name, "--external")
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ def install_for_files
57
+ return unless files_and_names = config['file'] || config['files']
58
+ for file in files_and_names.keys
59
+ case app_names = files_and_names[file]
60
+ when String
61
+ install_file file, app_names
62
+ when Array
63
+ app_names.each { |app_name| install_file(file, app_name) }
64
+ end
65
+ end
66
+ end
67
+
68
+ def install_file(file, app_name)
69
+ install_cmd_and_aliases(app_name, "--file #{File.expand_path(file)}")
70
+ end
71
+
72
+ def install_from_gems
73
+ find_gems_with_definition_files.each do |gem|
74
+ gem[:app_names].each do |app_name|
75
+ $stdout.puts "RubyGem #{gem[:gem_name]} contains TabTab definition for #{app_name}"
76
+ install_cmd_and_aliases(app_name, "--gem #{gem[:gem_name]}")
77
+ end
78
+ end
79
+ end
80
+
81
+ def find_gems_with_definition_files
82
+ Gem.all_load_paths.inject([]) do |mem, path|
83
+ root = path.gsub(/(lib|bin)$/,'')
84
+ mem << root unless mem.include?(root)
85
+ mem
86
+ end.inject([]) do |mem, path|
87
+ common_file = Dir[File.join(path, "**", "tabtab_definitions.rb")].reject { |tabtab_path| tabtab_path =~ /(spec|test)/ }.first
88
+ gem_name = nil
89
+ unless common_file.nil? || common_file.empty?
90
+ gem_name = common_file.match(/\/([^\/]*)-\d+.\d+.\d+\/lib\//)[1]
91
+ TabTab::Definition.clear
92
+ load common_file
93
+ mem << { :gem_name => gem_name, :app_names => TabTab::Definition.app_names }
94
+ end
95
+ files = Dir[File.join(path, "**", "tabtab_definitions", "**", "*.rb")].reject { |tabtab_path| tabtab_path =~ /(spec|test)/ }
96
+ if files && files.size > 0
97
+ gem_path, gem_name = files.first.match(/^(.*\/([^\/]*)-\d+.\d+.\d+)/)[1..2]
98
+ files.each do |file|
99
+ TabTab::Definition.clear
100
+ load file
101
+ tabtab_path = file.gsub(gem_path, "")
102
+ mem << { :gem_name => "#{gem_name}#{tabtab_path}", :app_names => TabTab::Definition.app_names }
103
+ end
104
+ end
105
+ mem
106
+ end
107
+ end
108
+
109
+ def install_cmd_and_aliases(app_name, arg_str)
110
+ tabtab = tabtab_cmd(arg_str)
111
+ @to_file << "complete -o default -C '#{tabtab}' #{app_name}"
112
+ aliases.each do |alias_cmd, cmd|
113
+ if cmd == app_name
114
+ tabtab = tabtab_cmd(arg_str, cmd)
115
+ @to_file << "complete -o default -C '#{tabtab}' #{alias_cmd}"
116
+ end
117
+ end if aliases
118
+ end
119
+
120
+ def usage
121
+ puts <<-EOS.gsub(/^ /, '')
122
+ USAGE: create a file ~/.tabtab.yml
123
+ EOS
124
+ exit 1
125
+ end
126
+
127
+ def tabtab_cmd(flags, aliased_to=nil)
128
+ tabtab = options[:development_cli] ? File.expand_path(File.dirname(__FILE__) + "/../../bin/tabtab") : "tabtab"
129
+ aliased_to ?
130
+ "#{tabtab} #{flags} --alias #{aliased_to}" :
131
+ "#{tabtab} #{flags}"
132
+ end
133
+
134
+ def aliases
135
+ config["alias"] || config["aliases"]
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,10 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module TabTab
5
+ VERSION = '0.9.0'
6
+ end
7
+
8
+ require 'tabtab/local_config'
9
+ require 'tabtab/completions'
10
+ require 'tabtab/definitions'
@@ -0,0 +1,116 @@
1
+ require 'optparse'
2
+
3
+ module TabTab
4
+ class CLI
5
+ include TabTab::LocalConfig
6
+
7
+ attr_reader :stdout
8
+ attr_reader :full_line
9
+ attr_reader :options, :global_config
10
+ attr_reader :current_token, :previous_token
11
+
12
+
13
+ def self.execute(stdout, arguments=[])
14
+ self.new.execute(stdout, arguments)
15
+ end
16
+
17
+ def execute(stdout, arguments=[])
18
+ @stdout = stdout
19
+ # require "shellwords"
20
+ # @full_line = ENV['COMP_LINE']
21
+ # @full_line_argv = Shellwords.shellwords(@full_line)
22
+ @app_name, @current_token, @previous_token = arguments[-3..-1]
23
+ parse_options(arguments[0..-4])
24
+ load_global_config
25
+ if options[:external]
26
+ process_external
27
+ elsif options[:gem]
28
+ process_gem
29
+ elsif options[:file]
30
+ process_file
31
+ else
32
+ usage
33
+ end
34
+ end
35
+
36
+ def parse_options(arguments)
37
+ @options = {}
38
+ OptionParser.new do |opts|
39
+ opts.banner = "Usage: #{$0} [options] app_name current_token previous_token"
40
+
41
+ opts.on("--alias ALIAS", "Map an alias to an actual command with its own tabtab definition") do |v|
42
+ options[:alias] = v
43
+ end
44
+ opts.on("--external", "Automatically import flags from application's -h flags") do |v|
45
+ options[:external] = v
46
+ end
47
+ opts.on("--gem GEM_NAME", "Load the tabtab definition from within target RubyGem") do |v|
48
+ options[:gem] = v
49
+ end
50
+ opts.on("--file FILE_NAME", "Load the tabtab definition from a specific file") do |v|
51
+ options[:file] = v
52
+ end
53
+ end.parse!(arguments)
54
+ end
55
+
56
+ def app_name
57
+ options[:alias] || @app_name
58
+ end
59
+
60
+ #
61
+ # Support for external apps (optionally configured in ~/.tabtab.yml)
62
+ # Generates a completion list from the -h help output of the target application
63
+ # --external
64
+ #
65
+ def process_external
66
+ usage unless config
67
+ # 'externals' => ['app1', 'app2', { '-?' => ['app3'] }]
68
+ # Only look for the internal hashes, and return -? if app_name == 'app3', else nil
69
+ options_flag = externals.inject(nil) do |o_flag, app_or_hash|
70
+ next if app_or_hash.is_a?(String) || app_or_hash.is_a?(Symbol)
71
+ app_or_hash.inject(nil) do |flag, flag_app_list|
72
+ flag, app_list = flag_app_list
73
+ flag if app_list.include?(app_name)
74
+ end
75
+ end
76
+ options_flag = options_flag.nil? ? '-h' : options_flag
77
+ stdout.puts TabTab::Completions::External.new(app_name, options_flag, global_config).starts_with(current_token)
78
+ end
79
+
80
+ def externals
81
+ config["external"] || config["externals"]
82
+ end
83
+
84
+ #
85
+ # Support for RubyGem-based completion definitions (found in any gem path)
86
+ # --gem gem_name
87
+ #
88
+ def process_gem
89
+ stdout.puts TabTab::Completions::Gem.new(options[:gem], app_name, current_token, previous_token, global_config).extract.join("\n")
90
+ end
91
+
92
+ #
93
+ # Support for file-based completion definitions (found in target file)
94
+ # --file /path/to/definition.rb
95
+ #
96
+ def process_file
97
+ stdout.puts TabTab::Completions::File.new(options[:file], app_name, current_token, previous_token, global_config).extract.join("\n")
98
+ end
99
+
100
+ def load_global_config
101
+ @global_config ||= {}
102
+ @global_config[:shortflags] = config["shortflags"] || "enable"
103
+ end
104
+
105
+ def usage
106
+ stdout.puts <<-EOS.gsub(/^ /, '')
107
+ Invalid #{@app_type} flag provided to #{$0}.
108
+ USAGE:
109
+ #{$0} --external
110
+ #{$0} --gem GEM_NAME
111
+ #{$0} --file FILE_PATH
112
+ EOS
113
+ exit
114
+ end
115
+ end
116
+ end