mister_bin 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 992b34023bc6042c8e5cada1a4d33674df2fda7856e3d67ec07e3e4607253ad5
4
- data.tar.gz: 1943a0338104dcbc63c14a62526d113ad2124443f694536aeec38a650dc49375
3
+ metadata.gz: df7d632c9b2d36ebd6cd52b961d4f690868575234528fa77c563e2766d4449b2
4
+ data.tar.gz: 8fef21a944ed08a965139c21ce96bf2560f1acfd0016b076345cd113d70933df
5
5
  SHA512:
6
- metadata.gz: 5d9454d65eef5a5f1f53ce6198011331ef772fece59fe831f1abb33d616f3e4847f2d6ff40281bb8aa6d50f40ede1616b5f012109df942460c61efb0fa7ea7e8
7
- data.tar.gz: 03dfe016fe75b4baa7da4fef059396f3534168ba284033b5037afd715b4e86efbf0461d0972e498828764df9f0b44034d647a570bd01fdd21ba3feede5c86798
6
+ metadata.gz: 48f0fc73b3731e339e4d131df19519c203b2ce8f1e9030323867eac715fe21793d9be8193675376a6538783a1bbbadfb1c4af40fce5076377f81a5ba1a3f82cd
7
+ data.tar.gz: 49f22c1ec956275a8c3c8a0681d9fc324e147db4595dea3681b5bc85212933a78738833b57dfa8eaa7221219f4dad172578c9726ea52d96cd38df0d9ca2dc2b1
data/README.md CHANGED
@@ -2,13 +2,13 @@ Mister Bin
2
2
  ==================================================
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/mister_bin.svg)](https://badge.fury.io/rb/mister_bin)
5
- [![Build Status](https://travis-ci.org/DannyBen/mister_bin.svg?branch=master)](https://travis-ci.org/DannyBen/mister_bin)
5
+ [![Build Status](https://travis-ci.com/DannyBen/mister_bin.svg?branch=master)](https://travis-ci.com/DannyBen/mister_bin)
6
6
  [![Maintainability](https://api.codeclimate.com/v1/badges/ae82443a99c2839d8ba8/maintainability)](https://codeclimate.com/github/DannyBen/mister_bin/maintainability)
7
7
  [![Test Coverage](https://api.codeclimate.com/v1/badges/ae82443a99c2839d8ba8/test_coverage)](https://codeclimate.com/github/DannyBen/mister_bin/test_coverage)
8
8
 
9
9
  ---
10
10
 
11
- Build modular command line utilities.
11
+ A command line framework for adding command line utilities to your gems.
12
12
 
13
13
  ---
14
14
 
@@ -41,7 +41,6 @@ Design Goals
41
41
  involved in building command line utilities.
42
42
  - Provide a mechanism for separating each command and subcommand to its
43
43
  own file.
44
- - Support the ability to extend a given command from different sources.
45
44
  - Allow gem developers to easily add command line interface to their gems.
46
45
  - Allow for easy and straight forward testing of the generated CLI.
47
46
 
@@ -59,65 +58,10 @@ Usage
59
58
 
60
59
  Creating a command line utility with Mister Bin involves at least two files:
61
60
 
62
- 1. The main "app" file. This is the actual "executable".
63
- 2. One or more subcommand files. These files use the DSL.
64
-
65
- For example, assuming we would like to create a command line tool similar
66
- to `git`, we will create two files:
67
-
68
- The `git` executable:
69
-
70
- ```ruby
71
- # git
72
- #!/usr/bin/env ruby
73
- require 'mister_bin'
74
-
75
- runner = MisterBin::Runner.new 'git', basedir: __dir__
76
- exitcode = runner.run ARGV
77
- ```
78
-
79
- The file to handle the `git push` command:
80
-
81
- ```ruby
82
- # git-push.rb
83
- version "0.1.0"
84
- help "Push git repo"
85
-
86
- usage "git push [--all]"
87
-
88
- option "--all", "Push all branches"
89
-
90
- action do |args|
91
- if args['--all']
92
- puts "pushing all"
93
- else
94
- puts "pushing, some..."
95
- end
96
- end
97
- ```
98
-
99
- Mister Bin also provides support for secondary subcommands. For example,
100
- if you want to create a command line that responds to the below commands:
101
-
102
- ```
103
- $ git status
104
- $ git repo create
105
- $ git repo delete
106
- ```
107
-
108
- You will need to create these files:
109
-
110
- 1. `git`
111
- 2. `git-status.rb`
112
- 3. `git-repo-create.rb`
113
- 4. `git-repo-delete.rb`
114
-
115
- Alternatively, if you prefer to handle all `repo` subcommands in a single
116
- file, simply implement these instead:
117
-
118
- 1. `git`
119
- 2. `git-status.rb`
120
- 3. `git-repo.rb`
61
+ 1. The main "bin" file. This is the actual executable, and if you are
62
+ developing a gem, this will be in the `bin` directory of your folder.
63
+ 2. One or more subcommand files. These files use the DSL, and will usually be
64
+ placed in your `lib/<your gem>/commands folder.
121
65
 
122
66
 
123
67
 
@@ -130,35 +74,23 @@ Bin with options.
130
74
  This is the minimal code:
131
75
 
132
76
  ```ruby
133
- # git
134
77
  #!/usr/bin/env ruby
135
- require 'mister_bin'
78
+ runner = MisterBin::Runner.new
79
+
80
+ runner.route 'dir', to: DirCommand
81
+ runner.route 'greet', to: GreetCommand
136
82
 
137
- runner = MisterBin::Runner.new 'appname'
138
- exitcode = runner.run ARGV
139
- exit exitcode
83
+ exit runner.run ARGV
140
84
  ```
141
85
 
142
86
  ### Runner Options
143
87
 
144
- The `Runner` method requires only the name of the main executable:
88
+ The `Runner` object accepts an optional hash of options:
145
89
 
146
90
  ```ruby
147
- runner = MisterBin::Runner.new 'appname'
148
- ```
149
-
150
- In addition, you can provide an options hash:
151
-
152
- ```ruby
153
- options = {
91
+ runner = MisterBin::Runner.new version: '1.2.3',
154
92
  header: 'My command line app'
155
- version: '1.2.3',
156
93
  footer: 'Use --help for additional info',
157
- basedir: __dir__,
158
- isolate: true
159
- }
160
-
161
- runner = MisterBin::Runner.new 'appname', options
162
94
  ```
163
95
 
164
96
  #### `version`
@@ -173,32 +105,52 @@ Text to display before the list of commands.
173
105
 
174
106
  Text to display after the list of commands.
175
107
 
176
- #### `basedir`
177
108
 
178
- The directory that holds the command files.
109
+ ### Runner Routes
110
+
111
+ The `Runner` object needs to be told how to route commands that are executed
112
+ in the command line.
179
113
 
180
- By default, the runner will look for its command files in all the `PATH`
181
- directories. You may add another directory to this search path by specifying
182
- it with `basedir`
114
+ Use the `#route` method as follows:
183
115
 
184
- #### `isolate`
116
+ ```ruby
117
+ runner = MisterBin::Runner.new
118
+ runner.route <regex>, to: <Class Name>
119
+ ```
120
+
121
+ for example:
185
122
 
186
- If you wish to prevent runner from searching in the `PATH` directories,
187
- specify `isolate: true`
123
+ ```ruby
124
+ runner = MisterBin::Runner.new
125
+ runner.route 'dir', to: DirCommand
126
+ runner.route 'greet', to: GreetCommand
127
+ runner.route 'config init', to: ConfigInitializerCommand
128
+ runner.route 'config show', to: ConfigDisplayerCommand
129
+ ```
188
130
 
189
131
 
190
132
  Creating Commands
191
133
  --------------------------------------------------
192
134
 
193
- When the main executable is executed, it will look for files matching a
194
- specific pattern in the same directory and in the `PATH`.
135
+ Create command classes by in heriting from `MisterBin::Command`, for example:
195
136
 
196
- Assuming the main executable is called `myapp`, it will look for
197
- `myapp-*.rb` files (e.g. `myapp-status.rb`)
137
+ ```ruby
138
+ require 'mister_bin'
139
+
140
+ class GreetCommand < MisterBin::Command
141
+ summary "Say hi"
142
+ usage "app greet [NAME]"
143
+ param "NAME", "The recipient of the greeting"
198
144
 
199
- These files do not need to be executables, and should use the DSL to define
200
- their actions.
145
+ def run(args)
146
+ name = args['NAME'] || 'Luke'
147
+ puts "#{name}... I am your father..."
148
+ end
149
+ end
150
+ ```
201
151
 
152
+ These classes can use any of the below DSL commands, and must define a
153
+ `def run(args)` method.
202
154
 
203
155
 
204
156
  ### Command DSL
data/lib/mister_bin.rb CHANGED
@@ -1,8 +1,5 @@
1
1
  require 'mister_bin/command'
2
- require 'mister_bin/commands'
3
2
  require 'mister_bin/docopt_maker'
4
- require 'mister_bin/path_helper'
5
3
  require 'mister_bin/runner'
6
- require 'mister_bin/script'
7
4
 
8
5
  require 'byebug' if ENV['BYEBUG']
@@ -1,25 +1,66 @@
1
+ require 'docopt'
2
+
1
3
  module MisterBin
2
4
  class Command
3
- attr_reader :command, :file, :type
5
+ class << self
6
+ def description
7
+ maker.summary || maker.help || ''
8
+ end
4
9
 
5
- def initialize(command, file)
6
- @command = command
7
- @file = file
8
- @type = command =~ / / ? :secondary : :primary
9
- end
10
+ def execute(argv=[])
11
+ args = Docopt.docopt docopt, version: maker.version, argv: argv
12
+ exitcode = new.run args
13
+ exitcode.is_a?(Numeric) ? exitcode : 0
10
14
 
11
- def run(argv=[])
12
- script.execute argv
13
- end
15
+ rescue Docopt::Exit => e
16
+ puts e.message
17
+ 1
18
+ end
14
19
 
15
- def metadata
16
- script.metadata
17
- end
20
+ # DSL
21
+
22
+ def summary(text)
23
+ maker.summary = text
24
+ end
25
+
26
+ def help(text)
27
+ maker.help = text
28
+ end
29
+
30
+ def version(text)
31
+ maker.version = text
32
+ end
33
+
34
+ def usage(text)
35
+ maker.usages << text
36
+ end
37
+
38
+ def option(flags, text)
39
+ maker.options << [flags, text]
40
+ end
41
+
42
+ def param(param, text)
43
+ maker.params << [param, text]
44
+ end
45
+
46
+ def example(text)
47
+ maker.examples << text
48
+ end
49
+
50
+ def environment(name, value)
51
+ maker.env_vars << [name, value]
52
+ end
53
+
54
+ protected
55
+
56
+ def maker
57
+ @maker ||= DocoptMaker.new
58
+ end
18
59
 
19
- private
60
+ def docopt
61
+ maker.docopt
62
+ end
20
63
 
21
- def script
22
- @script ||= Script.new file
23
64
  end
24
65
  end
25
- end
66
+ end
@@ -4,15 +4,17 @@ module MisterBin
4
4
  class Runner
5
5
  include Colsole
6
6
 
7
- attr_reader :name, :basedir, :header, :footer, :isolate, :version
7
+ attr_reader :header, :footer, :version, :commands
8
8
 
9
- def initialize(name, opts={})
10
- @name = name
9
+ def initialize(opts={})
11
10
  @header = opts[:header]
12
11
  @footer = opts[:footer]
13
- @isolate = opts[:isolate]
14
- @basedir = opts[:basedir]
15
12
  @version = opts[:version]
13
+ @commands = opts[:commands] || {}
14
+ end
15
+
16
+ def route(key, to: )
17
+ commands[key] = to
16
18
  end
17
19
 
18
20
  def run(argv=[])
@@ -29,18 +31,21 @@ module MisterBin
29
31
  private
30
32
 
31
33
  def execute(argv)
32
- command = commands.find argv[0], argv[1]
34
+ command = find_command argv
33
35
 
34
36
  if command
35
- execute_command command, argv
37
+ command.execute argv
36
38
  else
37
- puts "Unknown command: #{argv[0]}\n\n"
39
+ say "!txtred!Unknown command\n"
38
40
  show_subs
39
41
  end
40
42
  end
41
43
 
42
- def execute_command(command, argv)
43
- command.run argv
44
+ def find_command(argv)
45
+ argv_line = argv.join ' '
46
+ candidates = commands.keys.select { |c| argv_line =~ /^#{c}/ }
47
+ candidate = candidates.first
48
+ candidate ? commands[candidate] : nil
44
49
  end
45
50
 
46
51
  def show_subs
@@ -61,7 +66,7 @@ module MisterBin
61
66
 
62
67
  say "Commands:"
63
68
  commands.each do |key, command|
64
- summary = command.metadata[:summary] || ''
69
+ summary = command.description
65
70
  summary = summary[0..max_summary_size].strip
66
71
  say " !bldgrn!#{key.ljust longest_key} !txtrst!#{summary}"
67
72
  end
@@ -69,8 +74,5 @@ module MisterBin
69
74
  say "\n#{footer}" if footer
70
75
  end
71
76
 
72
- def commands
73
- @commands ||= Commands.new name, basedir, isolate: isolate
74
- end
75
77
  end
76
78
  end
@@ -1,3 +1,3 @@
1
1
  module MisterBin
2
- VERSION = "0.2.3"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mister_bin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Danny Ben Shitrit
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-18 00:00:00.000000000 Z
11
+ date: 2018-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colsole
@@ -173,11 +173,8 @@ files:
173
173
  - README.md
174
174
  - lib/mister_bin.rb
175
175
  - lib/mister_bin/command.rb
176
- - lib/mister_bin/commands.rb
177
176
  - lib/mister_bin/docopt_maker.rb
178
- - lib/mister_bin/path_helper.rb
179
177
  - lib/mister_bin/runner.rb
180
- - lib/mister_bin/script.rb
181
178
  - lib/mister_bin/version.rb
182
179
  homepage: https://github.com/dannyben/mister_bin
183
180
  licenses:
@@ -1,59 +0,0 @@
1
- require 'forwardable'
2
-
3
- module MisterBin
4
-
5
- # This class handles listing and finding command files
6
- class Commands
7
- extend Forwardable
8
-
9
- attr_reader :basename, :basedir, :isolate
10
- def_delegators :all, :[], :<<, :each, :size, :keys, :values, :empty?
11
-
12
- def initialize(basename, basedir='.', isolate: false)
13
- @basename = basename
14
- @basedir = basedir
15
- @isolate = isolate
16
- end
17
-
18
- def all
19
- @all ||= all!
20
- end
21
-
22
- def find(command, subcommand=nil)
23
- if subcommand and keys.include? "#{command} #{subcommand}"
24
- all["#{command} #{subcommand}"]
25
- else
26
- all["#{command}"]
27
- end
28
- end
29
-
30
- private
31
-
32
- def all!
33
- result = {}
34
- files.map do |file|
35
- if file =~ /#{basename}-([a-z]+)-([a-z]+)\.rb/
36
- command = "#{$1} #{$2}"
37
- elsif file =~ /#{basename}-([a-z]+)\.rb/
38
- command = $1
39
- end
40
- result[command] = Command.new command, file
41
- end
42
- result.sort.to_h
43
- end
44
-
45
- def files
46
- @files ||= path_helper.search("#{basename}-*.rb")
47
- end
48
-
49
- def path_helper
50
- @path_helper ||= path_helper!
51
- end
52
-
53
- def path_helper!
54
- helper = PathHelper.new(additional_dir: basedir)
55
- helper.paths = [basedir] if isolate
56
- helper
57
- end
58
- end
59
- end
@@ -1,30 +0,0 @@
1
- module MisterBin
2
-
3
- class PathHelper
4
- attr_reader :pathspec, :additional_dir
5
- attr_writer :paths
6
-
7
- def initialize(additional_dir: nil, pathspec: nil)
8
- @additional_dir = additional_dir
9
- @pathspec = pathspec || ENV['PATH']
10
- end
11
-
12
- def search(glob)
13
- result = []
14
- paths.each { |path| result += Dir["#{path}/#{glob}"] }
15
- result
16
- end
17
-
18
- def paths
19
- @paths ||= paths!
20
- end
21
-
22
- private
23
-
24
- def paths!
25
- result = pathspec.split File::PATH_SEPARATOR
26
- result.push additional_dir if additional_dir
27
- result
28
- end
29
- end
30
- end
@@ -1,101 +0,0 @@
1
- require 'docopt'
2
- require 'colsole'
3
-
4
- module MisterBin
5
- class Script
6
- # We are in cluding colsole to allow the evaluated script to use its
7
- # functions
8
- include Colsole
9
-
10
- attr_reader :file, :action_block
11
-
12
- def initialize(file)
13
- @file = file
14
- end
15
-
16
- def execute(argv=[])
17
- build_docopt
18
- execute! argv
19
- rescue Docopt::Exit => e
20
- puts e.message
21
- 1
22
- end
23
-
24
- def docopt
25
- maker.docopt
26
- end
27
-
28
- def metadata
29
- @metadata ||= metadata!
30
- end
31
-
32
- # DSL
33
-
34
- def summary(text)
35
- maker.summary = text
36
- end
37
-
38
- def help(text)
39
- maker.help = text
40
- end
41
-
42
- def version(text)
43
- maker.version = text
44
- end
45
-
46
- def usage(text)
47
- maker.usages << text
48
- end
49
-
50
- def option(flags, text)
51
- maker.options << [flags, text]
52
- end
53
-
54
- def param(param, text)
55
- maker.params << [param, text]
56
- end
57
-
58
- def example(text)
59
- maker.examples << text
60
- end
61
-
62
- def environment(name, value)
63
- maker.env_vars << [name, value]
64
- end
65
-
66
- def action(&block)
67
- @action_block = block
68
- end
69
-
70
- private
71
-
72
- def metadata!
73
- instance_eval script
74
-
75
- {
76
- summary: (maker.summary || maker.help),
77
- version: maker.version
78
- }
79
- end
80
-
81
- def execute!(argv)
82
- args = Docopt.docopt docopt, version: maker.version, argv: argv
83
- exitcode = action_block.call args if action_block
84
- exitcode.is_a?(Numeric) ? exitcode : 0
85
- end
86
-
87
- def build_docopt
88
- @maker = nil
89
- instance_eval script
90
- docopt
91
- end
92
-
93
- def maker
94
- @maker ||= DocoptMaker.new
95
- end
96
-
97
- def script
98
- @script ||= File.read file
99
- end
100
- end
101
- end