lib/rye/box.rb in rye-0.6.3 vs lib/rye/box.rb in rye-0.6.4

- old
+ new

@@ -44,10 +44,13 @@ # The most recent value from Box.cd or Box.[] attr_reader :current_working_directory # The most recent valud for umask (or 0022) attr_reader :current_umask + attr_writer :post_command_hook + attr_writer :pre_command_hook + # * +host+ The hostname to connect to. The default is localhost. # * +opts+ a hash of optional arguments. # # The +opts+ hash excepts the following keys: # @@ -265,23 +268,18 @@ (@current_environment_variables ||= {})[n] = v self end alias :add_env :setenv # deprecated? - def user - (@opts || {})[:user] - end + # The name of the user that opened the SSH connection + def user; (@opts || {})[:user]; end # See Rye.keys - def keys - Rye.keys - end + def keys; Rye.keys; end - # Returns +@host+ - def to_s - @host - end + # Returns +user@host+ + def to_s; '%s@%s' % [user, @host]; end def inspect %q{#<%s:%s cwd=%s umask=%s env=%s safe=%s opts=%s>} % [self.class.to_s, self.host, @current_working_directory, @current_umask, @@ -299,11 +297,10 @@ def host_key raise "No host" unless @host Rye.remote_host_keys(@host) end - # Uses the output of "useradd -D" to determine the default home # directory. This returns a GUESS rather than the a user's real # home directory. Currently used only by authorize_keys_remote. def guess_user_home(other_user=nil) this_user = other_user || opts[:user] @@ -318,20 +315,23 @@ # This code will be abstracted out once I find a decent home for it. # /etc/default/useradd, HOME=/home OR useradd -D # /etc/adduser.config, DHOME=/home OR ?? user_defaults = {} raw = self.useradd(:D) rescue ["HOME=/home"] + ostmp = self.ostype raw.each do |nv| - if self.ostype == "sunos" + if ostmp == "sunos" #nv.scan(/([\w_-]+?)=(.+?)\s/).each do |n, v| # n = 'HOME' if n == 'basedir' # user_defaults[n.upcase] = v.strip #end # In Solaris, useradd -D says the default home path is /home # but that directory is not writable. See: http://bit.ly/IJDD0 user_defaults['HOME'] = '/export/home' + elsif ostmp == "darwin" + user_defaults['HOME'] = '/Users' else n, v = nv.scan(/\A([\w_-]+?)=(.+)\z/).flatten user_defaults[n] = v end end @@ -351,10 +351,12 @@ def authorize_keys_remote(other_user=nil) this_user = other_user || opts[:user] added_keys = [] rap = Rye::Rap.new(self) + prevdir = self.current_working_directory + # The homedir path is important b/c this is where we're going to # look for the .ssh directory. That's where auth love is stored. homedir = self.guess_user_home(this_user) unless self.file_exists?(homedir) @@ -393,10 +395,12 @@ self.upload(authorized_keys, "#{homedir}/#{akey_path}") self.chmod('0600', akey_path) self.chown(:R, this_user.to_s, File.dirname(akey_path)) end + # And let's return to the directory we came from. + self.cd prevdir rap.add_exit_code(0) rap end @@ -421,14 +425,54 @@ # A handler for undefined commands. # Raises Rye::CommandNotFound exception. def method_missing(meth, *args, &block) raise Rye::CommandNotFound, "#{meth.to_s}" end + + # Returns the command an arguments as a String. def preview_command(*args) prep_args(*args).join(' ') end + + # Supply a block to be called before every command. It's called + # with three arguments: command name, an Array of arguments, user name + # + def pre_command_hook(&block) + @pre_command_hook = block if block + @pre_command_hook + end + + # Execute a block in the context of an instance of Rye::Box. + # + # rbox = Rye::Box.new + # + # rbox.batch do + # ls :l + # uname :a + # end + # OR + # rbox.batch(&block) + # + # + def batch(&block) + instance_eval &block + end + + # Supply a block to be called after every command. It's called + # with one argument: an instance of Rye::Rap. + # + # When this block is supplied, the command does not raise an + # exception when the exit code is greater than 0 (the typical + # behavior) so the block needs to check the Rye::Rap object to + # determine whether an exception should be raised. + def post_command_hook(&block) + @post_command_hook = block if block + @post_command_hook + end + + private def debug(msg="unknown debug msg"); @debug.puts msg if @debug; end def error(msg="unknown error msg"); @error.puts msg if @error; end def pinfo(msg="unknown info msg"); @info.print msg if @info; end @@ -489,24 +533,41 @@ info "COMMAND: #{cmd_clean}" debug "Executing: %s" % cmd_clean + if @pre_command_hook.is_a?(Proc) + @pre_command_hook.call(cmd, args, opts[:user]) + end + + ## NOTE: Do not raise a CommandNotFound exception in this method. + # We want it to be possible to define methods to a single instance + # of Rye::Box. i.e. def rbox.rm()... + # can? returns the methods in Rye::Cmd so it would incorrectly + # return false. We could use self.respond_to? but it's possible + # to get a name collision. I could write a work around but I think + # this is good enough for now. + ## raise Rye::CommandNotFound unless self.can?(cmd) + stdout, stderr, ecode, esignal = net_ssh_exec!(cmd_clean) rap = Rye::Rap.new(self) rap.add_stdout(stdout || '') rap.add_stderr(stderr || '') rap.add_exit_code(ecode) rap.exit_signal = esignal rap.cmd = cmd - # It seems a convention for various commands to return -1 - # when something only mildly concerning happens. ls even - # returns -1 for apparently no reason sometimes. In any - # case, the real errors are the ones greater than zero - raise Rye::CommandError.new(rap) if ecode > 0 + if @post_command_hook.is_a?(Proc) + @post_command_hook.call(rap) + else + # It seems a convention for various commands to return -1 + # when something only mildly concerning happens. ls even + # returns -1 for apparently no reason sometimes. In any + # case, the real errors are the ones greater than zero + raise Rye::CommandError.new(rap) if ecode > 0 + end rap end alias :cmd :run_command @@ -592,10 +653,10 @@ unless [:upload, :download].member?(direction.to_sym) raise "Must be one of: upload, download" end if @current_working_directory - info "CWD (#{@current_working_directory}) not used" + info "CWD (#{@current_working_directory})" end files = [files].flatten.compact || [] # We allow a single file to be downloaded into a StringIO object