clin 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 98bdce921d4896c86ed482d8db5cf87a74b2795a
4
+ data.tar.gz: e025be0b4c3c5fd36f49437e21abfc1f00ef1a0c
5
+ SHA512:
6
+ metadata.gz: 8a70c2a7c6e53bf8eb781b37841dc24052e8396a2b6a2e8d73d6ea9dc90525fe1e03bf2437f43aefdeaf6aef648b210d7c79fec2fd8d7a31734c5ab21da7d9c1
7
+ data.tar.gz: ef60cf35207752817b52bae1514c55f5db9c12e5f87ae322a17469b9039b6a37470327c6bb4e4d43d37f024c5ae5acd283ec200090c6bad5f6705739086edd49
data/.codeclimate.yml ADDED
@@ -0,0 +1,5 @@
1
+ languages:
2
+ Ruby: true
3
+ exclude_paths:
4
+ - "spec/*"
5
+ - "examples/*"
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .idea
data/.rubocop.yml ADDED
@@ -0,0 +1,19 @@
1
+ Metrics/LineLength:
2
+ Max: 100
3
+
4
+ Style/ClassAndModuleChildren:
5
+ Enabled: false
6
+
7
+ Style/SpaceInsideHashLiteralBraces:
8
+ EnforcedStyle: no_space
9
+
10
+ Metrics/MethodLength:
11
+ Description: Avoid methods longer than 25 lines of code.
12
+ Enabled: true
13
+ CountComments: false
14
+ Max: 25
15
+
16
+
17
+ AllCops:
18
+ Exclude:
19
+ - 'spec/**/*'
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - '2.0'
4
+ - '2.1'
5
+ - '2.2'
6
+
7
+
8
+ gemfile:
9
+ - Gemfile
10
+
11
+ matrix:
12
+
13
+ notifications:
14
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in clin.gemspec
4
+ gemspec
5
+
6
+ gem 'rubocop'
7
+ group :doc do
8
+ gem 'yard'
9
+ gem 'redcarpet'
10
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Timothee Guerin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # Clin
2
+ [![Build Status](https://travis-ci.org/timcolonel/clin.svg?branch=master)](https://travis-ci.org/timcolonel/clin)
3
+ [![Coverage Status](https://coveralls.io/repos/timcolonel/clin/badge.svg?branch=master)](https://coveralls.io/r/timcolonel/clin?branch=master)
4
+ [![Code Climate](https://codeclimate.com/github/timcolonel/clin/badges/gpa.svg)](https://codeclimate.com/github/timcolonel/clin)
5
+
6
+ Clin is Command Line Interface library that provide an clean api for complex command configuration.
7
+ 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
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'clin'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install clin
23
+
24
+ Then add the following in you ruby script.
25
+ ```ruby
26
+ require 'clin'
27
+ ```
28
+ ## Usage
29
+
30
+ The [examples](examples/) folder contains various use case of Clin.
31
+
32
+ ### Define a command
33
+ To define a command you must create a new class that inherit `Clin::Command`:
34
+
35
+ ```ruby
36
+ class DisplayCommand < Clin::Command
37
+ def run
38
+
39
+ end
40
+ end
41
+ ```
42
+
43
+ #### Specify arguments
44
+ To specify what argument your command takes use the `.arguments` method.
45
+ Clin will then automatically extract the arguments when parsing and pass them when creating the object.
46
+ You can after access the arguments with @params
47
+ ```ruby
48
+ class DisplayCommand < Clin::Command
49
+ arguments 'display <message>'
50
+
51
+ def run
52
+ puts "Display message: #{params[:message}"
53
+ end
54
+ end
55
+ ```
56
+
57
+ Then calling `DisplayCommand.parse('display "Hello World!"').run` will print `Display message: Hello World!`
58
+
59
+ #### Specify options
60
+ You can also specify options using the `.option` method.
61
+ ```ruby
62
+ class DisplayCommand < Clin::Command
63
+ arguments 'display <message>'
64
+ option :times, 'Display the message n times'
65
+
66
+ def run
67
+ params[:times] ||= 1
68
+ params[:times].times.each do
69
+ puts "Display message: #{params[:message}"
70
+ end
71
+ end
72
+ end
73
+ ```
74
+
75
+ ### Dispatch to the right command
76
+ For complex command line interface you might need several different commands(e.g. git add, git commit,etc.)
77
+ Define each command as shown previously then use the CommandDispatcher to choose the right command.
78
+ By default the dispatcher is going to try all the loaded commands(All subclasses of Clin::Commands)
79
+ but it can be filter.
80
+ ```ruby
81
+ # Suppose Git::Add and Git::Commit are Clin::Command.
82
+ Clin::CommandDispatcher.parse('commit -m "initial commit") #=> Git::Commit<params: {message: "initial commit"}>
83
+ ```
84
+
85
+ To limit filter the usage:
86
+ ```ruby
87
+ # Suppose Git::Add, Git::Commit, Git::Push are Clin::Command.
88
+ dispatcher = Clin::CommandDispatcher.new(Git::Commit, Git::Push)
89
+ dispatcher.parse('commit add -A") #=> Will show the help as no command match.
90
+ dispatcher.parse('commit -m "initial commit") #=> Git::Commit<params: {message: "initial commit"}>
91
+ ```
92
+ ## Contributing
93
+
94
+ 1. Fork it ( https://github.com/timcolonel/clin/fork )
95
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
96
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
97
+ 4. Push to the branch (`git push origin my-new-feature`)
98
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ task default: :spec
8
+ rescue LoadError
9
+ puts 'Spec cannot be loaded!'
10
+ end
data/clin.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'clin/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'clin'
8
+ spec.version = Clin::VERSION
9
+ spec.authors = ['Timothee Guerin']
10
+ spec.email = ['timothee.guerin@outlook.com']
11
+ spec.summary = 'Clin provide an advance way to define complex command line interface.'
12
+ spec.description = ''
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'activesupport', '>=4.0'
22
+ spec.add_development_dependency 'bundler', '~> 1.7'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rspec'
25
+ spec.add_development_dependency 'coveralls'
26
+ spec.add_development_dependency 'faker'
27
+ end
@@ -0,0 +1,76 @@
1
+ $LOAD_PATH.push File.expand_path('../../lib', __FILE__)
2
+ require 'clin'
3
+
4
+ # Simple command Example
5
+ class DisplayCommand < Clin::Command
6
+ arguments 'display <message>'
7
+
8
+ general_option Clin::HelpOptions
9
+
10
+ self.description = 'Display the given message'
11
+
12
+ def run
13
+ puts "Display: '#{params[:message]}'"
14
+ end
15
+ end
16
+
17
+ # Simple command Example
18
+ class PrintCommand < Clin::Command
19
+ arguments 'print <message>'
20
+
21
+ general_option Clin::HelpOptions
22
+
23
+ self.description = 'Print the given message'
24
+
25
+ def run
26
+ puts "Print: '#{params[:message]}'"
27
+ end
28
+ end
29
+
30
+ Clin::CommandDispatcher.parse('display "My Message"').run
31
+ puts
32
+ puts '=' * 60
33
+ puts
34
+ Clin::CommandDispatcher.parse('print "My Message"').run
35
+ puts
36
+ puts '=' * 60
37
+ puts
38
+ begin
39
+ Clin::CommandDispatcher.parse('display -h').run
40
+ rescue Clin::CommandLineError => e
41
+ puts e
42
+ end
43
+ puts
44
+ puts '=' * 60
45
+ puts
46
+ begin
47
+ Clin::CommandDispatcher.parse('-h')
48
+ rescue Clin::CommandLineError => e
49
+ puts e
50
+ end
51
+
52
+ # Output:
53
+ #
54
+ # $ ruby dispatcher.rb
55
+ # Display: 'My Message'
56
+ #
57
+ # ============================================================
58
+ #
59
+ # Print: 'My Message'
60
+ #
61
+ # ============================================================
62
+ #
63
+ # Usage: command display <message> [Options]
64
+ #
65
+ # Options:
66
+ # -h, --help Show the help.
67
+ #
68
+ # Description:
69
+ # Display the given message
70
+ #
71
+ #
72
+ # ============================================================
73
+ #
74
+ # Usage:
75
+ # command display <message> [Options]
76
+ # command print <message> [Options]
@@ -0,0 +1,91 @@
1
+ $LOAD_PATH.push File.expand_path('../../lib', __FILE__)
2
+ require 'clin'
3
+
4
+ # Simple dispatch Example
5
+ class DispatchCommand < Clin::Command
6
+ arguments 'you <args>...'
7
+ dispatch :args, prefix: 'you'
8
+ general_option Clin::HelpOptions
9
+
10
+ self.description = 'YOU print the given message'
11
+
12
+ def run
13
+ puts 'Should not be called'
14
+ end
15
+ end
16
+
17
+ # Simple command Example
18
+ class DisplayCommand < DispatchCommand
19
+ arguments 'you display <message>'
20
+
21
+ general_option Clin::HelpOptions
22
+
23
+ self.description = 'Display the given message'
24
+
25
+ def run
26
+ puts "I Display: '#{params[:message]}'"
27
+ end
28
+ end
29
+
30
+ # Simple command Example
31
+ class PrintCommand < DispatchCommand
32
+ arguments 'you print <message>'
33
+
34
+ general_option Clin::HelpOptions
35
+
36
+ self.description = 'Print the given message'
37
+
38
+ def run
39
+ puts "I Print: '#{params[:message]}'"
40
+ end
41
+ end
42
+
43
+
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
+
66
+ # Output:
67
+ #
68
+ # $ ruby dispatcher.rb
69
+ # I Display: 'My Message'
70
+ #
71
+ # ============================================================
72
+ #
73
+ # I Print: 'My Message'
74
+ #
75
+ # ============================================================
76
+ #
77
+ # Usage: command you <args>... [Options]
78
+ #
79
+ # Options:
80
+ # -h, --help Show the help.
81
+ #
82
+ # Description:
83
+ # YOU print the given message
84
+ #
85
+ #
86
+ # ============================================================
87
+ #
88
+ # Usage:
89
+ # command you <args>... [Options]
90
+ # command you display <message> [Options]
91
+ # command you print <message> [Options]
@@ -0,0 +1,24 @@
1
+ $LOAD_PATH.push File.expand_path('../../lib', __FILE__)
2
+ require 'clin'
3
+
4
+ # Simple command Example
5
+ class OptionalArgumentCommand < Clin::Command
6
+ arguments 'display [<message>]'
7
+
8
+ def run
9
+ puts params.fetch(:message, 'No message given')
10
+ end
11
+ end
12
+
13
+ OptionalArgumentCommand.parse('display "My Message"')
14
+ puts
15
+ puts '=' * 60
16
+ puts
17
+ OptionalArgumentCommand.parse('display')
18
+
19
+ # $ ruby optional_argument.rb
20
+ # My Message
21
+ #
22
+ # ============================================================
23
+ #
24
+ # No message given
@@ -0,0 +1,39 @@
1
+ $LOAD_PATH.push File.expand_path('../../lib', __FILE__)
2
+ require 'clin'
3
+ require 'clin'
4
+
5
+ # Simple command Example
6
+ class SimpleCommand < Clin::Command
7
+ arguments 'display <message>'
8
+
9
+ option :echo, 'Echo some text'
10
+ general_option Clin::HelpOptions
11
+
12
+ def run
13
+ puts @params[:message]
14
+ puts @params[:echo]
15
+ end
16
+ end
17
+
18
+ if __FILE__== $0
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.
data/examples/test.rb ADDED
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.push File.expand_path('../../lib', __FILE__)
2
+ require 'clin'
@@ -0,0 +1,90 @@
1
+ require 'clin'
2
+
3
+ # Command line positional argument(not option)
4
+ class Clin::Argument
5
+ attr_accessor :original
6
+ attr_accessor :optional
7
+ attr_accessor :multiple
8
+ attr_accessor :variable
9
+ attr_accessor :name
10
+
11
+ def initialize(argument)
12
+ @original = argument
13
+ @optional = false
14
+ @multiple = false
15
+ @variable = false
16
+ argument = check_optional(argument)
17
+ argument = check_multiple(argument)
18
+ @name = check_variable(argument)
19
+ end
20
+
21
+ def check_optional(argument)
22
+ if check_between(argument, '[', ']')
23
+ @optional = true
24
+ return argument[1...-1]
25
+ end
26
+ argument
27
+ end
28
+
29
+ def check_multiple(argument)
30
+ if argument.end_with? '...'
31
+ @multiple = true
32
+ return argument[0...-3]
33
+ end
34
+ argument
35
+ end
36
+
37
+ def check_variable(argument)
38
+ if check_between(argument, '<', '>')
39
+ @variable = true
40
+ return argument[1...-1]
41
+ end
42
+ argument
43
+ end
44
+
45
+ # Given a list of arguments extract the list of arguments that are matched
46
+ def parse(argv)
47
+ return handle_empty if argv.empty?
48
+ if @multiple
49
+ ensure_name(argv) unless @variable
50
+ [argv, []]
51
+ else
52
+ ensure_name(argv[0]) unless @variable
53
+ [argv[0], argv[1..-1]]
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def ensure_name(args)
60
+ [*args].each do |arg|
61
+ if arg != @name
62
+ fail Clin::FixedArgumentError, @name, arg
63
+ end
64
+ end
65
+ end
66
+
67
+ # Call when the argv is empty.
68
+ # Will return nil, [] if the argument is optional
69
+ # Will fail otherwise:
70
+ # * MissingArgumentError if the argument is a variable(e.g. <arg>)
71
+ # * FixedArgumentError if the argument is fixed(e.g. display)
72
+ def handle_empty
73
+ return nil, [] if optional
74
+ if @variable
75
+ fail Clin::MissingArgumentError, @name
76
+ else
77
+ fail Clin::FixedArgumentError, @name
78
+ end
79
+ end
80
+
81
+ def check_between(argument, start_char, end_char)
82
+ if argument[0] == start_char
83
+ if argument[-1] != end_char
84
+ fail Clin::Error, "Argument format error! Cannot start with #{start_char} and not end with #{end_char}"
85
+ end
86
+ return true
87
+ end
88
+ false
89
+ end
90
+ end