clin 0.2.0 → 0.3.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 +4 -4
- data/.travis.yml +0 -1
- data/CHANGELOG.md +7 -0
- data/README.md +1 -0
- data/clin.gemspec +1 -1
- data/examples/auto_option.rb +17 -0
- data/examples/dispatcher.rb +17 -23
- data/examples/list_option.rb +3 -8
- data/examples/nested_dispatcher.rb +8 -53
- data/examples/simple.rb +3 -22
- data/examples/test.rb +9 -9
- data/lib/clin/argument.rb +3 -3
- data/lib/clin/command.rb +11 -126
- data/lib/clin/command_mixin/core.rb +152 -0
- data/lib/clin/command_mixin/dispatcher.rb +34 -0
- data/lib/clin/command_mixin/options.rb +134 -0
- data/lib/clin/command_mixin.rb +9 -0
- data/lib/clin/general_option.rb +3 -1
- data/lib/clin/option.rb +37 -3
- data/lib/clin/option_list.rb +1 -1
- data/lib/clin/shell.rb +111 -0
- data/lib/clin/shell_interaction/choose.rb +59 -0
- data/lib/clin/shell_interaction/file_conflict.rb +53 -0
- data/lib/clin/shell_interaction/yes_or_no.rb +16 -0
- data/lib/clin/shell_interaction.rb +34 -0
- data/lib/clin/version.rb +1 -1
- data/lib/clin.rb +10 -1
- data/spec/clin/command_dispacher_spec.rb +3 -3
- data/spec/clin/command_mixin/core_spec.rb +37 -0
- data/spec/clin/{command_options_mixin_spec.rb → command_mixin/options_spec.rb} +11 -8
- data/spec/clin/command_parser_spec.rb +2 -2
- data/spec/clin/command_spec.rb +3 -3
- data/spec/clin/option_spec.rb +40 -0
- data/spec/clin/shell_interaction/choose_spec.rb +54 -0
- data/spec/clin/shell_spec.rb +147 -0
- data/spec/examples/auto_option_spec.rb +11 -0
- metadata +24 -7
- data/lib/clin/command_options_mixin.rb +0 -118
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'clin'
|
2
|
+
require 'clin/option'
|
3
|
+
|
4
|
+
# Contains class methods to add option to a command or a General option
|
5
|
+
module Clin::CommandMixin::Options
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
self.options = []
|
10
|
+
self.general_options = {}
|
11
|
+
# Trigger when a class inherit this class
|
12
|
+
# It will clone attributes that need inheritance
|
13
|
+
# @param subclass [Clin::Command]
|
14
|
+
def self.inherited(subclass)
|
15
|
+
subclass.options = @options.clone
|
16
|
+
subclass.general_options = @general_options.clone
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods # :nodoc:
|
22
|
+
attr_accessor :options
|
23
|
+
attr_accessor :general_options
|
24
|
+
|
25
|
+
# Add an option
|
26
|
+
# @param args list of arguments.
|
27
|
+
# * First argument must be the name if no block is given.
|
28
|
+
# It will set automatically read the value into the hash with +name+ as key
|
29
|
+
# * The remaining arguments are OptionsParser#on arguments
|
30
|
+
# ```
|
31
|
+
# option :require, '-r', '--require [LIBRARY]', 'Require the library'
|
32
|
+
# option '-h', '--helper', 'Show the help' do
|
33
|
+
# puts opts
|
34
|
+
# exit
|
35
|
+
# end
|
36
|
+
# ```
|
37
|
+
def opt_option(*args, &block)
|
38
|
+
add_option Clin::Option.new(*args, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Add an option.
|
42
|
+
# Helper method that just create a new Clin::Option with the argument then call add_option
|
43
|
+
# ```
|
44
|
+
# option :show, 'Show some message'
|
45
|
+
# # => -s --show SHOW Show some message
|
46
|
+
# option :require, 'Require a library', short: false, optional: true, argument: 'LIBRARY'
|
47
|
+
# # => --require [LIBRARY] Require a library
|
48
|
+
# option :help, 'Show the help', argument: false do
|
49
|
+
# puts opts
|
50
|
+
# exit
|
51
|
+
# end
|
52
|
+
# # => -h --help Show the help
|
53
|
+
# ```
|
54
|
+
def option(name, description, **config, &block)
|
55
|
+
add_option Clin::Option.new(name, description, **config, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
# For an option that does not have an argument
|
59
|
+
# Same as .option except it will default argument to false
|
60
|
+
# ```
|
61
|
+
# option :verbose, 'Use verbose' #=> -v --verbose will be added to the option of this command
|
62
|
+
# ```
|
63
|
+
def flag_option(name, description, **config, &block)
|
64
|
+
add_option Clin::Option.new(name, description, **config.merge(argument: false), &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Add a list option.
|
68
|
+
# @see Clin::OptionList#initialize
|
69
|
+
def list_option(name, description, **config)
|
70
|
+
add_option Clin::OptionList.new(name, description, **config)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Add a list options that don't take arguments
|
74
|
+
# Same as .list_option but set +argument+ to false
|
75
|
+
# @see Clin::OptionList#initialize
|
76
|
+
def list_flag_option(name, description, **config)
|
77
|
+
add_option Clin::OptionList.new(name, description, **config.merge(argument: false))
|
78
|
+
end
|
79
|
+
|
80
|
+
def auto_option(name, usage, &block)
|
81
|
+
add_option Clin::Option.parse(name, usage, &block)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Add a new option.
|
85
|
+
# @param option [Clin::Option] option to add.
|
86
|
+
def add_option(option)
|
87
|
+
# Need to use += instead of << otherwise the parent class will also be changed
|
88
|
+
@options << option
|
89
|
+
end
|
90
|
+
|
91
|
+
# Add a general option
|
92
|
+
# @param option_cls [Class<GeneralOption>] Class inherited from GeneralOption
|
93
|
+
# @param config [Hash] General option config. Check the general option config.
|
94
|
+
def general_option(option_cls, config = {})
|
95
|
+
option_cls = option_cls.constantize if option_cls.is_a? String
|
96
|
+
@general_options[option_cls] = option_cls.new(config)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Remove a general option
|
100
|
+
# Might be useful if a parent added the option but is not needed in this child.
|
101
|
+
def remove_general_option(option_cls)
|
102
|
+
option_cls = option_cls.constantize if option_cls.is_a? String
|
103
|
+
@general_options.delete(option_cls)
|
104
|
+
end
|
105
|
+
|
106
|
+
# To be called inside OptionParser block
|
107
|
+
# Extract the option in the command line using the OptionParser and map it to the out map.
|
108
|
+
# @param opts [OptionParser]
|
109
|
+
# @param out [Hash] Where the options shall be extracted
|
110
|
+
def register_options(opts, out)
|
111
|
+
@options.each do |option|
|
112
|
+
option.register(opts, out)
|
113
|
+
end
|
114
|
+
|
115
|
+
@general_options.each do |_cls, option|
|
116
|
+
option.class.register_options(opts, out)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Call #execute on each of the general options.
|
121
|
+
# This is called during the command initialization
|
122
|
+
# e.g. A verbose general option execute would be:
|
123
|
+
# ```
|
124
|
+
# def execute(params)
|
125
|
+
# MyApp.verbose = true if params[:verbose]
|
126
|
+
# end
|
127
|
+
# ```
|
128
|
+
def execute_general_options(options)
|
129
|
+
general_options.each do |_cls, gopts|
|
130
|
+
gopts.execute(options)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
data/lib/clin/general_option.rb
CHANGED
data/lib/clin/option.rb
CHANGED
@@ -3,8 +3,31 @@ require 'clin'
|
|
3
3
|
# Option container.
|
4
4
|
# Prefer the `.option`, `.flag_option`,... class methods than `.add_option Option.new(...)`
|
5
5
|
class Clin::Option
|
6
|
+
def self.parse(name, usage, &block)
|
7
|
+
long = nil
|
8
|
+
short = nil
|
9
|
+
argument = nil
|
10
|
+
desc = []
|
11
|
+
usage.split.each do |segment|
|
12
|
+
if segment.start_with? '--'
|
13
|
+
long, argument = segment.split('=', 2)
|
14
|
+
elsif segment.start_with? '-'
|
15
|
+
short = segment
|
16
|
+
else
|
17
|
+
desc << segment
|
18
|
+
end
|
19
|
+
end
|
20
|
+
argument = false if argument.nil?
|
21
|
+
new(name, desc.join(' '), short: short, long: long, argument: argument, &block)
|
22
|
+
end
|
23
|
+
|
6
24
|
attr_accessor :name, :description, :optional_argument, :block, :type, :default
|
7
|
-
|
25
|
+
|
26
|
+
# Set the short name(e.g. -v for verbose)
|
27
|
+
attr_writer :short
|
28
|
+
|
29
|
+
# Set the long name(e.g. --verbose for verbose)
|
30
|
+
attr_writer :long
|
8
31
|
|
9
32
|
# Create a new option.
|
10
33
|
# @param name [String] Option name.
|
@@ -24,7 +47,7 @@ class Clin::Option
|
|
24
47
|
@short = short
|
25
48
|
@long = long
|
26
49
|
@optional_argument = argument_optional
|
27
|
-
|
50
|
+
self.argument = argument
|
28
51
|
@type = type
|
29
52
|
@block = block
|
30
53
|
@default = default
|
@@ -101,6 +124,18 @@ class Clin::Option
|
|
101
124
|
@argument ||= default_argument
|
102
125
|
end
|
103
126
|
|
127
|
+
# Set the argument
|
128
|
+
# @param value
|
129
|
+
def argument=(value)
|
130
|
+
if value
|
131
|
+
arg = Clin::Argument.new(value)
|
132
|
+
@optional_argument = true if arg.optional
|
133
|
+
@argument = arg.name
|
134
|
+
else # If false or nil
|
135
|
+
@argument = value
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
104
139
|
def option_parser_arguments
|
105
140
|
args = [short, long_argument, @type, description]
|
106
141
|
args.compact
|
@@ -153,4 +188,3 @@ class Clin::Option
|
|
153
188
|
out
|
154
189
|
end
|
155
190
|
end
|
156
|
-
|
data/lib/clin/option_list.rb
CHANGED
data/lib/clin/shell.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'clin'
|
2
|
+
|
3
|
+
# Class the offer helper method to interact with the user using the command line
|
4
|
+
class Clin::Shell
|
5
|
+
# Input stream, default: STDIN
|
6
|
+
attr_accessor :in
|
7
|
+
|
8
|
+
# Output stream, default: STDOUT
|
9
|
+
attr_accessor :out
|
10
|
+
|
11
|
+
def initialize(input: STDIN, output: STDOUT)
|
12
|
+
@in = input
|
13
|
+
@out = output
|
14
|
+
@yes_or_no_persist = false
|
15
|
+
@override_persist = false
|
16
|
+
end
|
17
|
+
|
18
|
+
# Ask a question
|
19
|
+
# @param statement [String]
|
20
|
+
# @param default [String]
|
21
|
+
# @param autocomplete [Array|Proc] Filter for autocomplete
|
22
|
+
def ask(statement, default: nil, autocomplete: nil)
|
23
|
+
answer = scan(statement, autocomplete: autocomplete)
|
24
|
+
if answer.blank?
|
25
|
+
default
|
26
|
+
else
|
27
|
+
answer.strip
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Ask a question and expect the result to be in the list of choices
|
32
|
+
# Will continue asking until the input is correct
|
33
|
+
# or if a default value is supplied then empty will return.
|
34
|
+
# @param statement [String] Question to ask
|
35
|
+
# @param choices [Array] List of choices
|
36
|
+
# @param default [String] Default value if the user put blank value.
|
37
|
+
# @param allow_initials [Boolean] Allow the user to reply with only the initial of the choice.
|
38
|
+
# (e.g. yes/no => y/n)
|
39
|
+
# If multiple choices start with the same initial
|
40
|
+
# ONLY the first one will be able to be selected using its initial
|
41
|
+
def choose(statement, choices, default: nil, allow_initials: false)
|
42
|
+
Clin::ShellInteraction::Choose.new(self).run(statement, choices,
|
43
|
+
default: default, allow_initials: allow_initials)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Expect the user the return yes or no(y/n also works)
|
47
|
+
# @param statement [String] Question to ask
|
48
|
+
# @param default [String] Default value(yes/no)
|
49
|
+
# @param persist [Boolean] Add "always" to the choices. When all is selected all the following
|
50
|
+
# call to yes_or_no with persist: true will return true instead of asking the user.
|
51
|
+
def yes_or_no(statement, default: nil, persist: false)
|
52
|
+
Clin::ShellInteraction::YesOrNo.new(self).run(statement, default: default, persist: persist)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Yes or no question defaulted to yes
|
56
|
+
# @param options [Hash] Named parameters for yes_or_no
|
57
|
+
# @see #yes_or_no
|
58
|
+
def yes?(statement, options = {})
|
59
|
+
options[:default] = :yes
|
60
|
+
yes_or_no(statement, **options)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Yes or no question defaulted to no
|
64
|
+
# @param options [Hash] Named parameters for yes_or_no
|
65
|
+
# @see #yes_or_no
|
66
|
+
def no?(statement, options = {})
|
67
|
+
options[:default] = :no
|
68
|
+
yes_or_no(statement, **options)
|
69
|
+
end
|
70
|
+
|
71
|
+
# File conflict helper method.
|
72
|
+
# Give the following options to the user
|
73
|
+
# - yes, Yes for this one
|
74
|
+
# - no, No for this one
|
75
|
+
# - all, Yes for all one
|
76
|
+
# - quit, Quit the program
|
77
|
+
# - diff, Diff the 2 files
|
78
|
+
# @param filename [String] Filename with the conflict
|
79
|
+
# @param block [Block] optional block that give the new content in case of diff
|
80
|
+
# @return [Boolean] If the file should be overwritten.
|
81
|
+
def file_conflict(filename, default: nil, &block)
|
82
|
+
Clin::ShellInteraction::FileConflict.new(self).run(filename, default: default, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
# File conflict question defaulted to yes
|
86
|
+
def overwrite?(filename, &block)
|
87
|
+
file_conflict(filename, default: :yes, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
# File conflict question defaulted to no
|
91
|
+
def keep?(filename, &block)
|
92
|
+
file_conflict(filename, default: :no, &block)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Prompt the statement to the user and return his reply.
|
96
|
+
# @param statement [String]
|
97
|
+
# @param autocomplete [Array|Block]
|
98
|
+
protected def scan(statement, autocomplete: nil)
|
99
|
+
unless autocomplete.nil?
|
100
|
+
Readline.completion_proc = if autocomplete.is_a? Proc
|
101
|
+
autocomplete
|
102
|
+
else
|
103
|
+
proc { |s| autocomplete.grep(/^#{Regexp.escape(s)}/) }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
Readline.completion_append_character = nil
|
107
|
+
Readline.readline(statement + ' ', true)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
require 'clin/shell_interaction'
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'clin'
|
2
|
+
|
3
|
+
# Handle a choose question
|
4
|
+
class Clin::ShellInteraction::Choose < Clin::ShellInteraction
|
5
|
+
def run(statement, choices, default: nil, allow_initials: false)
|
6
|
+
choices = convert_choices(choices)
|
7
|
+
question = prepare_question(statement, choices, default: default, initials: allow_initials)
|
8
|
+
loop do
|
9
|
+
answer = @shell.ask(question, default: default, autocomplete: choices.keys)
|
10
|
+
unless answer.nil?
|
11
|
+
choices.each do |choice, _|
|
12
|
+
if choice.casecmp(answer) == 0 || (allow_initials && choice[0].casecmp(answer[0]) == 0)
|
13
|
+
return choice
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
print_choices_help(choices, allow_initials: allow_initials)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
protected def choice_message(choices, default: nil, initials: false)
|
22
|
+
choices = choices.keys.map { |x| x == default ? x.to_s.upcase : x }
|
23
|
+
msg = if initials
|
24
|
+
choices.map { |x| x[0] }.join('')
|
25
|
+
else
|
26
|
+
choices.join(',')
|
27
|
+
end
|
28
|
+
"[#{msg}]"
|
29
|
+
end
|
30
|
+
|
31
|
+
protected def prepare_question(statement, choices, default: nil, initials: false)
|
32
|
+
"#{statement} #{choice_message(choices, default: default, initials: initials)}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Convert the choices to a hash with key being the choice and value the description
|
36
|
+
protected def convert_choices(choices)
|
37
|
+
if choices.is_a? Array
|
38
|
+
Hash[*choices.map { |k| [k, ''] }.flatten]
|
39
|
+
elsif choices.is_a? Hash
|
40
|
+
choices
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
protected def print_choices_help(choices, allow_initials: false)
|
45
|
+
puts 'Choose from:'
|
46
|
+
used_initials = Set.new
|
47
|
+
choices.each do |choice, description|
|
48
|
+
suf = choice.to_s
|
49
|
+
suf += ", #{description}" unless description.blank?
|
50
|
+
line = if allow_initials && !used_initials.include?(choice[0])
|
51
|
+
used_initials << choice[0]
|
52
|
+
" #{choice[0]} - #{suf}"
|
53
|
+
else
|
54
|
+
" #{suf}"
|
55
|
+
end
|
56
|
+
puts line
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'clin'
|
2
|
+
|
3
|
+
# Handle the file_conflict interaction with the user.
|
4
|
+
class Clin::ShellInteraction::FileConflict < Clin::ShellInteraction
|
5
|
+
def run(filename, default: nil, &block)
|
6
|
+
choices = file_conflict_choices
|
7
|
+
choices = choices.except(:diff) unless block_given?
|
8
|
+
return true if persist?
|
9
|
+
result = nil
|
10
|
+
while result.nil?
|
11
|
+
choice = @shell.choose("Overwrite '#{filename}'?", choices,
|
12
|
+
default: default, allow_initials: true)
|
13
|
+
result = handle_choice(choice, filename, &block)
|
14
|
+
end
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
protected def handle_choice(choice, filename, &block)
|
19
|
+
case choice
|
20
|
+
when :yes
|
21
|
+
return true
|
22
|
+
when :no
|
23
|
+
return false
|
24
|
+
when :always
|
25
|
+
return persist!
|
26
|
+
when :quit
|
27
|
+
puts 'Aborting...'
|
28
|
+
fail SystemExit
|
29
|
+
when :diff
|
30
|
+
show_diff(filename, block.call)
|
31
|
+
return nil
|
32
|
+
else
|
33
|
+
return nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
protected def file_conflict_choices
|
38
|
+
{yes: 'Overwrite',
|
39
|
+
no: 'Do not Overwrite',
|
40
|
+
always: 'Override this and all the next',
|
41
|
+
quit: 'Abort!',
|
42
|
+
diff: 'Show the difference',
|
43
|
+
help: 'Show this'}
|
44
|
+
end
|
45
|
+
|
46
|
+
protected def show_diff(old_file, new_content)
|
47
|
+
Tempfile.open(File.basename(old_file)) do |f|
|
48
|
+
f.write new_content
|
49
|
+
f.rewind
|
50
|
+
system %(#{Clin.diff_cmd} "#{old_file}" "#{f.path}")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'clin'
|
2
|
+
|
3
|
+
# Handle a simple yes/no interaction
|
4
|
+
class Clin::ShellInteraction::YesOrNo < Clin::ShellInteraction
|
5
|
+
def run(statement, default: nil, persist: false)
|
6
|
+
default = default.to_sym unless default.nil?
|
7
|
+
options = [:yes, :no]
|
8
|
+
if persist
|
9
|
+
return true if persist?
|
10
|
+
options << :always
|
11
|
+
end
|
12
|
+
choice = @shell.choose(statement, options, default: default, allow_initials: true)
|
13
|
+
return persist! if choice == :always
|
14
|
+
choice == :yes
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'clin'
|
2
|
+
|
3
|
+
# Parent class for shell interaction.
|
4
|
+
class Clin::ShellInteraction
|
5
|
+
class << self
|
6
|
+
attr_accessor :persist
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :shell
|
10
|
+
|
11
|
+
# @param shell [Clin::Shell] Shell starting the interaction.
|
12
|
+
def initialize(shell)
|
13
|
+
@shell = shell
|
14
|
+
self.class.persist ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Boolean]
|
18
|
+
def persist?
|
19
|
+
self.class.persist[@shell] ||= false
|
20
|
+
end
|
21
|
+
|
22
|
+
# Mark the current shell to persist file interaction
|
23
|
+
def persist!
|
24
|
+
self.class.persist[@shell] = persist_answer
|
25
|
+
end
|
26
|
+
|
27
|
+
def persist_answer
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
require 'clin/shell_interaction/file_conflict'
|
33
|
+
require 'clin/shell_interaction/yes_or_no'
|
34
|
+
require 'clin/shell_interaction/choose'
|
data/lib/clin/version.rb
CHANGED
data/lib/clin.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'active_support'
|
2
2
|
require 'active_support/core_ext'
|
3
3
|
require 'optparse'
|
4
|
+
require 'readline'
|
4
5
|
require 'clin/version'
|
5
6
|
|
6
7
|
# Clin Global module. All classes and clin modules should be inside this module
|
@@ -9,6 +10,9 @@ module Clin
|
|
9
10
|
# Set the global exe name. `Clin.exe_name = 'git'`
|
10
11
|
attr_writer :exe_name
|
11
12
|
|
13
|
+
# Set the command when comparing 2 files(Used in the shell)
|
14
|
+
attr_writer :diff_cmd
|
15
|
+
|
12
16
|
def default_exe_name
|
13
17
|
'command'
|
14
18
|
end
|
@@ -18,15 +22,20 @@ module Clin
|
|
18
22
|
def exe_name
|
19
23
|
@exe_name ||= Clin.default_exe_name
|
20
24
|
end
|
25
|
+
|
26
|
+
def diff_cmd
|
27
|
+
@diff_cmd ||= 'diff -u'
|
28
|
+
end
|
21
29
|
end
|
22
30
|
end
|
23
31
|
|
32
|
+
require 'clin/command_mixin'
|
24
33
|
require 'clin/command'
|
25
34
|
require 'clin/command_parser'
|
26
|
-
require 'clin/command_options_mixin'
|
27
35
|
require 'clin/general_option'
|
28
36
|
require 'clin/command_dispatcher'
|
29
37
|
require 'clin/common/help_options'
|
30
38
|
require 'clin/errors'
|
31
39
|
require 'clin/option'
|
32
40
|
require 'clin/option_list'
|
41
|
+
require 'clin/shell'
|
@@ -43,8 +43,8 @@ RSpec.describe Clin::CommandDispatcher do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
describe '#parse' do
|
46
|
-
let(:cmd1) { double(:
|
47
|
-
let(:cmd2) { double(:
|
46
|
+
let(:cmd1) { double(:command_mixin, parse: 'cmd1', usage: 'cmd1 use') }
|
47
|
+
let(:cmd2) { double(:command_mixin, parse: 'cmd2', usage: 'cmd1 use') }
|
48
48
|
let(:args) { %w(some args) }
|
49
49
|
subject { Clin::CommandDispatcher.new(cmd1, cmd2) }
|
50
50
|
context 'when first command match' do
|
@@ -81,4 +81,4 @@ RSpec.describe Clin::CommandDispatcher do
|
|
81
81
|
it { expect(@error.to_s).to eq(subject.help_message) }
|
82
82
|
end
|
83
83
|
end
|
84
|
-
end
|
84
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
RSpec.describe Clin::CommandMixin::Core do
|
2
|
+
def new_subject
|
3
|
+
a = Class.new
|
4
|
+
a.include Clin::CommandMixin::Core
|
5
|
+
a
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.prioritize' do
|
9
|
+
subject { new_subject }
|
10
|
+
it 'set priority to 1 when no argument given' do
|
11
|
+
subject.prioritize
|
12
|
+
expect(subject._priority).to be 1
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'set priority to 1 when no argument given' do
|
16
|
+
subject.prioritize(42)
|
17
|
+
expect(subject._priority).to be 42
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '.priority' do
|
22
|
+
subject { new_subject }
|
23
|
+
it 'get the default priority' do
|
24
|
+
expect(subject.priority).to be subject._default_priority
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'sum default and priority when subject has been prioritize' do
|
28
|
+
subject.prioritize(42)
|
29
|
+
expect(subject.priority).to be subject._default_priority + 42
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'has a smaller priority when inheriting' do
|
33
|
+
child = Class.new(subject)
|
34
|
+
expect(child.priority).to be < subject.priority
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,9 +1,14 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'clin/command_options_mixin'
|
3
2
|
|
4
|
-
RSpec.describe Clin::
|
3
|
+
RSpec.describe Clin::CommandMixin::Options do
|
4
|
+
def new_subject
|
5
|
+
a = Class.new
|
6
|
+
a.include Clin::CommandMixin::Options
|
7
|
+
a
|
8
|
+
end
|
9
|
+
|
5
10
|
describe '#add_option' do
|
6
|
-
subject {
|
11
|
+
subject { new_subject }
|
7
12
|
let(:option) { Clin::Option.new(:name, '-n') }
|
8
13
|
before do
|
9
14
|
subject.add_option option
|
@@ -11,11 +16,10 @@ RSpec.describe Clin::CommandOptionsMixin do
|
|
11
16
|
|
12
17
|
it { expect(subject.options.size).to be 1 }
|
13
18
|
it { expect(subject.options.first).to eq option }
|
14
|
-
it { expect(Clin::CommandOptionsMixin.options.size).to be 0 }
|
15
19
|
end
|
16
20
|
|
17
21
|
describe '#option' do
|
18
|
-
subject {
|
22
|
+
subject { new_subject }
|
19
23
|
let(:args) { [:name, '-n'] }
|
20
24
|
let(:option) { Clin::Option.new(*args) }
|
21
25
|
|
@@ -29,7 +33,7 @@ RSpec.describe Clin::CommandOptionsMixin do
|
|
29
33
|
end
|
30
34
|
|
31
35
|
describe '#general_option' do
|
32
|
-
subject {
|
36
|
+
subject { new_subject }
|
33
37
|
let(:option) { double(:option, register_options: true, new: true) }
|
34
38
|
before do
|
35
39
|
subject.general_option option
|
@@ -37,11 +41,10 @@ RSpec.describe Clin::CommandOptionsMixin do
|
|
37
41
|
it { expect(option).to have_received(:new) }
|
38
42
|
it { expect(subject.general_options.size).to be 1 }
|
39
43
|
it { expect(subject.general_options.values.first).to eq(true) }
|
40
|
-
it { expect(Clin::CommandOptionsMixin.general_options.size).to be 0 }
|
41
44
|
end
|
42
45
|
|
43
46
|
describe '#register_options' do
|
44
|
-
subject {
|
47
|
+
subject { new_subject }
|
45
48
|
let(:opt1) { double(:option, register: true) }
|
46
49
|
let(:opt2) { double(:option, register: true) }
|
47
50
|
let(:g_opt_cls) { double(:general_option_class, register_options: true) }
|
@@ -130,8 +130,8 @@ RSpec.describe Clin::CommandParser do
|
|
130
130
|
end
|
131
131
|
|
132
132
|
context 'when using commands' do
|
133
|
-
let(:cmd1) { double(:
|
134
|
-
let(:cmd2) { double(:
|
133
|
+
let(:cmd1) { double(:command_mixin) }
|
134
|
+
let(:cmd2) { double(:command_mixin) }
|
135
135
|
before do
|
136
136
|
@command.dispatch :args, commands: [cmd1, cmd2]
|
137
137
|
allow_any_instance_of(Clin::CommandDispatcher).to receive(:initialize)
|
data/spec/clin/command_spec.rb
CHANGED
@@ -67,9 +67,9 @@ RSpec.describe Clin::Command do
|
|
67
67
|
subject.arguments(%w(remote <args>...))
|
68
68
|
end
|
69
69
|
|
70
|
-
let(:cmd1) { double(:
|
71
|
-
let(:cmd2) { double(:
|
72
|
-
let(:cmd3) { double(:
|
70
|
+
let(:cmd1) { double(:command_mixin, usage: 'cmd1') }
|
71
|
+
let(:cmd2) { double(:command_mixin, usage: 'cmd2') }
|
72
|
+
let(:cmd3) { double(:command_mixin, usage: 'cmd3') }
|
73
73
|
let(:cmds) { [cmd1, cmd2, cmd3] }
|
74
74
|
let(:opts) { double(:option_parser, separator: true) }
|
75
75
|
before do
|