Signed-off-by: Luke Kanies <[email protected]>
---
 lib/puppet/application.rb |  302 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 302 insertions(+), 0 deletions(-)
 create mode 100644 lib/puppet/application.rb

diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb
new file mode 100644
index 0000000..88ad9d4
--- /dev/null
+++ b/lib/puppet/application.rb
@@ -0,0 +1,302 @@
+require 'puppet'
+require 'optparse'
+
+# This class handles all the aspects of a Puppet application/executable
+# * setting up options
+# * setting up logs
+# * choosing what to run
+#
+# === Usage
+# The application is a Puppet::Application object that register itself in the 
list
+# of available application. Each application needs a +name+ and a getopt 
+options+
+# description array.
+#
+# The executable uses the application object like this:
+#      Puppet::Application[:example].run
+#
+# 
+# Puppet::Application.new(:example) do
+# 
+#     preinit do
+#         # perform some pre initialization
+#         @all = false
+#     end
+# 
+#     # dispatch is called to know to what command to call
+#     dispatch do
+#         ARGV.shift
+#     end
+#
+#     option("--arg ARGUMENT") do |v|
+#         @args << v
+#     end
+# 
+#     option("--debug", "-d") do |v|
+#         @debug = v
+#     end
+# 
+#     option("--all", "-a:) do |v|
+#         @all = v
+#     end
+#     
+#     unknown do |opt,arg|
+#         # last chance to manage an option
+#         ...
+#         # let's say to the framework we finally handle this option
+#         true
+#     end
+#
+#     command(:read) do
+#         # read action
+#     end
+# 
+#     command(:write) do
+#         # writeaction
+#     end
+# 
+# end
+#
+# === Preinit
+# The preinit block is the first code to be called in your application, before 
option parsing,
+# setup or command execution.
+#
+# === Options
+# Puppet::Application uses +OptionParser+ to manage the application options.
+# Options are defined with the +option+ method to which are passed various 
+# arguments, including the long option, the short option, a description...
+# Refer to +OptionParser+ documentation for the exact format.
+# * If the option method is given a block, this one will be called whenever
+# the option is encountered in the command-line argument.
+# * If the option method has no block, a default functionnality will be used, 
that
+# stores the argument (or true/false if the option doesn't require an 
argument) in
+# the global (to the application) options array.
+# * If a given option was not defined by a the +option+ method, but it exists 
as a Puppet settings:
+#  * if +unknown+ was used with a block, it will be called with the option 
name and argument
+#  * if +unknown+ wasn't used, then the option/argument is handed to 
Puppet.settings.handlearg for
+#    a default behavior
+#
+# --help is managed directly by the Puppet::Application class, but can be 
overriden.
+#
+# === Setup
+# Applications can use the setup block to perform any initialization.
+# The defaul +setup+ behaviour is to: read Puppet configuration and manage log 
level and destination
+#
+# === What and how to run
+# If the +dispatch+ block is defined it is called. This block should return 
the name of the registered command
+# to be run.
+# If it doesn't exist, it defaults to execute the +main+ command if defined.
+#
+class Puppet::Application
+    include Puppet::Util
+
+    @@applications = {}
+    class << self
+        include Puppet::Util
+    end
+
+    attr_reader :options, :opt_parser
+
+    def self.[](name)
+        name = symbolize(name)
+        @@applications[name]
+    end
+
+    def should_parse_config
+        @parse_config = true
+    end
+
+    def should_not_parse_config
+        @parse_config = false
+    end
+
+    def should_parse_config?
+        unless @parse_config.nil?
+            return @parse_config
+        end
+        @parse_config = true
+    end
+
+    # used to declare a new command
+    def command(name, &block)
+        meta_def(symbolize(name), &block)
+    end
+
+    # used as a catch-all for unknown option
+    def unknown(&block)
+        meta_def(:handle_unknown, &block)
+    end
+
+    # used to declare code that handle an option
+    def option(*options, &block)
+        long = options.find { |opt| opt =~ /^--/ }.gsub(/^--(?:\[no-\])?([^ 
=]+).*$/, '\1' ).gsub('-','_')
+        fname = "handle_#{long}"
+        if (block_given?)
+            meta_def(symbolize(fname), &block)
+        else
+            meta_def(symbolize(fname)) do |value|
+                self.options["#{long}".to_sym] = value
+            end
+        end
+        @opt_parser.on(*options) do |value|
+            self.send(symbolize(fname), value)
+        end
+    end
+
+    # used to declare accessor in a more natural way in the 
+    # various applications
+    def attr_accessor(*args)
+        args.each do |arg|
+            meta_def(arg) do
+                instance_variable_get("@#{arg}".to_sym)
+            end
+            meta_def("#{arg}=") do |value|
+                instance_variable_set("@#{arg}".to_sym, value)
+            end
+        end
+    end
+
+    # used to declare code run instead the default setup
+    def setup(&block)
+        meta_def(:run_setup, &block)
+    end
+
+    # used to declare code to choose which command to run
+    def dispatch(&block)
+        meta_def(:get_command, &block)
+    end
+
+    # used to execute code before running anything else
+    def preinit(&block)
+        meta_def(:run_preinit, &block)
+    end
+
+    def initialize(name, banner = nil, &block)
+        @opt_parser = OptionParser.new(banner)
+
+        name = symbolize(name)
+
+        init_default
+
+        @options = {}
+
+        instance_eval(&block) if block_given?
+
+        @@applications[name] = self
+    end
+
+    # initialize default application behaviour
+    def init_default
+        setup do 
+            default_setup
+        end
+
+        dispatch do
+            :main
+        end
+
+        # empty by default
+        preinit do
+        end
+
+        option("--version", "-V") do |arg|
+            puts "%s" % Puppet.version
+            exit
+        end
+
+        option("--help", "-h") do |v|
+            help
+        end
+    end
+
+    # This is the main application entry point
+    def run
+        run_preinit
+        parse_options
+        Puppet.settings.parse if should_parse_config?
+        run_setup
+        run_command
+    end
+
+    def main
+        raise NotImplementedError, "No valid command or main"
+    end
+
+    def run_command
+        if command = get_command() and respond_to?(command)
+            send(command)
+        else
+            main
+        end
+    end
+
+    def default_setup
+        # Handle the logging settings
+        if options[:debug] or options[:verbose]
+            Puppet::Util::Log.newdestination(:console)
+            if options[:debug]
+                Puppet::Util::Log.level = :debug
+            else
+                Puppet::Util::Log.level = :info
+            end
+        end
+
+        unless options[:setdest]
+            Puppet::Util::Log.newdestination(:syslog)
+        end
+    end
+
+    def parse_options
+        # get all puppet options
+        optparse_opt = []
+        optparse_opt = Puppet.settings.optparse_addargs(optparse_opt)
+
+        # convert them to OptionParser format
+        optparse_opt.each do |option|
+            @opt_parser.on(*option) do |arg|
+                handlearg(option[0], arg)
+            end
+        end
+
+        # scan command line argument
+        begin
+            @opt_parser.parse!
+        rescue OptionParser::ParseError => detail
+            $stderr.puts detail
+            $stderr.puts "Try '#{$0} --help'"
+            exit(1)
+        end
+    end
+
+    def handlearg(opt, arg)
+        # rewrite --[no-]option to --no-option if that's what was given
+        if opt =~ /\[no-\]/ and !arg
+            opt = opt.gsub(/\[no-\]/,'no-')
+        end
+        # otherwise remove the [no-] prefix to not confuse everybody
+        opt = opt.gsub(/\[no-\]/, '')
+        unless respond_to?(:handle_unknown) and send(:handle_unknown, opt, arg)
+            # Puppet.settings.handlearg doesn't handle direct true/false :-)
+            if arg.is_a?(FalseClass)
+                arg = "false"
+            elsif arg.is_a?(TrueClass)
+                arg = "true"
+            end
+            Puppet.settings.handlearg(opt, arg)
+        end
+    end
+
+    # this is used for testing
+    def self.exit(code)
+        exit(code)
+    end
+
+    def help
+        if Puppet.features.usage?
+            ::RDoc::usage && exit
+        else
+            puts "No help available unless you have RDoc::usage installed"
+            exit
+        end
+    end
+
+end
-- 
1.6.1


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to