convoy 1.0.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 (109) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.irbrc +3 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +8 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +22 -0
  9. data/README.md +705 -0
  10. data/Rakefile +1 -0
  11. data/convoy.gemspec +24 -0
  12. data/examples/.my_apprc +24 -0
  13. data/examples/basic +10 -0
  14. data/examples/basic_config_file +16 -0
  15. data/examples/basic_conflicts +17 -0
  16. data/examples/basic_depends_on +25 -0
  17. data/examples/basic_flags +15 -0
  18. data/examples/basic_options +14 -0
  19. data/examples/basic_options_multi +15 -0
  20. data/examples/basic_require_arguments +17 -0
  21. data/examples/basic_texts +21 -0
  22. data/examples/basic_validations +21 -0
  23. data/examples/basic_with_everything +30 -0
  24. data/examples/commands/example_command.rb +13 -0
  25. data/examples/suite_complex +65 -0
  26. data/examples/suite_simple +19 -0
  27. data/examples/suite_with_sub_commands +94 -0
  28. data/lib/convoy.rb +83 -0
  29. data/lib/convoy/action_command/base.rb +85 -0
  30. data/lib/convoy/action_command/escort_utility_command.rb +53 -0
  31. data/lib/convoy/app.rb +127 -0
  32. data/lib/convoy/arguments.rb +20 -0
  33. data/lib/convoy/auto_options.rb +71 -0
  34. data/lib/convoy/error/error.rb +33 -0
  35. data/lib/convoy/formatter/command.rb +87 -0
  36. data/lib/convoy/formatter/commands.rb +37 -0
  37. data/lib/convoy/formatter/cursor_position.rb +29 -0
  38. data/lib/convoy/formatter/default_help_formatter.rb +117 -0
  39. data/lib/convoy/formatter/global_command.rb +17 -0
  40. data/lib/convoy/formatter/option.rb +152 -0
  41. data/lib/convoy/formatter/options.rb +28 -0
  42. data/lib/convoy/formatter/shell_command_executor.rb +49 -0
  43. data/lib/convoy/formatter/stream_output_formatter.rb +88 -0
  44. data/lib/convoy/formatter/string_grid.rb +108 -0
  45. data/lib/convoy/formatter/string_splitter.rb +50 -0
  46. data/lib/convoy/formatter/terminal.rb +30 -0
  47. data/lib/convoy/global_pre_parser.rb +43 -0
  48. data/lib/convoy/logger.rb +75 -0
  49. data/lib/convoy/option_dependency_validator.rb +82 -0
  50. data/lib/convoy/option_parser.rb +155 -0
  51. data/lib/convoy/setup/configuration/generator.rb +75 -0
  52. data/lib/convoy/setup/configuration/instance.rb +34 -0
  53. data/lib/convoy/setup/configuration/loader.rb +43 -0
  54. data/lib/convoy/setup/configuration/locator/base.rb +19 -0
  55. data/lib/convoy/setup/configuration/locator/chaining.rb +29 -0
  56. data/lib/convoy/setup/configuration/locator/descending_to_home.rb +23 -0
  57. data/lib/convoy/setup/configuration/locator/executing_script_directory.rb +15 -0
  58. data/lib/convoy/setup/configuration/locator/specified_directory.rb +21 -0
  59. data/lib/convoy/setup/configuration/merge_tool.rb +38 -0
  60. data/lib/convoy/setup/configuration/reader.rb +36 -0
  61. data/lib/convoy/setup/configuration/writer.rb +46 -0
  62. data/lib/convoy/setup/dsl/action.rb +17 -0
  63. data/lib/convoy/setup/dsl/command.rb +67 -0
  64. data/lib/convoy/setup/dsl/config_file.rb +13 -0
  65. data/lib/convoy/setup/dsl/global.rb +29 -0
  66. data/lib/convoy/setup/dsl/options.rb +81 -0
  67. data/lib/convoy/setup_accessor.rb +206 -0
  68. data/lib/convoy/trollop.rb +861 -0
  69. data/lib/convoy/utils.rb +21 -0
  70. data/lib/convoy/validator.rb +45 -0
  71. data/spec/integration/basic_config_file_spec.rb +126 -0
  72. data/spec/integration/basic_conflicts_spec.rb +47 -0
  73. data/spec/integration/basic_depends_on_spec.rb +275 -0
  74. data/spec/integration/basic_options_spec.rb +41 -0
  75. data/spec/integration/basic_options_with_multi_spec.rb +30 -0
  76. data/spec/integration/basic_spec.rb +38 -0
  77. data/spec/integration/basic_validations_spec.rb +77 -0
  78. data/spec/integration/basic_with_arguments_spec.rb +35 -0
  79. data/spec/integration/basic_with_text_fields_spec.rb +21 -0
  80. data/spec/integration/suite_simple_spec.rb +45 -0
  81. data/spec/integration/suite_sub_command_spec.rb +51 -0
  82. data/spec/lib/convoy/action_command/base_spec.rb +200 -0
  83. data/spec/lib/convoy/formatter/command_spec.rb +238 -0
  84. data/spec/lib/convoy/formatter/global_command_spec.rb +50 -0
  85. data/spec/lib/convoy/formatter/option_spec.rb +300 -0
  86. data/spec/lib/convoy/formatter/shell_command_executor_spec.rb +59 -0
  87. data/spec/lib/convoy/formatter/stream_output_formatter_spec.rb +214 -0
  88. data/spec/lib/convoy/formatter/string_grid_spec.rb +59 -0
  89. data/spec/lib/convoy/formatter/string_splitter_spec.rb +50 -0
  90. data/spec/lib/convoy/formatter/terminal_spec.rb +19 -0
  91. data/spec/lib/convoy/setup/configuration/generator_spec.rb +101 -0
  92. data/spec/lib/convoy/setup/configuration/loader_spec.rb +79 -0
  93. data/spec/lib/convoy/setup/configuration/locator/chaining_spec.rb +81 -0
  94. data/spec/lib/convoy/setup/configuration/locator/descending_to_home_spec.rb +57 -0
  95. data/spec/lib/convoy/setup/configuration/locator/executing_script_directory_spec.rb +29 -0
  96. data/spec/lib/convoy/setup/configuration/locator/specified_directory_spec.rb +33 -0
  97. data/spec/lib/convoy/setup/configuration/merge_tool_spec.rb +41 -0
  98. data/spec/lib/convoy/setup/configuration/reader_spec.rb +41 -0
  99. data/spec/lib/convoy/setup/configuration/writer_spec.rb +75 -0
  100. data/spec/lib/convoy/setup_accessor_spec.rb +226 -0
  101. data/spec/lib/convoy/utils_spec.rb +30 -0
  102. data/spec/spec_helper.rb +29 -0
  103. data/spec/support/integration_helpers.rb +2 -0
  104. data/spec/support/matchers/execute_action_for_command_matcher.rb +21 -0
  105. data/spec/support/matchers/execute_action_with_arguments_matcher.rb +25 -0
  106. data/spec/support/matchers/execute_action_with_options_matcher.rb +29 -0
  107. data/spec/support/matchers/exit_with_code_matcher.rb +29 -0
  108. data/spec/support/shared_contexts/integration_setup.rb +34 -0
  109. metadata +292 -0
@@ -0,0 +1,87 @@
1
+ module Convoy
2
+ module Formatter
3
+ class Command
4
+ attr_reader :setup, :context, :name
5
+
6
+ def initialize(command_name, setup, context)
7
+ @setup = setup
8
+ @context = context
9
+ @name = command_name.to_sym
10
+ end
11
+
12
+ def name_with_aliases
13
+ [aliases, name].flatten.join(', ')
14
+ end
15
+
16
+ def outline
17
+ summary.empty? ? description : summary
18
+ end
19
+
20
+ def description
21
+ @description ||= setup.command_description_for(name, context) || ""
22
+ end
23
+
24
+ def summary
25
+ @summary ||= setup.command_summary_for(name, context) || ""
26
+ end
27
+
28
+ def aliases
29
+ @aliases ||= setup.command_aliases_for(name, context)
30
+ end
31
+
32
+ def has_aliases?
33
+ aliases && aliases.size > 0 ? true : false
34
+ end
35
+
36
+ def script_name
37
+ [canonical_script_name, context].flatten.join(" ")
38
+ end
39
+
40
+ def child_commands
41
+ @child_commands ||= setup.canonical_command_names_for(context) || []
42
+ end
43
+
44
+ def has_child_commands?
45
+ child_commands.length > 0
46
+ end
47
+
48
+ def requires_arguments?
49
+ @requires_arguments ||= setup.arguments_required_for(context)
50
+ end
51
+
52
+ def usage
53
+ [script_name_usage_string, parent_commands_usage_string, child_command_usage_string, arguments_usage_string].flatten.reject(&:empty?).join(" ")
54
+ end
55
+
56
+ private
57
+
58
+ def script_name_usage_string
59
+ "#{canonical_script_name} [options]"
60
+ end
61
+
62
+ def parent_commands_usage_string
63
+ context.map { |command_name| command_with_options(command_name) }.join(" ")
64
+ end
65
+
66
+ def child_command_usage_string
67
+ has_child_commands? ? "command [command_options]" : ""
68
+ end
69
+
70
+ def arguments_usage_string
71
+ requires_arguments? ? "arguments" : "[arguments]"
72
+ end
73
+
74
+ def command_with_options(command_name)
75
+ "#{command_name} [#{command_name}_options]"
76
+ end
77
+
78
+ def canonical_script_name
79
+ File.basename($0)
80
+ end
81
+
82
+ def alias_string
83
+ @alias_string ||= aliases.join(", ") if has_aliases?
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,37 @@
1
+ module Convoy
2
+ module Formatter
3
+ class Commands
4
+ include Enumerable
5
+
6
+ class << self
7
+ def command_for(setup, context)
8
+ if context.empty?
9
+ GlobalCommand.new(setup)
10
+ else
11
+ Command.new(context.last, setup, context)
12
+ end
13
+ end
14
+ end
15
+
16
+ attr_reader :setup, :context
17
+
18
+ def initialize(setup, context)
19
+ @setup = setup
20
+ @context = context
21
+ end
22
+
23
+ def each(&block)
24
+ setup.canonical_command_names_for(context).each do |command_name|
25
+ command = Command.new(command_name, setup, context)
26
+ block.call(command)
27
+ end
28
+ end
29
+
30
+ def count
31
+ setup.canonical_command_names_for(context).size
32
+ end
33
+
34
+ alias_method :size, :count
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,29 @@
1
+ module Convoy
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 Convoy::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
@@ -0,0 +1,117 @@
1
+ module Convoy
2
+ module Formatter
3
+ class DefaultHelpFormatter
4
+ attr_reader :setup, :context
5
+
6
+ def initialize(setup, context)
7
+ @setup = setup
8
+ @context = context
9
+ end
10
+
11
+ def print(parser)
12
+ options = Options.new(parser, setup, context)
13
+ commands = Commands.new(setup, context)
14
+ current_command = Commands.command_for(setup, context)
15
+ system('clear')
16
+ puts
17
+ StreamOutputFormatter.new($stdout, :max_output_width => Terminal.width) do |f|
18
+ if setup.summary_for != '' && !setup.summary_for.nil?
19
+ f.puts setup.summary_for, :newlines => 2
20
+ end
21
+ if (setup.description_for != '' && !setup.description_for.nil?) && current_command.summary == setup.summary_for
22
+ f.indent(4) do |f_inner|
23
+ f_inner.puts setup.description_for, :newlines => 2
24
+ end
25
+ end
26
+ # name_help(current_command, f)
27
+ usage_help(current_command, f)
28
+ version_help(current_command, f)
29
+ commands_help(commands, f)
30
+ options_help(options, f)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def name_help(current_command, f)
37
+ f.puts "\x1B[38;5;84mNAME\x1B[0m"
38
+ f.indent(4) do |f|
39
+ f.grid(:columns => 3) do |t|
40
+ t.row current_command.script_name, '-', setup.summary_for(context)
41
+ end
42
+ f.newline
43
+ f.puts(setup.description_for(context), :newlines => 2) if setup.description_for(context)
44
+ end
45
+ end
46
+
47
+ def usage_help(current_command, f)
48
+ f.puts "\x1B[38;5;84mUSAGE\x1B[0m"
49
+ f.indent(4) do |f|
50
+ f.puts current_command.usage, :newlines => 2
51
+ end
52
+ end
53
+
54
+ def version_help(current_command, f)
55
+ if setup.version
56
+ f.puts "\x1B[38;5;84mVERSION\x1B[0m"
57
+ f.indent(4) do |f|
58
+ f.puts setup.version, :newlines => 2
59
+ end
60
+ end
61
+ end
62
+
63
+ def options_help(options, f)
64
+ if options.count > 0
65
+ f.puts "\x1B[38;5;84mFLAGS\x1B[0m"
66
+ f.indent(4) do |f|
67
+ f.grid(:columns => 3) do |t|
68
+ options.each do |option|
69
+ unless option.usage == '--verbosity, <s>' || option.usage == '--error-output-format, <s>' || option.usage == '-v, --version'
70
+ t.row option.usage.ljust(10, ' '), " \xe2\x86\x92 ", option.description
71
+ option_conflicts_help(option, t)
72
+ option_dependencies_help(option, t)
73
+ option_validations_help(option, t)
74
+ end
75
+ end
76
+ end
77
+ f.newline
78
+ end
79
+ end
80
+ end
81
+
82
+ def option_conflicts_help(option, t)
83
+ if option.has_conflicts?
84
+ t.row '', '', "- #{option.conflicts}"
85
+ end
86
+ end
87
+
88
+ def option_dependencies_help(option, t)
89
+ if option.has_dependencies?
90
+ t.row '', '', "- #{option.dependencies}"
91
+ end
92
+ end
93
+
94
+ def option_validations_help(option, t)
95
+ if option.has_validations?
96
+ option.validations.each do |validation|
97
+ t.row '', '', "- #{validation}"
98
+ end
99
+ end
100
+ end
101
+
102
+ def commands_help(commands, f)
103
+ if commands.count > 0
104
+ f.puts "\x1B[38;5;84mCOMMANDS\x1B[0m"
105
+ f.indent(4) do |f|
106
+ f.grid(:columns => 3) do |t|
107
+ commands.each do |command|
108
+ t.row command.name_with_aliases.ljust(10, ' '), " \xe2\x86\x92 ", command.outline
109
+ end
110
+ end
111
+ f.newline
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,17 @@
1
+ module Convoy
2
+ module Formatter
3
+ class GlobalCommand < Command
4
+ def initialize(setup)
5
+ super(:global, setup, [])
6
+ end
7
+
8
+ def summary
9
+ @summary ||= setup.summary_for(context) || ""
10
+ end
11
+
12
+ def description
13
+ @description ||= setup.description_for(context) || ""
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,152 @@
1
+ module Convoy
2
+ module Formatter
3
+ class Option
4
+ attr_reader :name, :details, :setup, :context
5
+
6
+ def initialize(name, details, setup, context)
7
+ @name = name.to_sym
8
+ @details = details
9
+ @setup = setup
10
+ @context = context
11
+ end
12
+
13
+ def usage
14
+ [short_string, long_string, type_string].select { |item| !item.empty? }.join(', ')
15
+ end
16
+
17
+ def description
18
+ [base_description_string, description_default_string].select { |item| !item.empty? }.join(" ")
19
+ end
20
+
21
+ def has_conflicts?
22
+ !conflicts_list.empty?
23
+ end
24
+
25
+ def conflicts
26
+ has_conflicts? ? "conflicts with: #{conflicts_list.map { |option| "--#{option}" }.join(', ')}" : ''
27
+ end
28
+
29
+ def has_dependencies?
30
+ !dependencies_list.empty?
31
+ end
32
+
33
+ def dependencies
34
+ has_dependencies? ? "depends on: #{format_dependency_list(dependencies_list).join(', ')}" : ''
35
+ end
36
+
37
+ def has_validations?
38
+ !validations_list.empty?
39
+ end
40
+
41
+ def validations
42
+ has_validations? ? validation_messages : []
43
+ end
44
+
45
+ private
46
+ def validation_messages
47
+ validations_list.map { |validation| validation[:desc] }
48
+ end
49
+
50
+ def validations_list
51
+ setup.validations_for(context)[name] || []
52
+ end
53
+
54
+ def format_dependency_list(dependency_list)
55
+ dependencies_list.map do |option|
56
+ case option
57
+ when Hash
58
+ option.inject([]) { |acc, (key, value)| acc << "--#{key}=#{value}" }.join(', ')
59
+ else
60
+ "--#{option}"
61
+ end
62
+ end
63
+ end
64
+
65
+ def dependencies_list
66
+ setup.dependencies_for(context)[name] || []
67
+ end
68
+
69
+ def conflicts_list
70
+ setup.conflicting_options_for(context)[name] || []
71
+ end
72
+
73
+ def base_description_string
74
+ details[:desc] || details[:description] || ''
75
+ end
76
+
77
+ def description_default_string
78
+ if details[:default]
79
+ base_description_string =~ /\.$/ ? "(Default: #{default_string})" : "(default: #{default_string})"
80
+ else
81
+ ""
82
+ end
83
+ end
84
+
85
+ def default_string
86
+ case details[:default]
87
+ when $stdout;
88
+ "<stdout>"
89
+ when $stdin;
90
+ "<stdin>"
91
+ when $stderr;
92
+ "<stderr>"
93
+ when Array
94
+ details[:default].join(", ")
95
+ else
96
+ details[:default].to_s
97
+ end
98
+ end
99
+
100
+ def short_string
101
+ details[:short] && details[:short] != :none ? "-#{details[:short]}" : ""
102
+ end
103
+
104
+ def long_string
105
+ if flag_with_default_true?
106
+ "#{base_long_string}, --no-#{details[:long]}"
107
+ else
108
+ base_long_string
109
+ end
110
+ end
111
+
112
+ def base_long_string
113
+ "--#{details[:long]}"
114
+ end
115
+
116
+ def type_string
117
+ case details[:type]
118
+ when :flag;
119
+ ""
120
+ when :int;
121
+ "<i>"
122
+ when :ints;
123
+ "<i+>"
124
+ when :string;
125
+ "<s>"
126
+ when :strings;
127
+ "<s+>"
128
+ when :float;
129
+ "<f>"
130
+ when :floats;
131
+ "<f+>"
132
+ when :io;
133
+ "<filename/uri>"
134
+ when :ios;
135
+ "<filename/uri+>"
136
+ when :date;
137
+ "<date>"
138
+ when :dates;
139
+ "<date+>"
140
+ end
141
+ end
142
+
143
+ def flag_with_default_true?
144
+ flag? && details[:default]
145
+ end
146
+
147
+ def flag?
148
+ details[:type] == :flag
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,28 @@
1
+ module Convoy
2
+ module Formatter
3
+ class Options
4
+ include Enumerable
5
+
6
+ attr_reader :parser, :setup, :context
7
+
8
+ def initialize(parser, setup, context)
9
+ @parser = parser
10
+ @setup = setup
11
+ @context = context
12
+ end
13
+
14
+ def each(&block)
15
+ parser.specs.each do |option_name, details|
16
+ option = Option.new(option_name, details, setup, context)
17
+ block.call(option)
18
+ end
19
+ end
20
+
21
+ def count
22
+ parser.specs.keys.size
23
+ end
24
+
25
+ alias_method :size, :count
26
+ end
27
+ end
28
+ end