bales 0.0.1 → 0.0.2
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/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
|