diff --git a/lib/puppet/application/string_base.rb b/lib/puppet/application/string_base.rb index bc627adde..762fbfda8 100644 --- a/lib/puppet/application/string_base.rb +++ b/lib/puppet/application/string_base.rb @@ -1,97 +1,94 @@ require 'puppet/application' require 'puppet/string' class Puppet::Application::StringBase < Puppet::Application should_parse_config run_mode :agent def preinit super trap(:INT) do $stderr.puts "Cancelling String" exit(0) end end option("--debug", "-d") do |arg| Puppet::Util::Log.level = :debug end option("--verbose", "-v") do Puppet::Util::Log.level = :info end option("--format FORMAT") do |arg| @format = arg.to_sym end option("--mode RUNMODE", "-r") do |arg| raise "Invalid run mode #{arg}; supported modes are user, agent, master" unless %w{user agent master}.include?(arg) self.class.run_mode(arg.to_sym) set_run_mode self.class.run_mode end attr_accessor :string, :type, :verb, :arguments, :format attr_writer :exit_code # This allows you to set the exit code if you don't want to just exit # immediately but you need to indicate a failure. def exit_code @exit_code || 0 end def main # Call the method associated with the provided action (e.g., 'find'). if result = string.send(verb, *arguments) puts render(result) end exit(exit_code) end # Override this if you need custom rendering. def render(result) render_method = Puppet::Network::FormatHandler.format(format).render_method if render_method == "to_pson" jj result exit(0) else result.send(render_method) end end def setup Puppet::Util::Log.newdestination :console + # We copy all of the app options to the end of the call; This allows each + # action to read in the options. This replaces the older model where we + # would invoke the action with options set as global state in the + # interface object. --daniel 2011-03-28 @verb = command_line.args.shift - @arguments = command_line.args - @arguments ||= [] - - @arguments = Array(@arguments) + @arguments = Array(command_line.args) << options @type = self.class.name.to_s.sub(/.+:/, '').downcase.to_sym # TODO: These should be configurable versions. unless Puppet::String.string?(@type, :current) raise "Could not find any version of string '#{@type}'" end @string = Puppet::String[@type, :current] @format ||= @string.default_format - # We copy all of the app options to the string. - # This allows each action to read in the options. - @string.options = options - validate end def validate unless verb raise "You must specify #{string.actions.join(", ")} as a verb; 'save' probably does not work right now" end unless string.action?(verb) raise "Command '#{verb}' not found for #{type}" end end end diff --git a/lib/puppet/string.rb b/lib/puppet/string.rb index 783b6afe0..04db1f33b 100644 --- a/lib/puppet/string.rb +++ b/lib/puppet/string.rb @@ -1,100 +1,100 @@ require 'puppet' require 'puppet/util/autoload' class Puppet::String require 'puppet/string/action_manager' require 'puppet/string/string_collection' include Puppet::String::ActionManager extend Puppet::String::ActionManager include Puppet::Util class << self # This is just so we can search for actions. We only use its # list of directories to search. # Can't we utilize an external autoloader, or simply use the $LOAD_PATH? -pvb def autoloader @autoloader ||= Puppet::Util::Autoload.new(:application, "puppet/string") end def strings Puppet::String::StringCollection.strings end def string?(name, version) Puppet::String::StringCollection.string?(name, version) end def register(instance) Puppet::String::StringCollection.register(instance) end def define(name, version, &block) if string?(name, version) string = Puppet::String::StringCollection[name, version] else string = self.new(name, version) Puppet::String::StringCollection.register(string) string.load_actions end string.instance_eval(&block) if block_given? return string end alias :[] :define end attr_accessor :default_format def set_default_format(format) self.default_format = format.to_sym end - attr_accessor :type, :verb, :version, :arguments, :options + attr_accessor :type, :verb, :version, :arguments attr_reader :name def initialize(name, version, &block) unless Puppet::String::StringCollection.validate_version(version) raise ArgumentError, "Cannot create string with invalid version number '#{version}'!" end @name = Puppet::String::StringCollection.underscorize(name) @version = version @default_format = :pson instance_eval(&block) if block_given? end # Try to find actions defined in other files. def load_actions path = "puppet/string/#{name}" loaded = [] [path, "#{name}@#{version}/#{path}"].each do |path| Puppet::String.autoloader.search_directories.each do |dir| fdir = ::File.join(dir, path) next unless FileTest.directory?(fdir) Dir.chdir(fdir) do Dir.glob("*.rb").each do |file| aname = file.sub(/\.rb/, '') if loaded.include?(aname) Puppet.debug "Not loading duplicate action '#{aname}' for '#{name}' from '#{fdir}/#{file}'" next end loaded << aname Puppet.debug "Loading action '#{aname}' for '#{name}' from '#{fdir}/#{file}'" require "#{Dir.pwd}/#{aname}" end end end end end def to_s "Puppet::String[#{name.inspect}, #{version.inspect}]" end end diff --git a/spec/unit/application/string_base_spec.rb b/spec/unit/application/string_base_spec.rb index 86f9c09aa..65cadb8fd 100755 --- a/spec/unit/application/string_base_spec.rb +++ b/spec/unit/application/string_base_spec.rb @@ -1,74 +1,75 @@ #!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb') require 'puppet/application/string_base' require 'tmpdir' class Puppet::Application::StringBase::Basetest < Puppet::Application::StringBase + option("--[no-]foo") end describe Puppet::Application::StringBase do before :all do @dir = Dir.mktmpdir $LOAD_PATH.push(@dir) FileUtils.mkdir_p(File.join @dir, 'puppet', 'string') File.open(File.join(@dir, 'puppet', 'string', 'basetest.rb'), 'w') do |f| f.puts "Puppet::String.define(:basetest, '0.0.1')" end end after :all do FileUtils.remove_entry_secure @dir $LOAD_PATH.pop end before do @app = Puppet::Application::StringBase::Basetest.new @app.stubs(:exit) @app.stubs(:puts) Puppet::Util::Log.stubs(:newdestination) end describe "when calling main" do before do @app.verb = :find @app.arguments = ["myname", "myarg"] @app.string.stubs(:find) end it "should send the specified verb and name to the string" do @app.string.expects(:find).with("myname", "myarg") @app.main end it "should use its render method to render any result" it "should exit with the current exit code" end describe "during setup" do before do @app.command_line.stubs(:args).returns(["find", "myname", "myarg"]) @app.stubs(:validate) end it "should set the verb from the command line arguments" do @app.setup @app.verb.should == "find" end it "should make sure arguments are an array" do @app.command_line.stubs(:args).returns(["find", "myname", "myarg"]) @app.setup - @app.arguments.should == ["myname", "myarg"] + @app.arguments.should == ["myname", "myarg", {}] end - it "should set the options on the string" do - @app.options[:foo] = "bar" + it "should pass options as the last argument" do + @app.command_line.stubs(:args).returns(["find", "myname", "myarg", "--foo"]) + @app.parse_options @app.setup - - @app.string.options.should == @app.options + @app.arguments.should == ["myname", "myarg", { :foo => true }] end end end diff --git a/spec/unit/string/action_spec.rb b/spec/unit/string/action_spec.rb index caf3291b6..f4ca8316d 100755 --- a/spec/unit/string/action_spec.rb +++ b/spec/unit/string/action_spec.rb @@ -1,66 +1,66 @@ #!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb') require 'puppet/string/action' describe Puppet::String::Action do describe "when validating the action name" do [nil, '', 'foo bar', '-foobar'].each do |input| it "should treat #{input.inspect} as an invalid name" do - expect { Puppet::Interface::Action.new(nil, input) }. + expect { Puppet::String::Action.new(nil, input) }. should raise_error(/is an invalid action name/) end end end describe "when invoking" do it "should be able to call other actions on the same object" do string = Puppet::String.new(:my_string, '0.0.1') do action(:foo) do invoke { 25 } end action(:bar) do invoke { "the value of foo is '#{foo}'" } end end string.foo.should == 25 string.bar.should == "the value of foo is '25'" end # bar is a class action calling a class action # quux is a class action calling an instance action # baz is an instance action calling a class action # qux is an instance action calling an instance action it "should be able to call other actions on the same object when defined on a class" do class Puppet::String::MyStringBaseClass < Puppet::String action(:foo) do invoke { 25 } end action(:bar) do invoke { "the value of foo is '#{foo}'" } end action(:quux) do invoke { "qux told me #{qux}" } end end string = Puppet::String::MyStringBaseClass.new(:my_inherited_string, '0.0.1') do action(:baz) do invoke { "the value of foo in baz is '#{foo}'" } end action(:qux) do invoke { baz } end end string.foo.should == 25 string.bar.should == "the value of foo is '25'" string.quux.should == "qux told me the value of foo in baz is '25'" string.baz.should == "the value of foo in baz is '25'" string.qux.should == "the value of foo in baz is '25'" end end end