convoy 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|