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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 96826af1a7b6e82847b69c692292d7c2a10c69ed
4
- data.tar.gz: 28fd292aafc8be55a10af75853283a4def9def65
3
+ metadata.gz: be4386269225e68a69c419a0a4a90668f5da8287
4
+ data.tar.gz: 0a33c686511f6a0498fe40276c936b6574fbe497
5
5
  SHA512:
6
- metadata.gz: 3690346888ff69f711f292bca3f1e15a4580599ea12f070264b2c6eb6cdedc3f5081ac8113c08d2f391e1a892c6e6e36832d9351367ce83a924ab9ec801392e3
7
- data.tar.gz: 05d2c816d4140359ce2a01fca4462417b6c55fca85580fb7ffe7b8692b60498fe33b2dad151b21c783b7673b1a47f8e31a19215e1c63982a072e73a28345f1fd
6
+ metadata.gz: 2878f163e2974a9a3c93e9f565e9b5fdaf5ec91bcced8856b95b7045f45eca8427da8a22631f1741da582982ab1c575e19c6115cee4dd8078a69a08f217cf41d
7
+ data.tar.gz: eba57a52877c4ea5fca71571ab1a534459f173d9715028774b76a6be8fb9e7721b077fdc8371839ba770a43dfde17bd34fd7bf2c71e2001e88c6898be7458fab
data/.travis.yml CHANGED
@@ -1,6 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - '2.0'
4
3
  - '2.1'
5
4
  - '2.2'
6
5
 
data/CHANGELOG.md CHANGED
@@ -1,7 +1,14 @@
1
+ ## 0.3.0
2
+ Features:
3
+ - Added a shell class for any user interaction.
4
+ - Added `priority` to test commands in specific order and not the order they where loaded.(#4)
5
+ - Added `auto_option` to define specific options cleaner.(#5)
6
+
1
7
  ## 0.2.0
2
8
  Features:
3
9
  - Allow unknown options to be ignored and not raise Error(#1)
4
10
  - Added list options. For options that can be multiple times in the same command(#2)
5
11
  - Added default value for options.(#3)
12
+
6
13
  ## 0.1.0
7
14
  Inital release.
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![Coverage Status](https://coveralls.io/repos/timcolonel/clin/badge.svg?branch=master)](https://coveralls.io/r/timcolonel/clin?branch=master)
4
4
  [![Code Climate](https://codeclimate.com/github/timcolonel/clin/badges/gpa.svg)](https://codeclimate.com/github/timcolonel/clin)
5
5
  [![Inline docs](http://inch-ci.org/github/timcolonel/clin.svg?branch=master)](http://inch-ci.org/github/timcolonel/clin)
6
+
6
7
  Clin is Command Line Interface library that provide an clean api for complex command configuration.
7
8
  The way Clin is design allow a command defined by the user to be called via the command line as well as directly in the code without any additional configuration
8
9
  ## Installation
data/clin.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.add_dependency 'activesupport', '>=4.0'
22
22
  spec.add_development_dependency 'bundler', '~> 1.7'
23
23
  spec.add_development_dependency 'rake', '~> 10.0'
24
- spec.add_development_dependency 'rspec'
24
+ spec.add_development_dependency 'rspec', '>= 3.0'
25
25
  spec.add_development_dependency 'coveralls'
26
26
  spec.add_development_dependency 'faker'
27
27
  end
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.push File.expand_path('../../lib', __FILE__)
2
+ require 'clin'
3
+ require 'clin'
4
+
5
+ # Simple command Example
6
+ class AutoOptionCommand < Clin::Command
7
+ auto_option :echo, '-e --eko=message Echo the message'
8
+ general_option Clin::HelpOptions
9
+
10
+ def run
11
+ puts @params[:echo]
12
+ end
13
+ end
14
+
15
+ # Run example:
16
+ # AutoOptionCommand.parse('-e "Some message 1"').run
17
+ # AutoOptionCommand.parse('--eko="Some message 2"').run
@@ -5,7 +5,7 @@ require 'clin'
5
5
  class DisplayCommand < Clin::Command
6
6
  arguments 'display <message>'
7
7
 
8
- self.description = 'Display the given message'
8
+ description 'Display the given message'
9
9
 
10
10
  def run
11
11
  puts "Display: '#{params[:message]}'"
@@ -16,33 +16,27 @@ end
16
16
  class PrintCommand < Clin::Command
17
17
  arguments 'print <message>'
18
18
 
19
- self.description = 'Print the given message'
19
+ description 'Print the given message'
20
20
 
21
21
  def run
22
22
  puts "Print: '#{params[:message]}'"
23
23
  end
24
24
  end
25
25
 
26
- if __FILE__ == $PROGRAM_NAME
27
- Clin::CommandDispatcher.parse('display "My Message"').run
28
- puts
29
- puts '=' * 60
30
- puts
31
- Clin::CommandDispatcher.parse('print "My Message"').run
32
- puts
33
- puts '=' * 60
34
- puts
35
- begin
36
- Clin::CommandDispatcher.parse('display -h').run
37
- rescue Clin::CommandLineError => e
38
- puts e
39
- end
40
- puts
41
- puts '=' * 60
42
- puts
43
- begin
44
- Clin::CommandDispatcher.parse('-h')
45
- rescue Clin::CommandLineError => e
46
- puts e
26
+ # Simple command Example
27
+ class PrintAltCommand < Clin::Command
28
+ arguments 'print <message>'
29
+
30
+ description 'Print the given message alternative'
31
+ prioritize
32
+
33
+ def run
34
+ puts "Print alt: '#{params[:message]}'"
47
35
  end
48
36
  end
37
+
38
+ # Example of run:
39
+ # Clin::CommandDispatcher.parse('display "My Message"').run
40
+ # Clin::CommandDispatcher.parse('print "My Message"').run
41
+ # Clin::CommandDispatcher.parse('display -h').run
42
+ # Clin::CommandDispatcher.parse('-h')
@@ -17,11 +17,6 @@ class ListCommand < Clin::Command
17
17
  end
18
18
  end
19
19
  end
20
-
21
- if __FILE__ == $PROGRAM_NAME
22
- ListCommand.parse('--echo "Message 1" --echo "Message 2"').run
23
- puts
24
- puts '=' * 60
25
- puts
26
- ListCommand.parse('--echo "Message 3" --echo "Message 4" -ll').run
27
- end
20
+ # Use examples:
21
+ # ListCommand.parse('--echo "Message 1" --echo "Message 2"').run
22
+ # ListCommand.parse('--echo "Message 3" --echo "Message 4" -ll').run
@@ -9,7 +9,7 @@ class DispatchCommand < Clin::Command
9
9
 
10
10
  flag_option :verbose, 'Verbose the output'
11
11
 
12
- self.description = 'YOU print the given message'
12
+ description 'YOU print the given message'
13
13
 
14
14
  def run
15
15
  puts 'Should not be called'
@@ -22,7 +22,7 @@ class DisplayCommand < DispatchCommand
22
22
  option :echo, 'Display more text'
23
23
  option :times, 'Display the text multiple times', type: Integer
24
24
 
25
- self.description = 'Display the given message'
25
+ description 'Display the given message'
26
26
 
27
27
  def run
28
28
  puts "I Display: '#{params[:message]}'"
@@ -33,60 +33,15 @@ end
33
33
  class PrintCommand < DispatchCommand
34
34
  arguments 'you print <message>'
35
35
 
36
- self.description = 'Print the given message'
36
+ description 'Print the given message'
37
37
 
38
38
  def run
39
39
  puts "I Print: '#{params[:message]}'"
40
40
  end
41
41
  end
42
42
 
43
- if __FILE__ == $PROGRAM_NAME
44
- Clin::CommandDispatcher.parse('you display "My Message"').run
45
- puts
46
- puts '=' * 60
47
- puts
48
- Clin::CommandDispatcher.parse('you print "My Message"').run
49
- puts
50
- puts '=' * 60
51
- puts
52
- begin
53
- Clin::CommandDispatcher.parse('you -h').run
54
- rescue Clin::CommandLineError => e
55
- puts e
56
- end
57
- puts
58
- puts '=' * 60
59
- puts
60
- begin
61
- Clin::CommandDispatcher.parse('-h')
62
- rescue Clin::CommandLineError => e
63
- puts e
64
- end
65
- end
66
-
67
- # Output:
68
- #
69
- # $ ruby dispatcher.rb
70
- # I Display: 'My Message'
71
- #
72
- # ============================================================
73
- #
74
- # I Print: 'My Message'
75
- #
76
- # ============================================================
77
- #
78
- # Usage: command you <args>... [Options]
79
- #
80
- # Options:
81
- # -h, --help Show the help.
82
- #
83
- # Description:
84
- # YOU print the given message
85
- #
86
- #
87
- # ============================================================
88
- #
89
- # Usage:
90
- # command you <args>... [Options]
91
- # command you display <message> [Options]
92
- # command you print <message> [Options]
43
+ # Example of run:
44
+ # Clin::CommandDispatcher.parse('you display "My Message"').run
45
+ # Clin::CommandDispatcher.parse('you print "My Message"').run
46
+ # Clin::CommandDispatcher.parse('you -h').run
47
+ # Clin::CommandDispatcher.parse('-h')
data/examples/simple.rb CHANGED
@@ -15,25 +15,6 @@ class SimpleCommand < Clin::Command
15
15
  end
16
16
  end
17
17
 
18
- if __FILE__ == $PROGRAM_NAME
19
- SimpleCommand.parse('display "My Message" --echo SOME').run
20
- puts
21
- puts '=' * 60
22
- puts
23
- begin
24
- SimpleCommand.parse('').run
25
- rescue Clin::HelpError => e
26
- puts e
27
- end
28
- end
29
- # $ ruby simple.rb
30
- # My Message
31
- # SOME
32
- #
33
- # ============================================================
34
- #
35
- # Usage: command display <message> [Options]
36
- #
37
- # Options:
38
- # -e, --echo ECHO Echo some text
39
- # -h, --help Show the help.
18
+ # Run example:
19
+ # SimpleCommand.parse('display "My Message" --echo SOME').run
20
+ # SimpleCommand.parse('').run
data/examples/test.rb CHANGED
@@ -1,9 +1,9 @@
1
- $LOAD_PATH.push File.expand_path('../../lib', __FILE__)
2
- require 'clin'
3
-
4
- a = [1, 2, 3]
5
-
6
- b, c = a
7
-
8
- puts b
9
- puts c
1
+ $LOAD_PATH.push File.expand_path('../../lib', __FILE__)
2
+ require 'clin'
3
+
4
+
5
+ shell = Clin::Shell.new
6
+
7
+ choice = shell.choose('What?', %w(france usa italy germany))
8
+
9
+ puts 'YOu ' + choice
data/lib/clin/argument.rb CHANGED
@@ -20,7 +20,7 @@ class Clin::Argument
20
20
 
21
21
  # Check if the argument is optional(i.e [arg])
22
22
  def check_optional(argument)
23
- if beck_between(argument, '[', ']')
23
+ if check_between(argument, '[', ']')
24
24
  @optional = true
25
25
  return argument[1...-1]
26
26
  end
@@ -38,7 +38,7 @@ class Clin::Argument
38
38
 
39
39
  # Check if the argument is variable(i.e <arg>)
40
40
  def check_variable(argument)
41
- if beck_between(argument, '<', '>')
41
+ if check_between(argument, '<', '>')
42
42
  @variable = true
43
43
  return argument[1...-1]
44
44
  end
@@ -94,7 +94,7 @@ class Clin::Argument
94
94
  # beck_between('[<arg>]', '<'. '>') # => false
95
95
  # beck_between('[<arg>', '<'. '>') # => raise Clin::Error
96
96
  # ```
97
- def beck_between(argument, start_char, end_char)
97
+ def check_between(argument, start_char, end_char)
98
98
  if argument[0] == start_char
99
99
  if argument[-1] != end_char
100
100
  fail Clin::Error, "Argument format error! Cannot start
data/lib/clin/command.rb CHANGED
@@ -1,83 +1,15 @@
1
1
  require 'clin'
2
- require 'clin/command_options_mixin'
3
2
  require 'clin/argument'
4
3
  require 'shellwords'
5
4
  require 'clin/common/help_options'
6
5
 
7
6
  # Clin Command
8
- class Clin::Command < Clin::CommandOptionsMixin
9
- class_attribute :args
10
- class_attribute :description
7
+ class Clin::Command
8
+ include Clin::CommandMixin::Core
9
+ include Clin::CommandMixin::Dispatcher
10
+ include Clin::CommandMixin::Options
11
11
 
12
- # Redispatch will be reset to nil when inheriting a dispatcher command
13
- class_attribute :_redispatch_args
14
- class_attribute :_abstract
15
- class_attribute :_exe_name
16
- class_attribute :_skip_options
17
-
18
- self.args = []
19
- self.description = ''
20
- self._abstract = false
21
- self._skip_options = false
22
-
23
- # Trigger when a class inherit this class
24
- # Rest class_attributes that should not be shared with subclass
25
- # @param subclass [Clin::Command]
26
- def self.inherited(subclass)
27
- subclass._redispatch_args = nil
28
- subclass._abstract = false
29
- subclass._skip_options = false
30
- super
31
- end
32
-
33
- # Mark the class as abstract
34
- def self.abstract(value)
35
- self._abstract = value
36
- end
37
-
38
- # Set or get the exe name.
39
- # Executable name that will be display in the usage.
40
- # If exe_name is not set in a class or it's parent it will use the global setting Clin.exe_name
41
- # @param value [String] name of the exe.
42
- # ```
43
- # class Git < Clin::Command
44
- # exe_name 'git'
45
- # arguments '<command> <args>...'
46
- # end
47
- # Git.usage # => git <command> <args>...
48
- # ```
49
- def self.exe_name(value = nil)
50
- self._exe_name = value unless value.nil?
51
- self._exe_name ||= Clin.exe_name
52
- end
53
-
54
- def self.skip_options(value)
55
- self._skip_options = value
56
- end
57
-
58
- def self.skip_options?
59
- _skip_options
60
- end
61
-
62
- def self.redispatch?
63
- !_redispatch_args.nil?
64
- end
65
-
66
- def self.arguments(args)
67
- self.args = []
68
- [*args].map(&:split).flatten.each do |arg|
69
- self.args += [Clin::Argument.new(arg)]
70
- end
71
- end
72
-
73
- def self.usage
74
- a = [exe_name, args.map(&:original).join(' '), '[Options]']
75
- a.reject(&:blank?).join(' ')
76
- end
77
-
78
- def self.banner
79
- "Usage: #{usage}"
80
- end
12
+ general_option 'Clin::HelpOptions'
81
13
 
82
14
  # Parse the command and initialize the command object with the parsed options
83
15
  # @param argv [Array|String] command line to parse.
@@ -86,63 +18,16 @@ class Clin::Command < Clin::CommandOptionsMixin
86
18
  parser.parse
87
19
  end
88
20
 
89
- # Build the Option Parser object
90
- # Used to parse the option
91
- # Useful for regenerating the help as well.
92
- def self.option_parser(out = {})
93
- OptionParser.new do |opts|
94
- opts.banner = banner
95
- opts.separator ''
96
- opts.separator 'Options:'
97
- register_options(opts, out)
98
- dispatch_doc(opts)
99
- unless description.blank?
100
- opts.separator "\nDescription:"
101
- opts.separator description
102
- end
103
- opts.separator ''
104
- end
105
- end
106
-
107
- # Redispatch the command to a sub command with the given arguments
108
- # @param args [Array<String>|String] New argument to parse
109
- # @param prefix [String] Prefix to add to the beginning of the command
110
- # @param commands [Array<Clin::Command.class>] Commands that will be tried against
111
- # If no commands are given it will look for Clin::Command in the class namespace
112
- # e.g. If those 2 classes are defined.
113
- # `MyDispatcher < Clin::Command` and `MyDispatcher::ChildCommand < Clin::Command`
114
- # Will test against ChildCommand
115
- def self.dispatch(args, prefix: nil, commands: nil)
116
- self._redispatch_args = [[*args], prefix, commands]
117
- end
118
-
119
- def self.dispatch_doc(opts)
120
- return if _redispatch_args.nil?
121
- opts.separator 'Examples: '
122
- commands = (_redispatch_args[2] || default_commands)
123
- commands.each do |cmd_cls|
124
- opts.separator "\t#{cmd_cls.usage}"
125
- end
126
- end
127
-
128
- def self.default_commands
129
- # self.constants.map { |c| self.const_get(c) }
130
- # .select { |c| c.is_a?(Class) && (c < Clin::Command) }
131
- subcommands
132
- end
133
-
134
- # List the subcommands
135
- # The subcommands are all the Classes inheriting this one that are not set to abstract
136
- def self.subcommands
137
- subclasses.reject(&:_abstract)
138
- end
139
-
140
- general_option 'Clin::HelpOptions'
141
-
21
+ # Contains the parameters
142
22
  attr_accessor :params
143
23
 
24
+ # Contains a shell object for user interaction in the command
25
+ # @see Clin::Shell
26
+ attr_accessor :shell
27
+
144
28
  def initialize(params = {})
145
29
  @params = params
30
+ @shell = Clin::Shell.new
146
31
  self.class.execute_general_options(params)
147
32
  end
148
33
  end
@@ -0,0 +1,152 @@
1
+ require 'clin'
2
+
3
+ # Contains the core class methods for the command mixin
4
+ module Clin::CommandMixin::Core
5
+ extend ActiveSupport::Concern
6
+ included do
7
+ @_arguments = []
8
+ @_default_priority = 1000
9
+ @_priority = 0
10
+ end
11
+
12
+ module ClassMethods # :nodoc:
13
+ attr_accessor :_arguments
14
+ attr_accessor :_description
15
+ attr_accessor :_abstract
16
+ attr_accessor :_exe_name
17
+ attr_accessor :_skip_options
18
+ attr_accessor :_default_priority
19
+ attr_accessor :_priority
20
+
21
+ # Trigger when a class inherit this class
22
+ # Rest class_attributes that should not be shared with subclass
23
+ # @param subclass [Clin::Command]
24
+ def inherited(subclass)
25
+ subclass._arguments = []
26
+ subclass._description = ''
27
+ subclass._abstract = false
28
+ subclass._skip_options = false
29
+ subclass._exe_name = @_exe_name
30
+ subclass._default_priority = @_default_priority.to_f / 2
31
+ subclass._priority = 0
32
+ super
33
+ end
34
+
35
+ # Mark the class as abstract
36
+ def abstract(value)
37
+ @_abstract = value
38
+ end
39
+
40
+ # Return if the current command class is abstract
41
+ # @return [Boolean]
42
+ def abstract?
43
+ @_abstract
44
+ end
45
+
46
+ # Set or get the exe name.
47
+ # Executable name that will be display in the usage.
48
+ # If exe_name is not set in a class or it's parent it will use the global setting Clin.exe_name
49
+ # @param value [String] name of the exe.
50
+ # ```
51
+ # class Git < Clin::Command
52
+ # exe_name 'git'
53
+ # arguments '<command> <args>...'
54
+ # end
55
+ # Git.usage # => git <command> <args>...
56
+ # ```
57
+ def exe_name(value = nil)
58
+ @_exe_name = value unless value.nil?
59
+ @_exe_name ||= Clin.exe_name
60
+ end
61
+
62
+ # Allow the current option to skip unknown options.
63
+ # @param value [Boolean] skip or not
64
+ def skip_options(value)
65
+ @_skip_options = value
66
+ end
67
+
68
+ # Is the current command skipping options
69
+ def skip_options?
70
+ @_skip_options
71
+ end
72
+
73
+ # Set or get the arguments for the command
74
+ # @param args [Array<String>] List of arguments to set. If nil it just return the current args.
75
+ def arguments(args = nil)
76
+ return @_arguments if args.nil?
77
+ @_arguments = []
78
+ [*args].map(&:split).flatten.each do |arg|
79
+ @_arguments << Clin::Argument.new(arg)
80
+ end
81
+ end
82
+
83
+ alias_method :args, :arguments
84
+
85
+ # Set or get the description
86
+ # @param desc [String] Description to set. If nil it just return the current value.
87
+ def description(desc = nil)
88
+ @_description = desc unless desc.nil?
89
+ @_description
90
+ end
91
+
92
+ def usage
93
+ a = [exe_name, @_arguments.map(&:original).join(' '), '[Options]']
94
+ a.reject(&:blank?).join(' ')
95
+ end
96
+
97
+ def banner
98
+ "Usage: #{usage}"
99
+ end
100
+
101
+ # Priorities this command.
102
+ # This does not set the priority. It add +value+ to the default priority
103
+ # The default priority is computed using half of the parent default priority.
104
+ # e.g.
105
+ # ```
106
+ # Parent = Class.new(Clin::Command)
107
+ # Child1 = Class.new(Parent)
108
+ # Child2 = Class.new(Parent)
109
+ # Parent.priority # => 500
110
+ # Child1.priority # => 250
111
+ # Child2.priority # => 250
112
+ # Child2.prioritize
113
+ # Child2.priority # => 251
114
+ # ```
115
+ # When dispatching commands they are sorted by priority
116
+ def prioritize(value = 1)
117
+ @_priority = value
118
+ end
119
+
120
+ def priority
121
+ @_default_priority + @_priority
122
+ end
123
+
124
+ # Build the Option Parser object
125
+ # Used to parse the option
126
+ # Useful for regenerating the help as well.
127
+ def option_parser(out = {})
128
+ OptionParser.new do |opts|
129
+ opts.banner = banner
130
+ opts.separator ''
131
+ opts.separator 'Options:'
132
+ register_options(opts, out)
133
+ dispatch_doc(opts)
134
+ unless @description.blank?
135
+ opts.separator "\nDescription:"
136
+ opts.separator @description
137
+ end
138
+ opts.separator ''
139
+ end
140
+ end
141
+
142
+ def default_commands
143
+ subcommands.sort_by(&:priority).reverse
144
+ end
145
+
146
+ # List the subcommands
147
+ # The subcommands are all the Classes inheriting this one that are not set to abstract
148
+ def subcommands
149
+ subclasses.reject(&:abstract?)
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,34 @@
1
+ require 'clin'
2
+
3
+ # Dispatcher mixin contains the class methods for command that need to redispatch to subcommands
4
+ module Clin::CommandMixin::Dispatcher
5
+ extend ActiveSupport::Concern
6
+ module ClassMethods # :nodoc:
7
+ attr_accessor :_redispatch_args
8
+
9
+ # Redispatch the command to a sub command with the given arguments
10
+ # @param args [Array<String>|String] New argument to parse
11
+ # @param prefix [String] Prefix to add to the beginning of the command
12
+ # @param commands [Array<Clin::Command.class>] Commands that will be tried against
13
+ # If no commands are given it will look for Clin::Command in the class namespace
14
+ # e.g. If those 2 classes are defined.
15
+ # `MyDispatcher < Clin::Command` and `MyDispatcher::ChildCommand < Clin::Command`
16
+ # Will test against ChildCommand
17
+ def dispatch(args, prefix: nil, commands: nil)
18
+ @_redispatch_args = [[*args], prefix, commands]
19
+ end
20
+
21
+ def redispatch?
22
+ !@_redispatch_args.nil?
23
+ end
24
+
25
+ def dispatch_doc(opts)
26
+ return if _redispatch_args.nil?
27
+ opts.separator 'Examples: '
28
+ commands = (_redispatch_args[2] || default_commands)
29
+ commands.each do |cmd_cls|
30
+ opts.separator "\t#{cmd_cls.usage}"
31
+ end
32
+ end
33
+ end
34
+ end