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.