lib/hysh.rb in hysh-0.0.1 vs lib/hysh.rb in hysh-0.0.2

- old
+ new

@@ -21,20 +21,22 @@ WARN = :warn RAISE = :raise @@on_command_error = IGNORE # :startdoc: + module_function + # :section: Common Utilities # :call-seq: # with_set_globals(var_name, val, ...) { ... } # # Set the global variable named +var_name+ (a string or symbol) to # +val+, then run the block, return the return value of the block. # Restore the original value of the variable upon returning. # Multiple pairs of +var_name+ and +val+ can be specified. - def self.with_set_globals(*var_vals) + def with_set_globals(*var_vals) orig_var_vals = var_vals.each_slice(2).map { |var, val| svar = var.to_s unless svar.start_with? '$' raise ArgumentError, "Invalid global variable name: #{svar}" end @@ -62,11 +64,11 @@ # +$stderr, etc.) to +io+ (redirection in Ruby), and arrange to # redirect the +fd+ (usally 0, 1, 2, etc. to +io+ for the external # programs, then run the block, return the return value of the # block. Restore the original value of the variable and cancel the # arrangement to external program redirections upon returning. - def self.with_redirect_to(fd, var, io, &b) + def with_redirect_to(fd, var, io, &b) @@redirections.push([fd, io]) if fd if var with_set_globals(var, io, &b) else yield @@ -81,11 +83,11 @@ # Set the +$stdin+ to +io+ (redirection in Ruby), and arrange to # redirect the file descriptor 0 to +io+ for the external programs, # then run the block, return the return value of the block. Restore # the original value of the $stdin and cancel the arrangement to # external program redirections upon returning. - def self.with_redirect_stdin_to(io, &b) + def with_redirect_stdin_to(io, &b) with_redirect_to(0, :$stdin, io, &b) end # :call-seq: # with_redirect_stdout_to(io) { ... } @@ -93,11 +95,11 @@ # Set the +$stdout+ to +io+ (redirection in Ruby), and arrange to # redirect the file descriptor 1 to +io+ for the external programs, # then run the block, return the return value of the block. Restore # the original value of the $stdout and cancel the arrangement to # external program redirections upon returning. - def self.with_redirect_stdout_to(io, &b) + def with_redirect_stdout_to(io, &b) with_redirect_to(1, :$stdout, io, &b) end # :call-seq: # with_redirect_stderr_to(io) { ... } @@ -105,11 +107,11 @@ # Set the +$stderr+ to +io+ (redirection in Ruby), and arrange to # redirect the file descriptor 2 to +io+ for the external programs, # then run the block, return the return value of the block. Restore # the original value of the $stderr and cancel the arrangement to # external program redirections upon returning. - def self.with_redirect_stderr_to(io, &b) + def with_redirect_stderr_to(io, &b) with_redirect_to(2, :$stderr, io, &b) end # :call-seq: # with_redirect_stdin_file(args...) { ... } @@ -119,11 +121,11 @@ # (redirection in Ruby), and arrange to redirect the file descriptor # 0 to the returned +io+ for the external programs, then run the # block, return the return value of the block. Restore the original # value of the $stdin and cancel the arrangement to external program # redirections upon returning. - def self.with_redirect_stdin_to_file(*args, &b) + def with_redirect_stdin_to_file(*args, &b) File.open(*args) { |f| with_redirect_stdin_to f, &b } end @@ -135,11 +137,11 @@ # (redirection in Ruby), and arrange to redirect the file descriptor # 1 to the returned +io+ for the external programs, then run the # block, return the return value of the block. Restore the original # value of the $stdout and cancel the arrangement to external # program redirections upon returning. - def self.with_redirect_stdout_to_file(*args, &b) + def with_redirect_stdout_to_file(*args, &b) if args.size == 1 args.push "w" end File.open(*args) { |f| with_redirect_stdout_to f, &b @@ -154,20 +156,20 @@ # (redirection in Ruby), and arrange to redirect the file descriptor # 2 to the returned +io+ for the external programs, then run the # block, return the return value of the block. Restore the original # value of the $stderr and cancel the arrangement to external # program redirections upon returning. - def self.with_redirect_stderr_to_file(*args, &b) + def with_redirect_stderr_to_file(*args, &b) if args.size == 1 args.push "w" end File.open(*args) { |f| with_redirect_stderr_to f, &b } end - def self.__out_io(args, options, proc_arg) # :nodoc: + def __out_io(args, options, proc_arg) # :nodoc: Tempfile.open(TEMP_BASE) { |tempf| tempf.unlink ret = nil with_redirect_stdout_to(tempf) { ret = __run args, options, proc_arg @@ -188,11 +190,11 @@ # +options+ via stdout redirection. +command+ and +options+ # parameters are same as that of +Process.spawn+. Return the # collected output string and the return value of the block or the # function or exit success status of the external program as a two # element array. Restore stdout redirection upon returning. - def self.out_s(*args, &blk) + def out_s(*args, &blk) __out_io(*__parse_args(args, blk)) { |tempf| tempf.read } end @@ -201,11 +203,11 @@ # out_ss(function) -> [string, any] # out_ss(command...[, options]) -> [string, true or false] # # Same as out_s, except the collected output string are right # stripped before return. - def self.out_ss(*args_in, &blk) + def out_ss(*args_in, &blk) s, ret = out_s(*args_in, &blk) [s.rstrip, ret] end # :call-seq: @@ -227,11 +229,11 @@ # program specified via +command+ and +options+ via stdout # redirection. +command+ and +options+ are same as that of # +Process.spawn+. Feed each line of output to the block as +line+. # Return the exit success status of the forked sub-process or the # external program. Restore stdout redirection upon returning. - def self.out_lines(*args_in, &blk) + def out_lines(*args_in, &blk) args, options, proc_arg = __parse_args args_in if block_given? __popen(nil, true, nil, args, options, proc_arg) { |pid, stdin, stdout, stderr| stdout.each_line(&blk) Process.waitpid pid @@ -248,11 +250,11 @@ # out_err_s() { ... } -> [string, any] # out_err_s(function) -> [string, any] # out_err_s(command...[, options]) -> [string, true or false] # # Same as out_s, except collect output of stderr too. - def self.out_err_s(*args_in, &blk) + def out_err_s(*args_in, &blk) args, options, proc_arg = __parse_args args_in, blk Tempfile.open(TEMP_BASE) { |tempf| tempf.unlink ret = nil with_redirect_stdout_to(tempf) { @@ -271,16 +273,16 @@ # out_err_ss(function) -> [string, any] # out_err_ss(command...[, options]) -> [string, true or false] # # Same as out_err_s, except the collected output string are right # stripped before return. - def self.out_err_ss(*args_in, &blk) + def out_err_ss(*args_in, &blk) s, ret = out_err_s(*args_in, &blk) [s.rstrip. ret] end - def self.__in_io(args, options, proc_arg) # :nodoc: + def __in_io(args, options, proc_arg) # :nodoc: Tempfile.open(TEMP_BASE) { |tempf| tempf.unlink yield tempf tempf.rewind with_redirect_stdin_to(tempf) { @@ -299,11 +301,11 @@ # program specified via +command+ and +options+ via stdin # redirection. +command+ and +options+ are same as that of # +Process.spawn+. Return the return value of the block or the # function or the exit success status of the external program. # Restore stdin redirection upon returning. - def self.in_s(s, *args_in, &blk) + def in_s(s, *args_in, &blk) args, options, proc_arg = __parse_args args_in, blk __in_io(args, options, proc_arg) { |tempf| tempf.write s } end @@ -313,11 +315,11 @@ # in_lines(lines, function) -> any # in_lines(lines, command...[, options]) -> true or false # # Same as +in_s+, except input string are specified via +lines+ # (Array of String). - def self.in_lines(lines, *args_in, &blk) + def in_lines(lines, *args_in, &blk) args, options, proc_arg = __parse_args args_in, blk __in_io(args, options, proc_arg) { |tempf| lines.each { |line| tempf.write line } } end @@ -327,11 +329,11 @@ # io_s(string, function) -> [string, any] # io_s(stirng, command...[, options]) -> [string, true or false] # # Redirect the stdin and stdout like that of +in_s+ and +out_s+, # return value is same of +out_s+. - def self.io_s(s, *args_in, &blk) + def io_s(s, *args_in, &blk) in_s(s) { out_s { run *args_in, &blk } } @@ -342,11 +344,11 @@ # io_ss(string, function) -> [string, any] # io_ss(stirng, command...[, options]) -> [string, true or false] # # Same as +io_s+, except the output string is right stripped before # returning. - def self.io_ss(s, *args_in, &blk) + def io_ss(s, *args_in, &blk) s = io_s(s, *args_in, &blk) s.rstrip end # :section: Run Process @@ -355,35 +357,35 @@ # ignore_on_command_error() { ... } # # When running the block, the non-zero exit status of running # external program are ignored. The original behavior is restored # upon returning. - def self.ignore_on_command_error(&b) + def ignore_on_command_error(&b) with_set_globals(:@@on_command_error, IGNORE, &b) end # :call-seq: # warn_on_command_error() { ... } # # When running the block, the warning message will be print to # $stderr when the external program exited with non-zero status. # The original behavior is restored upon returning. - def self.warn_on_command_error(&b) + def warn_on_command_error(&b) with_set_globals(:@@on_command_error, WARN, &b) end # :call-seq: # raise_on_command_error() { ... } # # When running the block, an +Hysh::CommandError+ exception will be # raised when the external program exited with non-zero status. The # original behavior is restored upon returning. - def self.raise_on_command_error(&b) + def raise_on_command_error(&b) with_set_globals(:@@on_command_error, RAISE, &b) end - def self.__parse_args(args, blk = nil) # :nodoc: + def __parse_args(args, blk = nil) # :nodoc: args = [args] unless args.is_a? Array if args.last.is_a?(Hash) options = args.pop else options = {} @@ -408,11 +410,11 @@ # specified via parameters. The +env_var+ specifies the environment # variable name, and the +val+ specifies the value, when +val+ is # nil, the envioronment variable will be removed. Multiple pairs of # the environment variable names and values can be specified. The # changes to the environment are restored upon returning. - def self.with_change_env(*var_vals) + def with_change_env(*var_vals) orig_var_vals = var_vals.each_slice(2).map { |var, val| orig_val = ENV[var] [var, orig_val] } var_vals.each_slice(2) { |var, val| @@ -429,15 +431,15 @@ # :call-seq: # chdir(dir) { ... } # # Same as +Dir.chdir+. - def self.chdir(dir, &b) + def chdir(dir, &b) Dir.chdir(dir, &b) end - def self.__spawn(args, options_in, proc_arg) # :nodoc: + def __spawn(args, options_in, proc_arg) # :nodoc: if proc_arg Process.fork { fclose = options_in[:close] || [] fclose.each { |f| f.close } fin = options_in[0] @@ -471,11 +473,11 @@ # # Run the block or the function specified via +function+ in a forked # sub-process, or run external program specified via +command+ and # +options+, +command+ and +options+ are same as that of # Process.spawn. Return the +pid+. - def self.spawn(*args_in, &blk) + def spawn(*args_in, &blk) __spawn *__parse_args(args_in, blk) end # Exception class raised when an external program exits with # non-zero status and raise_on_command_error take effect. @@ -502,11 +504,11 @@ attr_reader :cmdline # External program exit status, as Process:Status attr_reader :status end - def self.__check_command_status(cmd) # :nodoc: + def __check_command_status(cmd) # :nodoc: unless $?.success? if @@on_command_error != IGNORE err = CommandError.new(cmd, $?) case @@on_command_error when WARN @@ -519,11 +521,11 @@ else true end end - def self.__run(args, options, proc_arg) #:nodoc: + def __run(args, options, proc_arg) #:nodoc: if proc_arg args.first.() else pid = __spawn args, options, proc_arg Process.waitpid pid @@ -541,23 +543,23 @@ # +command+ and +options+, +command+ and +options+ are same as that # of Process.spawn and return whether external program the exit with # 0. All IO redirections, environment change, current directory # change, etc. will take effect when running the block, the function # and the external program. - def self.run(*args_in, &blk) + def run(*args_in, &blk) __run *__parse_args(args_in, blk) end - def self.__check_close(*ios) # :nodoc: + def __check_close(*ios) # :nodoc: ios.each { |io| if io && !io.closed? io.close end } end - def self.__popen(stdin, stdout, stderr, args, options, proc_arg) # :nodoc: + def __popen(stdin, stdout, stderr, args, options, proc_arg) # :nodoc: options[:close] = [] if proc_arg stdin_in = stdin_out = nil stdout_in = stdout_out = nil stderr_in = stderr_out = nil @@ -622,11 +624,11 @@ # closed and the process will be detached if necessary upon # returning. # # If no block is given, the pid and stdin, stdout and stderr pipe # will be returned. - def self.popen(stdin, stdout, stderr, *args_in, &blk) + def popen(stdin, stdout, stderr, *args_in, &blk) args, options, proc_arg = __parse_args args_in options[:close] = [] if proc_arg __popen(stdin, stdout, stderr, args, options, proc_arg, &blk) end @@ -653,11 +655,11 @@ # [command, ..., options] # command with argument and options in an array # [command, ...] # command with/without arguments in an array # command # command without argument # [function] # function in an array # function # function - def self.pipe(*cmds, &blk) + def pipe(*cmds, &blk) if block_given? cmds.push [blk] end if cmds.empty? raise ArgumentError.new('No argument or block!') @@ -732,11 +734,11 @@ # # Run the functions and the external programs specified via # +command_line+, and the block if given, from left to right. # +command_line+ is same as that of +pipe+. Return the return value # or exit success status of the last function or external command. - def self.run_seq(*cmds, &blk) + def run_seq(*cmds, &blk) if block_given? cmds.push blk end ret = true cmds.each { |cmd| @@ -755,11 +757,11 @@ # block returns non-nil/false, or any external program exits # successfully, stop running the remaining function, or external # program and return the value. If all failed, false or nil will be # returned. If no function, external program, or block is given, # return false. - def self.run_or(*cmds, &blk) + def run_or(*cmds, &blk) if block_given? cmds.push blk end return false if cmds.empty? @@ -785,11 +787,11 @@ # stop running the remaining function, or external program and # return the value. If all succeed, return the return value of the # last function, the block or the exit success status of the # external program. If no function, external program, or block is # provided, return true. - def self.run_and(*cmds, &blk) + def run_and(*cmds, &blk) if block_given? cmds.push blk end return true if cmds.empty? @@ -809,11 +811,11 @@ # :call-seq: # filter_line() { |line| ... } -> true # # Feed each line from $stdin to the block, if non-nil/false # returned, write the return value to the $stdout. - def self.filter_line + def filter_line $stdin.each_line { |line| if ret_line = yield(line) $stdout.write ret_line end } @@ -823,14 +825,61 @@ # :call-seq: # filter_char() { |char| ... } -> true # # Feed each character from $stdin to the block, if non-nil/false # returned, write the return value to the $stdout. - def self.filter_char + def filter_char $stdin.each_char { |ch| if ret_ch = yield(ch) $stdout.write ret_ch end } true end + + def __with_add_hysh_to_self(s, &blk) # :nodoc: + sclass = s.singleton_class + mm_defined = sclass.instance_methods(false).include? :method_missing + mm_defined ||= sclass.private_instance_methods(false).include? :method_missing + class << s + alias __hysh_origin_method_missing__ method_missing + def method_missing(m, *args, &blk) + __hysh_origin_method_missing__ m, args, &blk + rescue NoMethodError + if Hysh.singleton_class.public_method_defined? m + Hysh.send m, *args, &blk + else + Hysh.run m.to_s, *args.map { |a| a.to_s }, &blk + end + end + end + s.instance_eval &blk + ensure + class << s + remove_method :method_missing + end + if mm_defined + class << s + alias method_missing __hysh_origin_method_missing__ + end + end + class << s + remove_method :__hysh_origin_method_missing__ + end + end +end + +# :call-seq: +# hysh_script { ... } +# +# Call the block, in addition to the methods, variables reference of +# current self object, the public methods in Hysh module can be called +# directly too, and all other method callings are regarded as external +# program running. That is, if there is no ls method defined for +# self, you can run it via +# +# hysh_script { +# ls +# } +def hysh_script(&blk) + Hysh.__with_add_hysh_to_self(self, &blk) end