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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.irbrc +3 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +705 -0
- data/Rakefile +1 -0
- data/convoy.gemspec +24 -0
- data/examples/.my_apprc +24 -0
- data/examples/basic +10 -0
- data/examples/basic_config_file +16 -0
- data/examples/basic_conflicts +17 -0
- data/examples/basic_depends_on +25 -0
- data/examples/basic_flags +15 -0
- data/examples/basic_options +14 -0
- data/examples/basic_options_multi +15 -0
- data/examples/basic_require_arguments +17 -0
- data/examples/basic_texts +21 -0
- data/examples/basic_validations +21 -0
- data/examples/basic_with_everything +30 -0
- data/examples/commands/example_command.rb +13 -0
- data/examples/suite_complex +65 -0
- data/examples/suite_simple +19 -0
- data/examples/suite_with_sub_commands +94 -0
- data/lib/convoy.rb +83 -0
- data/lib/convoy/action_command/base.rb +85 -0
- data/lib/convoy/action_command/escort_utility_command.rb +53 -0
- data/lib/convoy/app.rb +127 -0
- data/lib/convoy/arguments.rb +20 -0
- data/lib/convoy/auto_options.rb +71 -0
- data/lib/convoy/error/error.rb +33 -0
- data/lib/convoy/formatter/command.rb +87 -0
- data/lib/convoy/formatter/commands.rb +37 -0
- data/lib/convoy/formatter/cursor_position.rb +29 -0
- data/lib/convoy/formatter/default_help_formatter.rb +117 -0
- data/lib/convoy/formatter/global_command.rb +17 -0
- data/lib/convoy/formatter/option.rb +152 -0
- data/lib/convoy/formatter/options.rb +28 -0
- data/lib/convoy/formatter/shell_command_executor.rb +49 -0
- data/lib/convoy/formatter/stream_output_formatter.rb +88 -0
- data/lib/convoy/formatter/string_grid.rb +108 -0
- data/lib/convoy/formatter/string_splitter.rb +50 -0
- data/lib/convoy/formatter/terminal.rb +30 -0
- data/lib/convoy/global_pre_parser.rb +43 -0
- data/lib/convoy/logger.rb +75 -0
- data/lib/convoy/option_dependency_validator.rb +82 -0
- data/lib/convoy/option_parser.rb +155 -0
- data/lib/convoy/setup/configuration/generator.rb +75 -0
- data/lib/convoy/setup/configuration/instance.rb +34 -0
- data/lib/convoy/setup/configuration/loader.rb +43 -0
- data/lib/convoy/setup/configuration/locator/base.rb +19 -0
- data/lib/convoy/setup/configuration/locator/chaining.rb +29 -0
- data/lib/convoy/setup/configuration/locator/descending_to_home.rb +23 -0
- data/lib/convoy/setup/configuration/locator/executing_script_directory.rb +15 -0
- data/lib/convoy/setup/configuration/locator/specified_directory.rb +21 -0
- data/lib/convoy/setup/configuration/merge_tool.rb +38 -0
- data/lib/convoy/setup/configuration/reader.rb +36 -0
- data/lib/convoy/setup/configuration/writer.rb +46 -0
- data/lib/convoy/setup/dsl/action.rb +17 -0
- data/lib/convoy/setup/dsl/command.rb +67 -0
- data/lib/convoy/setup/dsl/config_file.rb +13 -0
- data/lib/convoy/setup/dsl/global.rb +29 -0
- data/lib/convoy/setup/dsl/options.rb +81 -0
- data/lib/convoy/setup_accessor.rb +206 -0
- data/lib/convoy/trollop.rb +861 -0
- data/lib/convoy/utils.rb +21 -0
- data/lib/convoy/validator.rb +45 -0
- data/spec/integration/basic_config_file_spec.rb +126 -0
- data/spec/integration/basic_conflicts_spec.rb +47 -0
- data/spec/integration/basic_depends_on_spec.rb +275 -0
- data/spec/integration/basic_options_spec.rb +41 -0
- data/spec/integration/basic_options_with_multi_spec.rb +30 -0
- data/spec/integration/basic_spec.rb +38 -0
- data/spec/integration/basic_validations_spec.rb +77 -0
- data/spec/integration/basic_with_arguments_spec.rb +35 -0
- data/spec/integration/basic_with_text_fields_spec.rb +21 -0
- data/spec/integration/suite_simple_spec.rb +45 -0
- data/spec/integration/suite_sub_command_spec.rb +51 -0
- data/spec/lib/convoy/action_command/base_spec.rb +200 -0
- data/spec/lib/convoy/formatter/command_spec.rb +238 -0
- data/spec/lib/convoy/formatter/global_command_spec.rb +50 -0
- data/spec/lib/convoy/formatter/option_spec.rb +300 -0
- data/spec/lib/convoy/formatter/shell_command_executor_spec.rb +59 -0
- data/spec/lib/convoy/formatter/stream_output_formatter_spec.rb +214 -0
- data/spec/lib/convoy/formatter/string_grid_spec.rb +59 -0
- data/spec/lib/convoy/formatter/string_splitter_spec.rb +50 -0
- data/spec/lib/convoy/formatter/terminal_spec.rb +19 -0
- data/spec/lib/convoy/setup/configuration/generator_spec.rb +101 -0
- data/spec/lib/convoy/setup/configuration/loader_spec.rb +79 -0
- data/spec/lib/convoy/setup/configuration/locator/chaining_spec.rb +81 -0
- data/spec/lib/convoy/setup/configuration/locator/descending_to_home_spec.rb +57 -0
- data/spec/lib/convoy/setup/configuration/locator/executing_script_directory_spec.rb +29 -0
- data/spec/lib/convoy/setup/configuration/locator/specified_directory_spec.rb +33 -0
- data/spec/lib/convoy/setup/configuration/merge_tool_spec.rb +41 -0
- data/spec/lib/convoy/setup/configuration/reader_spec.rb +41 -0
- data/spec/lib/convoy/setup/configuration/writer_spec.rb +75 -0
- data/spec/lib/convoy/setup_accessor_spec.rb +226 -0
- data/spec/lib/convoy/utils_spec.rb +30 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/integration_helpers.rb +2 -0
- data/spec/support/matchers/execute_action_for_command_matcher.rb +21 -0
- data/spec/support/matchers/execute_action_with_arguments_matcher.rb +25 -0
- data/spec/support/matchers/execute_action_with_options_matcher.rb +29 -0
- data/spec/support/matchers/exit_with_code_matcher.rb +29 -0
- data/spec/support/shared_contexts/integration_setup.rb +34 -0
- metadata +292 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Convoy
|
|
2
|
+
class OptionDependencyValidator
|
|
3
|
+
class << self
|
|
4
|
+
def for(parser)
|
|
5
|
+
self.new(parser)
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_reader :parser
|
|
10
|
+
|
|
11
|
+
def initialize(parser)
|
|
12
|
+
@parser = parser
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def validate(options, dependencies)
|
|
16
|
+
dependencies.each_pair do |option_name, dependency_rules|
|
|
17
|
+
ensure_dependencies_satisfied_for(option_name, dependency_rules, options)
|
|
18
|
+
end
|
|
19
|
+
options
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def option_exists?(option)
|
|
25
|
+
parser.specs.keys.include?(option)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def ensure_dependencies_satisfied_for(option_name, dependency_rules, options)
|
|
29
|
+
ensure_dependency_for_valid_option(option_name)
|
|
30
|
+
dependency_rules.each do |rule|
|
|
31
|
+
case rule
|
|
32
|
+
when Hash
|
|
33
|
+
handle_all_option_value_dependency_rules(option_name, rule, options)
|
|
34
|
+
else
|
|
35
|
+
ensure_option_depends_on_valid_option(option_name, rule)
|
|
36
|
+
handle_possible_presence_dependency_issue(option_name, rule, options)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def handle_all_option_value_dependency_rules(option_name, rule, options)
|
|
42
|
+
if option_was_specified?(option_name, options)
|
|
43
|
+
rule.each_pair do |rule_option, rule_option_value|
|
|
44
|
+
ensure_option_depends_on_valid_option(option_name, rule_option)
|
|
45
|
+
handle_possible_option_value_dependency_issue(option_name, rule_option, rule_option_value, options)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def handle_possible_option_value_dependency_issue(option_name, rule_option, rule_option_value, options)
|
|
51
|
+
unless options[rule_option] == rule_option_value
|
|
52
|
+
raise Convoy::UserError.new("Option dependency unsatisfied, '#{option_name}' depends on '#{rule_option}' having value '#{rule_option_value}', '#{option_name}' specified with value '#{options[option_name]}', but '#{rule_option}' is '#{options[rule_option]}'")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def handle_possible_presence_dependency_issue(option_name, rule, options)
|
|
57
|
+
if option_was_specified?(option_name, options) && option_was_unspecified?(rule, options)
|
|
58
|
+
raise Convoy::UserError.new("Option dependency unsatisfied, '#{option_name}' depends on '#{rule}', '#{option_name}' specified with value '#{options[option_name]}', but '#{rule}' is unspecified")
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def option_was_unspecified?(option_name, options)
|
|
63
|
+
!option_was_specified?(option_name, options)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def option_was_specified?(option_name, options)
|
|
67
|
+
!options[option_name].nil? && !(options[option_name] == []) && !(options[option_name] == false)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def ensure_dependency_for_valid_option(option_name)
|
|
71
|
+
unless option_exists?(option_name)
|
|
72
|
+
raise Convoy::ClientError.new("Dependency specified for option '#{option_name}', but no such option was defined, perhaps you misspelled it")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def ensure_option_depends_on_valid_option(option_name, rule)
|
|
77
|
+
unless option_exists?(rule)
|
|
78
|
+
raise Convoy::ClientError.new("'#{option_name}' is set up to depend on '#{rule}', but '#{rule}' does not appear to be a valid option, perhaps it is a spelling error")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
module Convoy
|
|
2
|
+
class OptionParser
|
|
3
|
+
attr_reader :setup, :configuration
|
|
4
|
+
|
|
5
|
+
def initialize(configuration, setup)
|
|
6
|
+
@configuration = configuration
|
|
7
|
+
@setup = setup
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def parse(cli_options)
|
|
11
|
+
options = init_invoked_options_hash
|
|
12
|
+
parse_global_options(cli_options, options[:global][:options])
|
|
13
|
+
parse_command_options(cli_options, [], options[:global][:commands])
|
|
14
|
+
|
|
15
|
+
[options, arguments_from(cli_options)]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def init_invoked_options_hash
|
|
21
|
+
{
|
|
22
|
+
:global => {
|
|
23
|
+
:options => {},
|
|
24
|
+
:commands => {}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def parse_global_options(cli_options, options)
|
|
30
|
+
context = []
|
|
31
|
+
options.merge!(parse_options(cli_options, context))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def parse_command_options(cli_options, context, options)
|
|
35
|
+
unless cli_option_is_a_command?(cli_options, context)
|
|
36
|
+
options
|
|
37
|
+
else
|
|
38
|
+
command = command_name_from(cli_options)
|
|
39
|
+
context << command
|
|
40
|
+
current_options = parse_options(cli_options, context)
|
|
41
|
+
options[command] = {}
|
|
42
|
+
options[command][:options] = current_options
|
|
43
|
+
options[command][:commands] = {}
|
|
44
|
+
parse_command_options(cli_options, context, options[command][:commands])
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def parse_options(cli_options, context = [])
|
|
49
|
+
stop_words = setup.command_names_for(context).map(&:to_s)
|
|
50
|
+
parser = init_parser(stop_words)
|
|
51
|
+
parser = add_setup_options_to(parser, context)
|
|
52
|
+
parser = add_option_conflicts_to(parser, context)
|
|
53
|
+
parser = default_option_values_from_config_for(parser, context)
|
|
54
|
+
parser.version(setup.version) #set the version if it was provided
|
|
55
|
+
parser.help_formatter(Convoy::Formatter::DefaultHelpFormatter.new(setup, context))
|
|
56
|
+
parsed_options = parse_options_string(parser, cli_options)
|
|
57
|
+
Convoy::OptionDependencyValidator.for(parser).validate(parsed_options, setup.dependencies_for(context))
|
|
58
|
+
Convoy::Validator.for(parser).validate(parsed_options, setup.validations_for(context))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def add_setup_options_to(parser, context = [])
|
|
62
|
+
setup.options_for(context).each do |name, opts|
|
|
63
|
+
parser.opt name, opts[:desc] || "", opts.dup #have to make sure to dup here, otherwise opts might get stuff added to it and it
|
|
64
|
+
#may cause problems later, e.g. adds default value and when parsed again trollop barfs
|
|
65
|
+
end
|
|
66
|
+
parser
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def add_option_conflicts_to(parser, context = [])
|
|
70
|
+
conflicting_options_for_context = setup.conflicting_options_for(context)
|
|
71
|
+
conflicting_options_for_context.keys.each do |option_name|
|
|
72
|
+
conflict_list = [option_name] + conflicting_options_for_context[option_name]
|
|
73
|
+
conflict_list.each do |option|
|
|
74
|
+
raise Convoy::ClientError.new("Conflict defined with option '#{option}', but this option does not exist, perhaps it is a spelling error.") unless option_exists?(parser, option)
|
|
75
|
+
end
|
|
76
|
+
parser.conflicts *conflict_list
|
|
77
|
+
end
|
|
78
|
+
parser
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def option_exists?(parser, option)
|
|
82
|
+
parser.specs.keys.include?(option)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def default_option_values_from_config_for(parser, context)
|
|
86
|
+
unless configuration.empty?
|
|
87
|
+
parser.specs.each do |sym, opts|
|
|
88
|
+
if config_has_value_for_context?(sym, context)
|
|
89
|
+
default = config_value_for_context(sym, context)
|
|
90
|
+
opts[:default] = default
|
|
91
|
+
if opts[:multi] && default.nil?
|
|
92
|
+
opts[:default] = [] # multi arguments default to [], not nil
|
|
93
|
+
elsif opts[:multi] && !default.kind_of?(Array)
|
|
94
|
+
opts[:default] = [default]
|
|
95
|
+
else
|
|
96
|
+
opts[:default] = default
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
parser
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def config_has_value_for_context?(option, context)
|
|
105
|
+
relevant_config_hash = config_hash_for_context(context)
|
|
106
|
+
relevant_config_hash[:options].include?(option)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def config_value_for_context(option, context)
|
|
110
|
+
relevant_config_hash = config_hash_for_context(context)
|
|
111
|
+
relevant_config_hash[:options][option]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def config_hash_for_context(context)
|
|
115
|
+
relevant_config_hash = configuration.global
|
|
116
|
+
context.each do |command_name|
|
|
117
|
+
command_name = command_name.to_sym
|
|
118
|
+
relevant_config_hash = relevant_config_hash[:commands][command_name]
|
|
119
|
+
relevant_config_hash = ensure_config_hash_has_options_and_commands(relevant_config_hash)
|
|
120
|
+
end
|
|
121
|
+
relevant_config_hash
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def ensure_config_hash_has_options_and_commands(relevant_config_hash)
|
|
125
|
+
relevant_config_hash ||= {}
|
|
126
|
+
relevant_config_hash[:commands] ||= {}
|
|
127
|
+
relevant_config_hash[:options] ||= {}
|
|
128
|
+
relevant_config_hash
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def cli_option_is_a_command?(cli_options, context)
|
|
132
|
+
cli_options.size > 0 && setup.command_names_for(context).include?(cli_options[0].to_sym)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def init_parser(stop_words)
|
|
136
|
+
Trollop::Parser.new.tap do |parser|
|
|
137
|
+
parser.stop_on(stop_words) # make sure we halt parsing if we see a command
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def parse_options_string(parser, cli_options)
|
|
142
|
+
Trollop::with_standard_exception_handling(parser) do
|
|
143
|
+
parser.parse(cli_options)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def command_name_from(cli_options)
|
|
148
|
+
cli_options.shift.to_sym
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def arguments_from(cli_options)
|
|
152
|
+
cli_options
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Convoy
|
|
2
|
+
module Setup
|
|
3
|
+
module Configuration
|
|
4
|
+
class Generator
|
|
5
|
+
attr_reader :setup
|
|
6
|
+
|
|
7
|
+
def initialize(setup)
|
|
8
|
+
@setup = setup
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def default_data
|
|
12
|
+
config_hash = init_config_hash
|
|
13
|
+
options([], config_hash[:global][:options]) #global options
|
|
14
|
+
options_for_commands(setup.canonical_command_names_for([]), [], config_hash[:global][:commands])
|
|
15
|
+
config_hash
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def options_for_commands(commands, context, options = {})
|
|
21
|
+
commands.each do |command_name|
|
|
22
|
+
command_name = command_name.to_sym
|
|
23
|
+
#next if command_name == :convoy
|
|
24
|
+
options[command_name] = {}
|
|
25
|
+
options[command_name][:options] = {}
|
|
26
|
+
options[command_name][:commands] = {}
|
|
27
|
+
current_context = context.dup
|
|
28
|
+
current_context << command_name
|
|
29
|
+
options(current_context, options[command_name][:options]) #command_options
|
|
30
|
+
options_for_commands(setup.canonical_command_names_for(current_context), current_context, options[command_name][:commands])
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def init_config_hash
|
|
35
|
+
{
|
|
36
|
+
:global => {
|
|
37
|
+
:options => {},
|
|
38
|
+
:commands => {}
|
|
39
|
+
},
|
|
40
|
+
:user => {}
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def options(context = [], options = {})
|
|
45
|
+
command_names = setup.command_names_for(context)
|
|
46
|
+
parser = init_parser(command_names)
|
|
47
|
+
parser = add_setup_options_to(parser, context)
|
|
48
|
+
options.merge!(default_option_values(parser))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def init_parser(stop_words)
|
|
52
|
+
Trollop::Parser.new.tap do |parser|
|
|
53
|
+
parser.stop_on(stop_words) # make sure we halt parsing if we see a command
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def add_setup_options_to(parser, context = [])
|
|
58
|
+
setup.options_for(context).each do |name, opts|
|
|
59
|
+
parser.opt name, opts[:desc] || "", opts.dup #have to make sure to dup here, otherwise opts might get stuff added to it and it
|
|
60
|
+
#may cause problems later, e.g. adds default value and when parsed again trollop barfs
|
|
61
|
+
end
|
|
62
|
+
parser
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def default_option_values(parser)
|
|
66
|
+
hash = {}
|
|
67
|
+
parser.specs.each_pair do |key, data|
|
|
68
|
+
hash[key] = data[:default] || nil
|
|
69
|
+
end
|
|
70
|
+
hash
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Convoy
|
|
2
|
+
module Setup
|
|
3
|
+
module Configuration
|
|
4
|
+
class Instance
|
|
5
|
+
class << self
|
|
6
|
+
def blank
|
|
7
|
+
self.new(nil, {})
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_reader :data, :path
|
|
12
|
+
|
|
13
|
+
def initialize(path, hash)
|
|
14
|
+
@data = hash
|
|
15
|
+
@path = path
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def blank?
|
|
19
|
+
data.empty?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
alias empty? blank?
|
|
23
|
+
|
|
24
|
+
def global
|
|
25
|
+
data[:global] || {}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def user
|
|
29
|
+
data[:user] || {}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Convoy
|
|
2
|
+
module Setup
|
|
3
|
+
module Configuration
|
|
4
|
+
class Loader
|
|
5
|
+
attr_reader :setup, :auto_options
|
|
6
|
+
|
|
7
|
+
def initialize(setup, auto_options)
|
|
8
|
+
@setup = setup
|
|
9
|
+
@auto_options = auto_options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def configuration
|
|
13
|
+
if setup.has_config_file?
|
|
14
|
+
Writer.new(config_path, Generator.new(setup).default_data).write if setup.config_file_autocreatable?
|
|
15
|
+
Reader.new(config_path).read
|
|
16
|
+
else
|
|
17
|
+
Instance.blank
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def default_config_path
|
|
22
|
+
@default_config_path ||= (config_filename ? File.join(File.expand_path(ENV["HOME"]), config_filename) : nil)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def config_filename
|
|
28
|
+
@config_filename ||= setup.config_file
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def config_path
|
|
32
|
+
@config_path ||= (auto_options.non_default_config_path || locator.locate || default_config_path)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def locator
|
|
36
|
+
Locator::Chaining.new(config_filename).
|
|
37
|
+
add_locator(Locator::ExecutingScriptDirectory.new(config_filename)).
|
|
38
|
+
add_locator(Locator::DescendingToHome.new(config_filename))
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Convoy
|
|
2
|
+
module Setup
|
|
3
|
+
module Configuration
|
|
4
|
+
module Locator
|
|
5
|
+
class Base
|
|
6
|
+
attr_reader :filename
|
|
7
|
+
|
|
8
|
+
def initialize(filename)
|
|
9
|
+
@filename = filename
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def locate
|
|
13
|
+
raise "Must be defined in child class"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Convoy
|
|
2
|
+
module Setup
|
|
3
|
+
module Configuration
|
|
4
|
+
module Locator
|
|
5
|
+
class Chaining < Base
|
|
6
|
+
attr_reader :locators
|
|
7
|
+
|
|
8
|
+
def initialize(filename, locators = [])
|
|
9
|
+
super(filename)
|
|
10
|
+
@locators = locators || []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def locate
|
|
14
|
+
locators.each do |locator|
|
|
15
|
+
filepath = locator.locate
|
|
16
|
+
return filepath if filepath
|
|
17
|
+
end
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_locator(locator)
|
|
22
|
+
@locators << locator
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
3
|
+
module Convoy
|
|
4
|
+
module Setup
|
|
5
|
+
module Configuration
|
|
6
|
+
module Locator
|
|
7
|
+
class DescendingToHome < Base
|
|
8
|
+
def locate
|
|
9
|
+
return nil unless filename
|
|
10
|
+
possible_configs = []
|
|
11
|
+
Pathname.new(Dir.pwd).descend do |path|
|
|
12
|
+
filepath = File.join(path, filename)
|
|
13
|
+
if File.exists?(filepath)
|
|
14
|
+
possible_configs << filepath
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
possible_configs.empty? ? nil : possible_configs.last
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|