bales 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -1
- data/lib/bales/application.rb +6 -36
- data/lib/bales/command.rb +56 -62
- data/lib/bales/version.rb +1 -1
- metadata +1 -2
- data/lib/bales/#command.rb# +0 -196
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58dd5ac6bd3070bc03dc5c84db2f92c394a43fd2
|
4
|
+
data.tar.gz: dac3a1f876ae148806de122ceca026ac39db55ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 650893c15923467acaa65cb97aad581b66b8cc1dcb037f74cb9013c54887cefa2d41f39897ca06a9823a6517dc24e0caffef08862d600daf8d751be0dff97484
|
7
|
+
data.tar.gz: 4b1f685e20a94a1012ce13ea5ec1152449accd0515c452cf02c4fbdb69a83b407df67b1ad672ba540f6dd801501754a675070b4868400a46eeadb057203aa8be
|
data/README.md
CHANGED
@@ -70,12 +70,27 @@ John has been smacked with a fish.
|
|
70
70
|
|
71
71
|
## So how does it work?
|
72
72
|
|
73
|
-
|
73
|
+
* Come up with a name for your app, like `MyApp`
|
74
|
+
* Create an `Application` class under that namespace which inherits from `Bales::Application`
|
75
|
+
* Create a `Command` class under that namespace which inherits from `Bales::Command`
|
76
|
+
* Give that `Command` an `action`, which will be what your application does by default if no valid subcommands are passed to it
|
77
|
+
* (Optional) Create one or more classes under the `MyApp::Command` namespace, inheriting from some subclass of `Bales::Command` (including the base command you defined previously), if you want some git-style or rails-style subcommands.
|
78
|
+
|
79
|
+
Basically, a Bales app is just a bunch of classes with some fairy dust that turns them into runnable commands. Bales will check the namespace that your subclass of `Bales::Application` lives in for a `Command` namespace, then search there for available commands.
|
74
80
|
|
75
81
|
The application has (or *will* have, more precisely; I don't have a whole lot for you on this front just yet) a few available DSL-ish functions for you to play with.
|
76
82
|
|
77
83
|
* `version`: sets your app's version number. If you use semantic versioning, you can query this with the `major_version`, `minor_version`, and `patch_level` class methods.
|
78
84
|
|
85
|
+
Meanwhile, commands *also* have some DSL-ish functions to play around with.
|
86
|
+
|
87
|
+
* `option`: defines a command-line option, like `--verbose` or `-f` or something. It takes the name of the option (which becomes a key in your command's options hash) and some named parameters:
|
88
|
+
* `:type`: a valid Ruby class, like `String`. For a boolean, you should provide either `TrueClass` or `FalseClass`, which - when set - will set the option in question to `true` or `false` (respectively).
|
89
|
+
* `:short_form`: a short flag, like `'-v'`. You must specify this if you want a short flag.
|
90
|
+
* `:long_form`: a long flag, like `'--verbose'`. This will be created from the option's name if you don't override it here.
|
91
|
+
* `:description`: a quick description of the option, like `"Whether or not to be verbose"`.
|
92
|
+
* `action`: defines what the command should do when it's called. This is provided in the form of a block. Said block should accept two arguments (an array of arguments and a hash of options), though you don't *have* to name them with pipes and stuff if you know that your command won't take any arguments or options.
|
93
|
+
|
79
94
|
## What kind of a silly names is "Bales", anyway?
|
80
95
|
|
81
96
|
It's shamelessly stolen^H^H^H^H^H^Hborrowed from Jason R. Clark's "Testing the Multiverse" talk at Ruby on Ales 2015 (which, if you haven't watched, you [totally should](http://confreaks.tv/videos/roa2015-testing-the-multiverse)). Sorry, Jason. Hope you don't mind.
|
data/lib/bales/application.rb
CHANGED
@@ -2,26 +2,16 @@ require 'bales/command'
|
|
2
2
|
|
3
3
|
##
|
4
4
|
# Base class for Bales apps. Your command-line program should create a
|
5
|
-
# subclass of this, then call said subclass'
|
5
|
+
# subclass of this, then call said subclass' +#parse_and_run+ instance
|
6
6
|
# method, like so:
|
7
7
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# end
|
8
|
+
# class MyApp::Application < Bales::Application
|
9
|
+
# # insert customizations here
|
10
|
+
# end
|
12
11
|
#
|
13
|
-
#
|
14
|
-
# ```
|
12
|
+
# MyApp::Application.parse_and_run
|
15
13
|
module Bales
|
16
14
|
class Application
|
17
|
-
def self.default_command
|
18
|
-
@default_command ||= Bales::Command::Help
|
19
|
-
@default_command
|
20
|
-
end
|
21
|
-
def self.default_command=(command)
|
22
|
-
@default_command = command
|
23
|
-
end
|
24
|
-
|
25
15
|
##
|
26
16
|
# Set or retrieve the application's version number. Defaults to "0.0.0".
|
27
17
|
def self.version(v="0.0.0")
|
@@ -51,7 +41,7 @@ module Bales
|
|
51
41
|
|
52
42
|
##
|
53
43
|
# Runs the specified command (should be a valid class; preferably, should
|
54
|
-
# be a subclass of Bales::Command). Takes a list of positional args
|
44
|
+
# be a subclass of +Bales::Command+). Takes a list of positional args
|
55
45
|
# followed by named options.
|
56
46
|
def self.run(command, *args, **opts)
|
57
47
|
command.run *args, **opts
|
@@ -78,26 +68,6 @@ module Bales
|
|
78
68
|
|
79
69
|
private
|
80
70
|
|
81
|
-
# def self.parse_command_name(argv)
|
82
|
-
# command_name_parts = [*constant_to_args(base_name), "command"]
|
83
|
-
# puts command_name_parts
|
84
|
-
# argv.each do |arg|
|
85
|
-
# break if arg.match(/^-/)
|
86
|
-
# begin
|
87
|
-
# test = args_to_constant [*command_name_parts, arg]
|
88
|
-
# rescue NameError
|
89
|
-
# break
|
90
|
-
# end
|
91
|
-
# if eval("defined? #{test}") == "constant"
|
92
|
-
# command_name_parts.push argv.shift
|
93
|
-
# else
|
94
|
-
# break
|
95
|
-
# end
|
96
|
-
# end
|
97
|
-
# command = args_to_constant [*command_name_parts]
|
98
|
-
# return command, argv
|
99
|
-
# end
|
100
|
-
|
101
71
|
def self.parse_command_name(argv)
|
102
72
|
command_name_parts = [*constant_to_args(base_name), "command"]
|
103
73
|
depth = 0
|
data/lib/bales/command.rb
CHANGED
@@ -4,52 +4,50 @@ require 'optparse'
|
|
4
4
|
# Base class for all Bales commands. Subclass this class to create your
|
5
5
|
# own command, like so:
|
6
6
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# end
|
12
|
-
# end # produces a `my-app hello` command that prints "Hello, world!"
|
13
|
-
# ```
|
7
|
+
# class MyApp::Command::Hello < Bales::Command
|
8
|
+
# def self.run(*args, **opts)
|
9
|
+
# puts "Hello, world!"
|
10
|
+
# end
|
11
|
+
# end # produces a `my-app hello` command that prints "Hello, world!"
|
14
12
|
#
|
15
13
|
# Note that the above will accept any number of arguments (including none
|
16
14
|
# at all!). If you want to change this behavior, change `self.run`'s
|
17
15
|
# signature, like so:
|
18
16
|
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
17
|
+
# class MyApp::Command::Smack < Bales::Command
|
18
|
+
# def self.run(target, **opts)
|
19
|
+
# puts "#{target} has been smacked with a large trout"
|
20
|
+
# end
|
23
21
|
# end
|
24
|
-
# end
|
25
|
-
# ```
|
26
22
|
#
|
27
23
|
# Subcommands are automatically derived from namespacing, like so:
|
28
24
|
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# end
|
34
|
-
# end # produces `my-app foo bar`
|
35
|
-
# ```
|
25
|
+
# class MyApp::Command::Foo::Bar < Bales::Command
|
26
|
+
# def self.run(*args, **opts)
|
27
|
+
# # ...
|
28
|
+
# end
|
29
|
+
# end # produces `my-app foo bar`
|
36
30
|
#
|
37
31
|
# Camel-cased command classes can be accessed using either hyphenation or
|
38
32
|
# underscores, like so:
|
39
33
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
# ```
|
34
|
+
# class MyApp::Command::FooBarBaz < Bales::Command
|
35
|
+
# # ...
|
36
|
+
# end
|
37
|
+
# # valid result: "my-app foo-bar-baz"
|
38
|
+
# # also valid: "my-app foo_bar_baz"
|
39
|
+
#
|
47
40
|
module Bales
|
48
41
|
class Command
|
42
|
+
##
|
43
|
+
# Accessor for the options hash generated by +#option+.
|
49
44
|
def self.options
|
50
45
|
@options ||= {}
|
51
46
|
@options
|
52
47
|
end
|
48
|
+
##
|
49
|
+
# Setter for the options hash generated by +#option+. Usually not
|
50
|
+
# needed, since that's what +#option+ is for.
|
53
51
|
def self.options=(new)
|
54
52
|
@options = new
|
55
53
|
end
|
@@ -59,13 +57,11 @@ module Bales
|
|
59
57
|
# block, which should accept an array of arguments and a hash of options.
|
60
58
|
# For example:
|
61
59
|
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
60
|
+
# class MyApp::Hello < Bales::Command
|
61
|
+
# action do |args, opts|
|
62
|
+
# puts "Hello, world!"
|
63
|
+
# end
|
66
64
|
# end
|
67
|
-
# end
|
68
|
-
# ```
|
69
65
|
def self.action(&code)
|
70
66
|
@action = code
|
71
67
|
end
|
@@ -78,41 +74,39 @@ module Bales
|
|
78
74
|
# Defines a named option that the command will accept, along with some
|
79
75
|
# named arguments:
|
80
76
|
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
# `:long_form` (optional)
|
86
|
-
# : A longhand flag to use for the option (like `--verbose`). This is
|
87
|
-
# derived from the name of the option if not specified. This should be
|
88
|
-
# a string, like `"--verbose"`
|
77
|
+
# [+:short_form+ (optional)] A shorthand flag to use for the option
|
78
|
+
# (like +-v+). This should be a string, like
|
79
|
+
# +"-v"+.
|
89
80
|
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
81
|
+
# [+:long_form+ (optional)] A longhand flag to use for the option (like
|
82
|
+
# +--verbose+). This is derived from the name
|
83
|
+
# of the option if not specified. This should
|
84
|
+
# be a string, like +"--verbose"+
|
93
85
|
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
86
|
+
# [+:type+ (optional)] The type that this option represents.
|
87
|
+
# Defaults to +TrueClass+. Should be a valid
|
88
|
+
# class name, like +String+ or +Integer+
|
97
89
|
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
90
|
+
# A special note on boolean options: if you
|
91
|
+
# want your boolean to default to `true`, set
|
92
|
+
# +:type+ to +TrueClass+. Likewise, if you
|
93
|
+
# want it to default to +false+, set +:type+
|
94
|
+
# to +FalseClass+.
|
103
95
|
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
96
|
+
# [+:arg+ (optional)] The name of the argument this option
|
97
|
+
# accepts. This should be a symbol (like
|
98
|
+
# :level) or +false+ (if the option is a
|
99
|
+
# boolean flag). Defaults to the name of the
|
100
|
+
# option or (if the option's +:type+ is
|
101
|
+
# +TrueClass+ or +FalseClass+) +false+.
|
108
102
|
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
103
|
+
# [+:required+ (optional)] Whether or not the option is required. This
|
104
|
+
# should be a boolean (+true+ or +false+).
|
105
|
+
# Default is `false`.
|
112
106
|
#
|
113
|
-
# Aside from the hash of option-options,
|
107
|
+
# Aside from the hash of option-options, +option+ takes a single +name+
|
114
108
|
# argument, which should be a symbol representing the name of the option
|
115
|
-
# to be set, like
|
109
|
+
# to be set, like +:verbose+.
|
116
110
|
def self.option(name, **opts)
|
117
111
|
name = name.to_sym
|
118
112
|
opts[:long_form] ||= "--#{name.to_s}".gsub("_","-")
|
@@ -138,7 +132,7 @@ module Bales
|
|
138
132
|
##
|
139
133
|
# Takes an ARGV-like array and returns a hash of options and what's left
|
140
134
|
# of the original array. This is rarely needed for normal use, but is
|
141
|
-
# an integral part of how a Bales::Application parses the ARGV it
|
135
|
+
# an integral part of how a +Bales::Application+ parses the ARGV it
|
142
136
|
# receives.
|
143
137
|
#
|
144
138
|
# Normally, this should be perfectly fine to leave alone, but if you
|
data/lib/bales/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bales
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan S. Northrup
|
@@ -20,7 +20,6 @@ files:
|
|
20
20
|
- README.md
|
21
21
|
- lib/bales.rb
|
22
22
|
- lib/bales.rb~
|
23
|
-
- lib/bales/#command.rb#
|
24
23
|
- lib/bales/application.rb
|
25
24
|
- lib/bales/application.rb~
|
26
25
|
- lib/bales/command.rb
|
data/lib/bales/#command.rb#
DELETED
@@ -1,196 +0,0 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
|
3
|
-
##
|
4
|
-
# Base class for all Bales commands. Subclass this class to create your
|
5
|
-
# own command, like so:
|
6
|
-
#
|
7
|
-
# ```ruby
|
8
|
-
# class MyApp::Command::Hello < Bales::Command
|
9
|
-
# def self.run(*args, **opts)
|
10
|
-
# puts "Hello, world!"
|
11
|
-
# end
|
12
|
-
# end # produces a `my-app hello` command that prints "Hello, world!"
|
13
|
-
# ```
|
14
|
-
#
|
15
|
-
# Note that the above will accept any number of arguments (including none
|
16
|
-
# at all!). If you want to change this behavior, change `self.run`'s
|
17
|
-
# signature, like so:
|
18
|
-
#
|
19
|
-
# ```ruby
|
20
|
-
# class MyApp::Command::Smack < Bales::Command
|
21
|
-
# def self.run(target, **opts)
|
22
|
-
# puts "#{target} has been smacked with a large trout"
|
23
|
-
# end
|
24
|
-
# end
|
25
|
-
# ```
|
26
|
-
#
|
27
|
-
# Subcommands are automatically derived from namespacing, like so:
|
28
|
-
#
|
29
|
-
# ```ruby
|
30
|
-
# class MyApp::Command::Foo::Bar < Bales::Command
|
31
|
-
# def self.run(*args, **opts)
|
32
|
-
# # ...
|
33
|
-
# end
|
34
|
-
# end # produces `my-app foo bar`
|
35
|
-
# ```
|
36
|
-
#
|
37
|
-
# Camel-cased command classes can be accessed using either hyphenation or
|
38
|
-
# underscores, like so:
|
39
|
-
#
|
40
|
-
# ```ruby
|
41
|
-
# class MyApp::Command::FooBarBaz < Bales::Command
|
42
|
-
# # ...
|
43
|
-
# end
|
44
|
-
# # valid result: "my-app foo-bar-baz"
|
45
|
-
# # also valid: "my-app foo_bar_baz"
|
46
|
-
# ```
|
47
|
-
module Bales
|
48
|
-
class Command
|
49
|
-
def self.options
|
50
|
-
@options ||= {}
|
51
|
-
@options
|
52
|
-
end
|
53
|
-
def self.options=(new)
|
54
|
-
@options = new
|
55
|
-
end
|
56
|
-
|
57
|
-
##
|
58
|
-
# Assigns an action to this command. Said action is represented as a
|
59
|
-
# block, which should accept an array of arguments and a hash of options.
|
60
|
-
# For example:
|
61
|
-
#
|
62
|
-
# ```ruby
|
63
|
-
# class MyApp::Hello < Bales::Command
|
64
|
-
# action do |args, opts|
|
65
|
-
# puts "Hello, world!"
|
66
|
-
# end
|
67
|
-
# end
|
68
|
-
# ```
|
69
|
-
def self.action(&code)
|
70
|
-
@action = code
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.run(*args, **opts)
|
74
|
-
@action.call(args, opts) unless @action.nil?
|
75
|
-
end
|
76
|
-
|
77
|
-
##
|
78
|
-
# Defines a named option that the command will accept, along with some
|
79
|
-
# named arguments:
|
80
|
-
#
|
81
|
-
# `:short_form` (optional)
|
82
|
-
# : A shorthand flag to use for the option (like `-v`). This should be a
|
83
|
-
# string, like `"-v"`.
|
84
|
-
#
|
85
|
-
# `:long_form` (optional)
|
86
|
-
# : A longhand flag to use for the option (like `--verbose`). This is
|
87
|
-
# derived from the name of the option if not specified. This should be
|
88
|
-
# a string, like `"--verbose"`
|
89
|
-
#
|
90
|
-
# `:type` (optional)
|
91
|
-
# : The type that this option represents. Defaults to `TrueClass`.
|
92
|
-
# Should be a valid class name, like `String` or `Integer`
|
93
|
-
#
|
94
|
-
# A special note on boolean options: if you want your boolean to
|
95
|
-
# default to `true`, set `:type` to `TrueClass`. Likewise, if you want
|
96
|
-
# it to default to `false`, set `:type` to `FalseClass`.
|
97
|
-
#
|
98
|
-
# `:arg` (optional)
|
99
|
-
# : The name of the argument this option accepts. This should be a
|
100
|
-
# symbol (like :level) or `false` (if the option is a boolean flag).
|
101
|
-
# Defaults to the name of the option or (if the option's `:type` is
|
102
|
-
# `TrueClass` or `FalseClass`) `false`.
|
103
|
-
#
|
104
|
-
# If this is an array, and `:type` is set to `Enumerable` or some
|
105
|
-
# subclass thereof, this will instead be interpreted as a list of
|
106
|
-
# sample arguments during option parsing. It's recommended you set
|
107
|
-
# this accordingly if `:type` is `Enumerable` or any of its subclasses.
|
108
|
-
#
|
109
|
-
# `:required` (optional)
|
110
|
-
# : Whether or not the option is required. This should be a boolean
|
111
|
-
# (`true` or `false`). Default is `false`.
|
112
|
-
#
|
113
|
-
# Aside from the hash of option-options, `option` takes a single `name`
|
114
|
-
# argument, which should be a symbol representing the name of the option
|
115
|
-
# to be set, like `:verbose`.
|
116
|
-
def self.option(name, **opts)
|
117
|
-
name = name.to_sym
|
118
|
-
opts[:long_form] ||= "--#{name.to_s}".gsub("_","-")
|
119
|
-
|
120
|
-
opts[:type] = String if opts[:type].nil?
|
121
|
-
|
122
|
-
unless opts[:type].is_a? Class
|
123
|
-
raise ArgumentError, ":type option should be a valid class"
|
124
|
-
end
|
125
|
-
|
126
|
-
if (opts[:type].ancestors & [TrueClass, FalseClass]).empty?
|
127
|
-
opts[:arg] ||= name
|
128
|
-
end
|
129
|
-
|
130
|
-
opts[:default] = false if opts[:type].ancestors.include? TrueClass
|
131
|
-
opts[:default] = true if opts[:type].ancestors.include? FalseClass
|
132
|
-
|
133
|
-
result = options
|
134
|
-
result[name] = opts
|
135
|
-
options = result
|
136
|
-
end
|
137
|
-
|
138
|
-
##
|
139
|
-
# Takes an ARGV-like array and returns a hash of options and what's left
|
140
|
-
# of the original array. This is rarely needed for normal use, but is
|
141
|
-
# an integral part of how a Bales::Application parses the ARGV it
|
142
|
-
# receives.
|
143
|
-
#
|
144
|
-
# Normally, this should be perfectly fine to leave alone, but if you
|
145
|
-
# prefer to define your own parsing method (e.g. if you want to specify
|
146
|
-
# an alternative format for command-line options, or you are otherwise
|
147
|
-
# dissatisfied with the default approach of wrapping OptionParser), this
|
148
|
-
# is the method you'd want to override.
|
149
|
-
def self.parse_opts(argv)
|
150
|
-
optparser = OptionParser.new
|
151
|
-
result = {}
|
152
|
-
options.each do |name, opts|
|
153
|
-
result[name] = opts[:default]
|
154
|
-
parser_args = []
|
155
|
-
parser_args.push opts[:short_form] if opts[:short_form]
|
156
|
-
if (opts[:type].ancestors & [TrueClass,FalseClass]).empty?
|
157
|
-
argstring = opts[:arg].to_s.upcase
|
158
|
-
if opts[:required]
|
159
|
-
parser_args.push "#{opts[:long_form]} #{argstring}"
|
160
|
-
else
|
161
|
-
parser_args.push "#{opts[:long_form]} [#{argstring}]"
|
162
|
-
end
|
163
|
-
parser_args.push opts[:type]
|
164
|
-
else
|
165
|
-
parser_args.push opts[:long_form]
|
166
|
-
end
|
167
|
-
parser_args.push opts[:description]
|
168
|
-
|
169
|
-
if opts[:type].ancestors.include? FalseClass
|
170
|
-
optparser.on(*parser_args) do
|
171
|
-
result[name] = false
|
172
|
-
end
|
173
|
-
elsif opts[:type].ancestors.include? TrueClass
|
174
|
-
optparser.on(*parser_args) do
|
175
|
-
result[name] = true
|
176
|
-
end
|
177
|
-
else
|
178
|
-
optparser.on(*parser_args) do |value|
|
179
|
-
result[name] = value
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
optparser.parse! argv
|
185
|
-
return result, argv
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
##
|
191
|
-
# Default help command. You'll probably use your own...
|
192
|
-
class Bales::Command::Help < Bales::Command
|
193
|
-
action do |args, opts|
|
194
|
-
puts "This will someday output some help text"
|
195
|
-
end
|
196
|
-
end
|