escort 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 (69) hide show
  1. checksums.yaml +8 -8
  2. data/.irbrc +2 -0
  3. data/.travis.yml +1 -1
  4. data/README.md +272 -3
  5. data/TODO.md +118 -71
  6. data/examples/.my_apprc +24 -0
  7. data/examples/basic_config_file +16 -0
  8. data/examples/basic_conflicts +1 -1
  9. data/examples/basic_with_everything +30 -0
  10. data/examples/suite_complex +65 -0
  11. data/examples/{command → suite_simple} +0 -0
  12. data/examples/suite_with_sub_commands +94 -0
  13. data/lib/escort.rb +6 -4
  14. data/lib/escort/action_command/base.rb +7 -5
  15. data/lib/escort/app.rb +2 -8
  16. data/lib/escort/auto_options.rb +5 -3
  17. data/lib/escort/formatter/cursor_position.rb +29 -0
  18. data/lib/escort/formatter/default_help_formatter.rb +37 -32
  19. data/lib/escort/formatter/option.rb +1 -1
  20. data/lib/escort/formatter/stream_output_formatter.rb +88 -0
  21. data/lib/escort/formatter/{borderless_table.rb → string_grid.rb} +21 -19
  22. data/lib/escort/formatter/string_splitter.rb +24 -4
  23. data/lib/escort/setup/configuration/loader.rb +8 -2
  24. data/lib/escort/setup/configuration/locator/chaining.rb +29 -0
  25. data/lib/escort/setup/configuration/locator/executing_script_directory.rb +15 -0
  26. data/lib/escort/setup/configuration/locator/specified_directory.rb +21 -0
  27. data/lib/escort/setup/configuration/reader.rb +4 -2
  28. data/lib/escort/setup/configuration/writer.rb +6 -2
  29. data/lib/escort/setup/dsl/command.rb +7 -8
  30. data/lib/escort/setup/dsl/global.rb +3 -51
  31. data/lib/escort/version.rb +1 -1
  32. data/spec/integration/basic_config_file_spec.rb +82 -0
  33. data/spec/integration/suite_simple_spec.rb +45 -0
  34. data/spec/integration/suite_sub_command_spec.rb +51 -0
  35. data/spec/lib/escort/action_command/base_spec.rb +200 -0
  36. data/spec/lib/escort/formatter/option_spec.rb +2 -2
  37. data/spec/lib/escort/formatter/stream_output_formatter_spec.rb +214 -0
  38. data/spec/lib/escort/formatter/string_grid_spec.rb +59 -0
  39. data/spec/lib/escort/setup/configuration/generator_spec.rb +101 -0
  40. data/spec/lib/escort/setup/configuration/loader_spec.rb +79 -0
  41. data/spec/lib/escort/setup/configuration/locator/chaining_spec.rb +81 -0
  42. data/spec/lib/escort/setup/configuration/locator/descending_to_home_spec.rb +57 -0
  43. data/spec/lib/escort/setup/configuration/locator/executing_script_directory_spec.rb +29 -0
  44. data/spec/lib/escort/setup/configuration/locator/specified_directory_spec.rb +33 -0
  45. data/spec/lib/escort/setup/configuration/merge_tool_spec.rb +41 -0
  46. data/spec/lib/escort/setup/configuration/reader_spec.rb +41 -0
  47. data/spec/lib/escort/setup/configuration/writer_spec.rb +75 -0
  48. data/spec/spec_helper.rb +2 -1
  49. metadata +44 -24
  50. data/examples/attic/1_1_basic.rb +0 -15
  51. data/examples/attic/1_2_basic_requires_arguments.rb +0 -15
  52. data/examples/attic/2_2_command.rb +0 -18
  53. data/examples/attic/2_2_command_requires_arguments.rb +0 -20
  54. data/examples/attic/2_3_nested_commands.rb +0 -26
  55. data/examples/attic/3_validations.rb +0 -31
  56. data/examples/attic/4_1_config_file.rb +0 -42
  57. data/examples/attic/argument_handling/basic.rb +0 -12
  58. data/examples/attic/argument_handling/basic_command.rb +0 -18
  59. data/examples/attic/argument_handling/no_arguments.rb +0 -14
  60. data/examples/attic/argument_handling/no_arguments_command.rb +0 -20
  61. data/examples/attic/command_aliases/app.rb +0 -31
  62. data/examples/attic/config_file/.apprc2 +0 -16
  63. data/examples/attic/config_file/app.rb +0 -78
  64. data/examples/attic/config_file/sub_commands.rb +0 -35
  65. data/examples/attic/default_command/app.rb +0 -20
  66. data/examples/attic/sub_commands/app.rb +0 -18
  67. data/examples/attic/validation_basic/app.rb +0 -31
  68. data/lib/escort/formatter/terminal_formatter.rb +0 -58
  69. data/lib/escort/setup/dsl/validations.rb +0 -25
@@ -0,0 +1,24 @@
1
+ {
2
+ "global": {
3
+ "options": {
4
+ "option1": "option from file",
5
+ "config": null,
6
+ "verbosity": "WARN",
7
+ "error_output_format": "basic"
8
+ },
9
+ "commands": {
10
+ "escort": {
11
+ "options": {
12
+ "create_config": null,
13
+ "create_default_config": null,
14
+ "update_config": null,
15
+ "update_default_config": null
16
+ },
17
+ "commands": {
18
+ }
19
+ }
20
+ }
21
+ },
22
+ "user": {
23
+ }
24
+ }
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.expand_path(__FILE__), "..", "..", "lib", "escort"))
4
+ require File.join(File.expand_path(__FILE__), "..", "commands", "example_command")
5
+
6
+ Escort::App.create do |app|
7
+ app.config_file ".my_apprc", :autocreate => true
8
+
9
+ app.options do |opts|
10
+ opts.opt :option1, "Option1", :short => '-o', :long => '--option1', :type => :string, :default => "option 1"
11
+ end
12
+
13
+ app.action do |options, arguments|
14
+ Escort::ExampleCommand.new(options, arguments).execute
15
+ end
16
+ end
@@ -8,7 +8,7 @@ Escort::App.create do |app|
8
8
  opts.opt :flag1, "Flag 1", :short => '-f', :long => '--flag1', :type => :boolean
9
9
  opts.opt :flag2, "Flag 2", :short => :none, :long => '--flag2', :type => :boolean
10
10
 
11
- opts.conflict :flag1, :flag2, :flag3
11
+ opts.conflict :flag1, :flag2
12
12
  end
13
13
 
14
14
  app.action do |options, arguments|
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.expand_path(__FILE__), "..", "..", "lib", "escort"))
4
+ require File.join(File.expand_path(__FILE__), "..", "commands", "example_command")
5
+
6
+ Escort::App.create do |app|
7
+ app.version "0.1.1"
8
+ app.summary "Summary 1"
9
+ app.description "Description 1"
10
+
11
+ app.config_file ".my_apprc", :autocreate => false
12
+
13
+ app.requires_arguments
14
+
15
+ app.options do |opts|
16
+ opts.opt :option1, "Option1", :short => '-o', :long => '--option1', :type => :string, :default => "option 1"
17
+ opts.opt :option2, "Option2", :short => :none, :long => '--option2', :type => :string, :multi => true
18
+ opts.opt :int_option, "Int option", :short => '-i', :long => '--int-option', :type => :int
19
+ opts.opt :flag1, "Flag 1", :short => '-f', :long => '--flag1', :type => :boolean, :default => true
20
+ opts.opt :flag2, "Flag 2", :short => :none, :long => '--flag2', :type => :boolean
21
+
22
+ opts.conflict :flag1, :flag2
23
+ opts.dependency :option1, :on => :flag1
24
+ opts.validate(:int_option, "must be greater than 10") { |option| option > 10 }
25
+ end
26
+
27
+ app.action do |options, arguments|
28
+ Escort::ExampleCommand.new(options, arguments).execute
29
+ end
30
+ end
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.expand_path(__FILE__), "..", "..", "lib", "escort"))
4
+ require File.join(File.expand_path(__FILE__), "..", "commands", "example_command")
5
+
6
+ Escort::App.create do |app|
7
+ app.version "0.1.1"
8
+ app.summary "Summary 1"
9
+ app.description "Description 1"
10
+
11
+ app.config_file ".my_apprc", :autocreate => false
12
+
13
+ app.requires_arguments
14
+
15
+ app.options do |opts|
16
+ opts.opt :option1, "Option1", :short => '-o', :long => '--option1', :type => :string, :default => "option 1"
17
+ opts.opt :option2, "Option2", :short => :none, :long => '--option2', :type => :string, :multi => true
18
+ opts.opt :int_option, "Int option", :short => '-i', :long => '--int-option', :type => :int
19
+ opts.opt :flag1, "Flag 1", :short => '-f', :long => '--flag1', :type => :boolean, :default => true
20
+ opts.opt :flag2, "Flag 2", :short => :none, :long => '--flag2', :type => :boolean
21
+
22
+ opts.conflict :flag1, :flag2
23
+ opts.dependency :option1, :on => :flag1
24
+ opts.validate(:int_option, "must be greater than 10") { |option| option > 10 }
25
+ end
26
+
27
+ app.command :command1 do |command|
28
+ command.summary "Command summary 1"
29
+ command.description "Command description 1"
30
+
31
+ command.options do |opts|
32
+ opts.opt :option1, "Option1 for command1", :short => '-o', :long => '--option1', :type => :string, :default => "option 1 command 1"
33
+ opts.opt :flag_for_command1, "Flag for command 1", :short => :none, :long => '--flag-for-command1', :type => :boolean
34
+
35
+ opts.dependency :option1, :on => :flag_for_command1
36
+ end
37
+
38
+ command.action do |options, arguments|
39
+ Escort::ExampleCommand.new(options, arguments).execute
40
+ end
41
+ end
42
+
43
+ app.command :command2, :aliases => :c2 do |command|
44
+ command.summary "Command summary 2"
45
+ command.description "Command description 2"
46
+
47
+ command.requires_arguments false
48
+
49
+ command.options do |opts|
50
+ opts.opt :optionb, "Optionb", :short => :none, :long => '--optionb', :type => :string, :multi => true
51
+ opts.opt :float_option, "Float option", :short => '-d', :long => '--float-option', :type => :float
52
+
53
+ opts.conflict :optionb, :float_option
54
+ opts.validate(:float_option, "must be less than 5") { |option| option < 5 }
55
+ end
56
+
57
+ command.action do |options, arguments|
58
+ Escort::ExampleCommand.new(options, arguments).execute
59
+ end
60
+ end
61
+
62
+ app.action do |options, arguments|
63
+ Escort::ExampleCommand.new(options, arguments).execute
64
+ end
65
+ end
File without changes
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.expand_path(__FILE__), "..", "..", "lib", "escort"))
4
+ require File.join(File.expand_path(__FILE__), "..", "commands", "example_command")
5
+
6
+ Escort::App.create do |app|
7
+ app.version "0.1.1"
8
+ app.summary "Summary 1"
9
+ app.description "Description 1"
10
+
11
+ app.config_file ".my_apprc", :autocreate => false
12
+
13
+ app.requires_arguments
14
+
15
+ app.options do |opts|
16
+ opts.opt :option1, "Option1", :short => '-o', :long => '--option1', :type => :string, :default => "option 1"
17
+ opts.opt :option2, "Option2", :short => :none, :long => '--option2', :type => :string, :multi => true
18
+ opts.opt :int_option, "Int option", :short => '-i', :long => '--int-option', :type => :int
19
+ opts.opt :flag1, "Flag 1", :short => '-f', :long => '--flag1', :type => :boolean, :default => true
20
+ opts.opt :flag2, "Flag 2", :short => :none, :long => '--flag2', :type => :boolean
21
+
22
+ opts.conflict :flag1, :flag2
23
+ opts.dependency :option1, :on => :flag1
24
+ opts.validate(:int_option, "must be greater than 10") { |option| option > 10 }
25
+ end
26
+
27
+ app.command :command1 do |command|
28
+ command.summary "Command summary 1"
29
+ command.description "Command description 1"
30
+
31
+ command.options do |opts|
32
+ opts.opt :option1, "Option1 for command1", :short => '-o', :long => '--option1', :type => :string, :default => "option 1 command 1"
33
+ opts.opt :flag_for_command1, "Flag for command 1", :short => :none, :long => '--flag-for-command1', :type => :boolean
34
+
35
+ opts.dependency :option1, :on => :flag_for_command1
36
+ end
37
+
38
+ command.action do |options, arguments|
39
+ Escort::ExampleCommand.new(options, arguments).execute
40
+ end
41
+ end
42
+
43
+ app.command :command2, :aliases => :c2 do |command|
44
+ command.summary "Command summary 2"
45
+ command.description "Command description 2"
46
+
47
+ command.requires_arguments false
48
+
49
+ command.options do |opts|
50
+ opts.opt :optionb, "Optionb", :short => :none, :long => '--optionb', :type => :string, :multi => true
51
+ opts.opt :float_option, "Float option", :short => '-d', :long => '--float-option', :type => :float
52
+
53
+ opts.conflict :optionb, :float_option
54
+ opts.validate(:float_option, "must be less than 5") { |option| option < 5 }
55
+ end
56
+
57
+ command.command :sub_command1 do |command|
58
+ command.summary "Sub command summary 1"
59
+ command.description "Sub command description 1"
60
+
61
+ command.requires_arguments
62
+
63
+ command.options do |opts|
64
+ opts.opt :flag2, "Flag 2", :short => :none, :long => '--flag2', :type => :boolean
65
+ end
66
+
67
+ command.action do |options, arguments|
68
+ Escort::ExampleCommand.new(options, arguments).execute
69
+ end
70
+ end
71
+
72
+ command.command :sub_command2 do |command|
73
+ command.summary "Sub command summary 2"
74
+ command.description "Sub command description 2"
75
+
76
+ command.options do |opts|
77
+ opts.opt :sub_option1, "Sub option1", :short => '-s', :long => '--sub-option1', :type => :string
78
+ opts.validate(:sub_option1, "must be 'x' or 'y'") { |option| ['x', 'y'].include? option }
79
+ end
80
+
81
+ command.action do |options, arguments|
82
+ Escort::ExampleCommand.new(options, arguments).execute
83
+ end
84
+ end
85
+
86
+ command.action do |options, arguments|
87
+ Escort::ExampleCommand.new(options, arguments).execute
88
+ end
89
+ end
90
+
91
+ app.action do |options, arguments|
92
+ Escort::ExampleCommand.new(options, arguments).execute
93
+ end
94
+ end
@@ -14,20 +14,22 @@ require 'escort/formatter/global_command'
14
14
  require 'escort/formatter/shell_command_executor'
15
15
  require 'escort/formatter/terminal'
16
16
  require 'escort/formatter/string_splitter'
17
- require 'escort/formatter/terminal_formatter'
18
- require 'escort/formatter/borderless_table'
19
- #require 'escort/formatter/common'
17
+ require 'escort/formatter/cursor_position'
18
+ require 'escort/formatter/stream_output_formatter'
19
+ require 'escort/formatter/string_grid'
20
20
  require 'escort/formatter/default_help_formatter'
21
21
 
22
22
  require 'escort/setup/dsl/options'
23
23
  require 'escort/setup/dsl/action'
24
- require 'escort/setup/dsl/validations'
25
24
  require 'escort/setup/dsl/command'
26
25
  require 'escort/setup/dsl/config_file'
27
26
  require 'escort/setup/dsl/global'
28
27
 
29
28
  require 'escort/setup/configuration/locator/base'
30
29
  require 'escort/setup/configuration/locator/descending_to_home'
30
+ require 'escort/setup/configuration/locator/executing_script_directory'
31
+ require 'escort/setup/configuration/locator/specified_directory'
32
+ require 'escort/setup/configuration/locator/chaining'
31
33
  require 'escort/setup/configuration/merge_tool'
32
34
  require 'escort/setup/configuration/instance'
33
35
  require 'escort/setup/configuration/reader'
@@ -19,17 +19,18 @@ module Escort
19
19
  def command_context
20
20
  return @command_context if @command_context
21
21
  @command_context = []
22
- current_command_hash = options[:global][:commands]
22
+ options[:global] ||= {}
23
+ current_command_hash = options[:global][:commands] || {}
23
24
  until current_command_hash.keys.empty?
24
25
  key = current_command_hash.keys.first
25
26
  @command_context << key
26
- current_command_hash = current_command_hash[key][:commands]
27
+ current_command_hash = current_command_hash[key][:commands] || {}
27
28
  end
28
29
  @command_context
29
30
  end
30
31
 
31
32
  def command_name
32
- command_context.first || :global
33
+ command_context.last || :global
33
34
  end
34
35
 
35
36
  def command_options
@@ -73,8 +74,9 @@ module Escort
73
74
  end
74
75
 
75
76
  def ensure_ancestor(generation_number, &block)
76
- return {} if generation_number < 1
77
- return {} unless command_context.size > generation_number
77
+ return {} if generation_number < 0
78
+ #return {} if generation_number == 0
79
+ return {} unless command_context.size >= generation_number
78
80
  ancestor_context = command_context.dup.slice(0, command_context.size - generation_number)
79
81
  block.call(ancestor_context)
80
82
  end
@@ -2,14 +2,6 @@ module Escort
2
2
  class App
3
3
  #TODO ensure that every command must have an action
4
4
  class << self
5
- #def create(option_string = '', &block)
6
- #cli_app_configuration = Escort::Setup::Dsl::Global.new(&block)
7
- #setup = Escort::SetupAccessor.new(cli_app_configuration)
8
- #app = self.new(option_string, setup)
9
- #app.execute
10
- #exit(0)
11
- #end
12
-
13
5
  def create(option_string = '', &block)
14
6
  self.new(option_string, &block).setup_application.execute
15
7
  exit(0)
@@ -54,6 +46,8 @@ module Escort
54
46
  invoked_options, arguments = Escort::OptionParser.new(configuration, setup).parse(cli_options)
55
47
  context = context_from_options(invoked_options[:global])
56
48
  action = setup.action_for(context)
49
+ current_command = context.empty? ? :global : context.last
50
+ raise Escort::ClientError.new("No action defined for command '#{current_command}'") unless action
57
51
  actual_arguments = Escort::Arguments.read(arguments, setup.arguments_required_for(context))
58
52
  rescue => e
59
53
  handle_escort_error(e)
@@ -5,7 +5,9 @@ module Escort
5
5
  if setup.has_config_file?
6
6
  setup.add_global_option :config, "Configuration file to use for this execution", :short => :none, :long => '--config', :type => :string
7
7
 
8
- setup.add_global_command :escort, :description => "Auto created utility command", :aliases => [] do |command|
8
+ setup.add_global_command :escort, :aliases => [] do |command|
9
+ command.summary "Auto created utility command"
10
+ command.description "Auto created utility command"
9
11
  command.requires_arguments false
10
12
 
11
13
  command.options do |opts|
@@ -13,9 +15,9 @@ module Escort
13
15
  opts.opt :create_default_config, "Create a default configuration file", :short => :none, :long => '--create-default-config', :type => :boolean, :default => false
14
16
  opts.opt :update_config, "Update configuration file at specified location", :short => :none, :long => '--update-config', :type => :string
15
17
  opts.opt :update_default_config, "Update the default configuration file", :short => :none, :long => '--update-default-config', :type => :boolean, :default => false
16
- end
17
18
 
18
- command.conflicting_options :create_config, :create_default_config, :update_config, :update_default_config
19
+ opts.conflict :create_config, :create_default_config, :update_config, :update_default_config
20
+ end
19
21
 
20
22
  command.action do |options, arguments|
21
23
  ActionCommand::EscortUtilityCommand.new(setup, options, arguments).execute
@@ -0,0 +1,29 @@
1
+ module Escort
2
+ module Formatter
3
+ class CursorPosition
4
+ attr_reader :max_line_width, :position
5
+
6
+ def initialize(max_line_width)
7
+ @max_line_width = max_line_width
8
+ reset
9
+ end
10
+
11
+ def update_for(string)
12
+ @position += string.length
13
+ raise Escort::InternalError.new("Cursor position for help output is out of bounds") if position > max_line_width
14
+ end
15
+
16
+ def newline?
17
+ @position == 0
18
+ end
19
+
20
+ def reset
21
+ @position = 0
22
+ end
23
+
24
+ def chars_to_end_of_line
25
+ max_line_width - position
26
+ end
27
+ end
28
+ end
29
+ end
@@ -13,48 +13,49 @@ module Escort
13
13
  commands = Commands.new(setup, context)
14
14
  current_command = Commands.command_for(setup, context)
15
15
 
16
- TerminalFormatter.display($stdout, Terminal.width) do |d|
17
- name_help(current_command, d)
18
- usage_help(current_command, d)
19
- version_help(current_command, d)
20
- options_help(options, d)
21
- commands_help(commands, d)
16
+ StreamOutputFormatter.new($stdout, :max_output_width => Terminal.width) do |f|
17
+ name_help(current_command, f)
18
+ usage_help(current_command, f)
19
+ version_help(current_command, f)
20
+ options_help(options, f)
21
+ commands_help(commands, f)
22
22
  end
23
23
  end
24
24
 
25
25
  private
26
26
 
27
- def name_help(current_command, d)
28
- d.puts "NAME"
29
- d.indent(4) do
30
- d.table(:columns => 3, :newlines => 1) do |t|
31
- t.row current_command.script_name, '-', current_command.summary
27
+ def name_help(current_command, f)
28
+ f.puts "NAME"
29
+ f.indent(4) do |f|
30
+ f.grid(:columns => 3) do |t|
31
+ t.row current_command.script_name, '-', setup.summary_for(context)
32
32
  end
33
- d.put(setup.description_for(context), :newlines => 2) if setup.description_for(context)
33
+ f.newline
34
+ f.puts(setup.description_for(context), :newlines => 2) if setup.description_for(context)
34
35
  end
35
36
  end
36
37
 
37
- def usage_help(current_command, d)
38
- d.puts "USAGE"
39
- d.indent(4) do
40
- d.put current_command.usage, :newlines => 2
38
+ def usage_help(current_command, f)
39
+ f.puts "USAGE"
40
+ f.indent(4) do |f|
41
+ f.puts current_command.usage, :newlines => 2
41
42
  end
42
43
  end
43
44
 
44
- def version_help(current_command, d)
45
+ def version_help(current_command, f)
45
46
  if setup.version
46
- d.puts "VERSION"
47
- d.indent(4) {
48
- d.put setup.version, :newlines => 2
49
- }
47
+ f.puts "VERSION"
48
+ f.indent(4) do |f|
49
+ f.puts setup.version, :newlines => 2
50
+ end
50
51
  end
51
52
  end
52
53
 
53
- def options_help(options, d)
54
+ def options_help(options, f)
54
55
  if options.count > 0
55
- d.puts "OPTIONS"
56
- d.indent(4) {
57
- d.table(:columns => 3, :newlines => 1) do |t|
56
+ f.puts "OPTIONS"
57
+ f.indent(4) do |f|
58
+ f.grid(:columns => 3) do |t|
58
59
  options.each do |option|
59
60
  t.row option.usage, '-', option.description
60
61
  option_conflicts_help(option, t)
@@ -62,7 +63,8 @@ module Escort
62
63
  option_validations_help(option, t)
63
64
  end
64
65
  end
65
- }
66
+ f.newline
67
+ end
66
68
  end
67
69
  end
68
70
 
@@ -80,20 +82,23 @@ module Escort
80
82
 
81
83
  def option_validations_help(option, t)
82
84
  if option.has_validations?
83
- t.row '', '', "- #{option.validations}"
85
+ option.validations.each do |validation|
86
+ t.row '', '', "- #{validation}"
87
+ end
84
88
  end
85
89
  end
86
90
 
87
- def commands_help(commands, d)
91
+ def commands_help(commands, f)
88
92
  if commands.count > 0
89
- d.puts "COMMANDS"
90
- d.indent(4) {
91
- d.table(:columns => 3, :newlines => 1) do |t|
93
+ f.puts "COMMANDS"
94
+ f.indent(4) do |f|
95
+ f.grid(:columns => 3) do |t|
92
96
  commands.each do |command|
93
97
  t.row command.name_with_aliases, '-', command.outline
94
98
  end
95
99
  end
96
- }
100
+ f.newline
101
+ end
97
102
  end
98
103
  end
99
104
  end