lib/command_lion/app.rb in command_lion-1.0.4 vs lib/command_lion/app.rb in command_lion-2.0.0
- old
+ new
@@ -1,88 +1,7 @@
module CommandLion
- # The App class provides what can be considered the "main" function for the a Command Lion application.
- #
- # The App class is primarily used in one of two ways:
- #
- # == Building Block
- # To build an application using the DSL, but not run it right away, the build method block is available.
- # app = CommandLion::App.build do
- # # ...
- # end
- #
- # app.run!
- #
- # == Run Block
- # To build, parse, and run everything in one concise block, the run method block is available.
- # CommandLion::App.run do
- # # ...
- # end
- #
- # == DSL Keywords:
- # name::
- # The name of your application. This is how your application would be referenced in conversation.
- # It's also going to be used as the defualt banner for the application which will appear at the
- # top of the help menu.
- #
- # == Example
- # app = CommandLion::App.build do
- # name "Example"
- # end
- #
- # app.name?
- # # => true
- #
- # app.name = "Changed Name"
- # # => "Changed Name"
- #
- # app.name
- # # => Changed Name
- # usage::
- # Your usage string can be used to help show the basic information for how to use your application.
- # You can make this as simple or as complex as you like. One will be generated for you by default
- # when your application runs, but won't be pre-built for you inside the build block for now.
- #
- # == Example
- # app = CommandLion::App.build do
- # usage "example [commands] [options...]"
- # end
- #
- # app.usage?
- # # => true
- #
- # app.usage = <<USAGE
- # /|
- # ~~~/ |~
- # tsharky [command] [switches] [--] [arguments]
- # USAGE
- # # => " /|\n" + "~~~/ |~\n" + "tsharky [command] [switches] [--] [arguments]\n"
- #
- # app.usage
- # # => " /|\n" + "~~~/ |~\n" + "tsharky [command] [switches] [--] [arguments]\n"
- #
- # puts app.usage
- # # /|
- # # ~~~/ |~
- # # tsharky [command] [switches] [--] [arguments]
- # description::
- # To provide further context for your application's existence, it's fairly nice to have a description.
- # Like, the usage statement, this can be as complex or as simple as you would like. It isn't required either.
- #
- # == Example
- # app = CommandLion::App.build do
- # description "Example"
- # end
- #
- # app.description?
- # # => true
- #
- # app.description = "Changed"
- # # => "Changed"
- #
- # app.description
- # # => Changed
class App < Base
def self.default_help(app)
flagz = app.commands.map do |_, cmd|
if cmd.flags?
@@ -98,15 +17,15 @@
end
end
max_flag = flagz.map(&:length).max + 2
max_desc = app.commands.values.map(&:description).select{|d| d unless d.nil? }.map(&:length).max
puts app.name
- puts
if app.version?
+ puts
puts "VERSION"
puts app.version
- puts
+ puts unless app.description?
end
if app.description?
puts
puts "DESCRIPTION"
puts app.description
@@ -116,10 +35,11 @@
puts
puts "USAGE"
puts usage
puts
end
+ puts unless app.version? || app.description? || app.usage?
puts "COMMANDS"
app.commands.values.select { |cmd| cmd unless cmd.is_a? CommandLion::Option }.each do |command|
if command.flags?
short = command.flags.long? ? command.flags.short + ", " : command.flags.short
short_long = "#{short}#{command.flags.long}".ljust(max_flag)
@@ -140,16 +60,13 @@
end
puts
end
end
- # This run method is a pretty important method when using command lion typically.
- #
- # Under the hood, an application object is initialized. The block of code passed to
- # this method is then used as the code that is ran in the context of a application
- # object. So all of those methods will be available.
- #
+
+ # The run method will run a given block of code using the
+ # Commmand Lion DSL.
def self.run(&block)
# Initialize an instance of an App object.
app = new
# Evaluate the block of code within the context of that App object.
app.instance_eval(&block)
@@ -160,34 +77,29 @@
# Default to a help menu.
if cmd = app.commands[:help]
cmd.before.call if cmd.before?
cmd.action.call if cmd.action?
cmd.after.call if cmd.after?
- # maybe exit?
+ exit 0
else
# Use the default help menu for the application unless that's been
# explictly removed by the author for whatever reason.
default_help(app) unless app.default_help_menu_removed?
end
else
- threadz = false
app.commands.each do |_, cmd|
next unless cmd.given?
- if cmd.threaded?
- threadz = [] unless threadz
- threadz << Thread.new do
- cmd.before.call if cmd.before?
- cmd.action.call if cmd.action?
- cmd.after.call if cmd.after?
- end
- else
- cmd.before.call if cmd.before?
- cmd.action.call if cmd.action?
- cmd.after.call if cmd.after?
- end
+ cmd.options.each do |_, opt|
+ next unless opt.given?
+ opt.before.call if opt.before?
+ opt.action.call if opt.action?
+ opt.after.call if opt.after?
+ end if cmd.options?
+ cmd.before.call if cmd.before?
+ cmd.action.call if cmd.action?
+ cmd.after.call if cmd.after?
end
- threadz.map(&:join) if threadz
end
end
# Check if there has been an indexed help command.
def help?
@@ -203,23 +115,10 @@
# Check if the default help menu for the application has been explicitly removed.
def default_help_menu_removed?
@remove_default_help_menu || false
end
- # A tiny bit of rainbow magic is included. You can simple include
- # this option within your application and, if you have the `lolize` gem
- # installed, then rainbows will automagically be hooked to STDOUT to make your
- # application much prettier.
- #
- # It'd be funny if this was turned on by default and you had to opt-out of the
- # rainbows. Good thing I didn't do that, right?
- def rainbows
- require 'lolize/auto'
- rescue
- raise "The 'lolize' gem is not installed. Install it for rainbow magic!"
- end
-
# Simple attributes for the application. Mostly just metadata to help
# provide some context to the application.
#
# * `name` is the name people would refernce your application to in conversation.
# * `usage` is a simple, optional custom string to provide further context for the app.
@@ -276,15 +175,18 @@
end
@commands[cmd.index] = cmd
cmd
end
+ def ctrl_c(&block)
+ trap("SIGINT") { block.call }
+ end
+
def help(&block)
command :help, &block
end
-
# Plugin a command that's probably been built outside of the application's run or build block.
# This is helpful for sharing or reusing commands in applications.
# @param command [Command]
def plugin(command)
command(command)
@@ -295,11 +197,11 @@
@flags
end
# Direct access to the various commands an application has. Helpful for debugging.
def commands
- @commands
+ @commands.reject { |_, v| v.is_a? CommandLion::Option }
end
# Parse arguments off of ARGV.
#
# @TODO Re-visit this.
@@ -324,22 +226,24 @@
end
end
end
end
- # Parse a given command with its
+ # Parse a given command with its given flags.
# @TODO Re-visit this.
def parse_cmd(cmd, flags)
if cmd.flags?
args = Raw.arguments_to(cmd.flags.short, flags)
if args.nil? || args.empty?
args = Raw.arguments_to(cmd.flags.long, flags)
end
else
args = Raw.arguments_to(cmd.index.to_s, flags)
end
- return nil if args.nil?
+ unless cmd.type.to_s =~ /stdin/
+ return nil if args.nil?
+ end
case cmd.type
when :stdin
args = STDIN.gets.strip
when :stdin_stream
args = STDIN
@@ -372,13 +276,10 @@
args = args.first
when :strings, :multi
if cmd.delimiter?
if args.count > 1
args = args.first.split(cmd.delimiter)
- #args = args.first.join.split(cmd.delimiter).flatten.select { |arg| arg unless arg.empty? }
- #args = args.select { |arg| arg if arg.include?(cmd.delimiter) }
- #args = args.map { |arg| arg.split(cmd.delimiter) }.flatten
else
args = args.map { |arg| arg.split(cmd.delimiter) }.flatten
end
end
args
@@ -410,13 +311,21 @@
rescue => e# this is dangerous
puts e
nil
end
- # @TODO Re-visit this.
def run!
parse do |cmd|
- cmd.action.call
+ next unless cmd.given?
+ cmd.options.each do |_, opt|
+ next unless opt.given?
+ opt.before.call if opt.before?
+ opt.action.call if opt.action?
+ opt.after.call if opt.after?
+ end if cmd.options?
+ cmd.before.call if cmd.before?
+ cmd.action.call if cmd.action?
+ cmd.after.call if cmd.after?
end
end
# Run the application if the file is the main file being run.
# It's almost kind of narcisitc, if you think about it.