-
Notifications
You must be signed in to change notification settings - Fork 1
Overview
Ryan S. Northrup edited this page Jul 27, 2015
·
3 revisions
#!/usr/bin/env ruby
# First, we define a namespace for our app. You *could*
# hypothetically skip this part and just define everything on the main
# namespace, but this makes things more clear.
module MyApp
# Next, we define an 'Application' class that inherits from
# Bales::Application
class Application < Bales::Application
# Now, we're in the heart of our bales app. Here, inside this
# class, we have full access to a DSL for creating our app. Let's
# start with defining our "root" command, which is what is called
# by default.
# First, we should define a version.
version "1.2.3"
# A summary is a short description of a command (or, in this case,
# our app as a whole). This is used in things like help text
# output.
summary "Ruby on Bales demo"
# A description is longer than a summary. Like summaries,
# descriptions are also used in helptext.
description %q( This program is a demonstration of Ruby on Bales, a
command-line application framework that combines simplicity, ease-of-use,
functionality, ergonomics, and all those other buzzwords that Ruby hackers
like to use when describing their own things. )
# Now we'll define our action. Not that this is a very exciting
# action, but it's good enough for now.
action do
puts "Hello, world!"
end
# "But Ryan!" I already hear you asking. "What if I want
# subcommands, like in Git's or Rails' command-line tools?" Well
# it's a good thing I already thought of that, isn't it?
command "foo" do
summary "A subcommand"
option :bar,
type: TrueClass,
description: "Whether or not I should bar",
short_form: '-b'
action do |bar:false|
if bar
puts "bar"
else
puts "foo"
end
end
end
# ...and with that, you now have a "foo" subcommand.
# Lastly, we wrap it all together with a call to `parse_and_run`,
# which is what actually causes our app to run
parse_and_run
end
end
# Meanwhile, let's step outside the above for a moment. You're
# probably wondering "well, how does this all work? Is it magic?
# Sufficiently-advanced technology? Are you a wizard?"
#
# Well, the answer to the latter-three questions is *obviously* yes,
# but I might as well give you a taste of the sorcery involved by
# showing you the end result. Here, let's create another subcommand,
# but this time *outside* our application's class.
class MyApp::Command::Bar < Bales::Command
# As you might be able to guess, that 'command "foo" do ... end' you
# saw earlier translates to a class called MyApp::Command::Foo,
# which inherits from Bales::Command. Meanwhile, the block is
# important, since it places you *inside the class definition of
# your command*. Don't believe me? Well, let's try some of those
# DSL methods we were using.
summary "foo bar baz"
description "a b c d e f g h i j k elemeno p q r s t u v w x y z"
option :baz,
type: String,
description: "the thing we should baz"
action do |foo, **opts|
if opts[:baz].nil?
puts "#{foo}"
else
puts "#{foo} #{opts[:baz]}"
end
end
# See? Seeing *is* believing, after all.
end
# But there's another level of magic that still hasn't really been
# uncovered yet. Let's define one last subcommand - again, using a
# direct class definition - but this time without using so many helper
# methods.
class MyApp::Command::Baz < Bales::Command
SUMMARY="baz"
DESCRIPTION="entirely-manually-defined subcommand"
OPTIONS={ foo: {
type: TrueClass,
description: "To foo or not to foo. That is the question."
}
}
def run(**opts)
if opts[:foo]
puts "foo"
else
puts "baz"
end
end
end
# There's still a bit of magic involved here (in terms of how
# MyApp::Application invokes the command, parses options, etc.). More
# details on that can be found in the RDoc-generated documentation.
#
# You can mix and match these styles to your heart's content. Have fun!