class Environment
Constants
- DEFAULT_IO
- RASH_LOCAL_FILE
Attributes
aliasing_disabled[R]
prompt[R]
umask[R]
Public Class Methods
new()
click to toggle source
# File lib/rash.rb, line 6 def initialize common_init end
Public Instance Methods
add_path(path)
click to toggle source
# File lib/rash.rb, line 33 def add_path(path) ENV["PATH"] += File::PATH_SEPARATOR + (path.respond_to?(:path) ? path.path : path.to_s) end
alias?(f)
click to toggle source
# File lib/rash/aliasing.rb, line 10 def alias?(f) @aliases.key?(f.to_sym) end
aliases()
click to toggle source
# File lib/rash/aliasing.rb, line 14 def aliases @aliases.dup end
as_pipe_command(&block)
click to toggle source
# File lib/rash/pipeline.rb, line 33 def as_pipe_command(&block) raise IOError.new("pipelining not enabled") unless @in_pipeline return as_sync_pipe_command(&block) if @synchronous_pipeline input = (@active_pipelines.empty? ? $stdin : @active_pipelines.last.reader) @active_pipelines << Pipeline.new output = @active_pipelines.last.writer error = ($stderr == $stdout ? output : $stderr) pid = fork do @in_pipeline = false $stdin = input $stdout = output $stderr = error block.call output.close exit!(true) end output.close @active_pipelines.last.link_process(pid) nil end
as_superuser(&block)
click to toggle source
# File lib/rash.rb, line 52 def as_superuser(&block) @superuser_mode = true begin block.call ensure @superuser_mode = false end end
as_sync_pipe_command(&block)
click to toggle source
# File lib/rash/pipeline.rb, line 57 def as_sync_pipe_command(&block) raise IOError.new("pipelining not enabled") unless @in_pipeline raise IOError.new("pipeline is not synchronous") unless @synchronous_pipeline @next_pipe.close @next_pipe = Pipeline.new # flush the output pipe @prev_pipe.writer.close input = (@first_sync_command ? $stdin : @prev_pipe.reader) @first_sync_command = false output = @next_pipe.writer error = ($stderr == $stdout ? @next_pipe.writer : $stdin) pid = fork do @in_pipeline = false @synchronous_pipeline = false $stdin = input $stdout = output $stderr = error block.call exit!(true) end Process.wait(pid) @prev_pipe, @next_pipe = @next_pipe, @prev_pipe nil end
async(&block)
click to toggle source
# File lib/rash/jobcontrol.rb, line 6 def async(&block) pid = fork { block.call exit!(true) } @active_jobs << pid Process.detach(pid) pid end
capture_block(&block)
click to toggle source
# File lib/rash/capturing.rb, line 2 def capture_block(&block) raise ArgumentError.new("no block provided") unless block_given? result = nil old_pipeline = @in_pipeline begin reader, writer = IO.pipe self.stdout = writer @in_pipeline = false block.call ensure @in_pipeline = old_pipeline reset_stdout writer.close result = reader.read reader.close end result end
capture_command(m, *args)
click to toggle source
# File lib/rash/capturing.rb, line 21 def capture_command(m, *args) raise NameError.new("no such command", m) unless which(m) || ($env.alias?(m) && !$env.aliasing_disabled) result = nil begin reader, writer = IO.pipe system_command(m, *args, out: writer) ensure writer.close result = reader.read reader.close end result end
chdir(dir = nil)
click to toggle source
# File lib/rash.rb, line 10 def chdir(dir = nil) old = @working_directory Dir.chdir(dir.nil? ? "~" : dir.to_s) @working_directory = Dir.pwd ENV["OLDPWD"] = old.to_s ENV["PWD"] = Dir.pwd Dir.pwd end
clear_alias(func)
click to toggle source
# File lib/rash/aliasing.rb, line 6 def clear_alias(func) @aliases.delete(func.to_sym) end
continued_prompt=(prompt)
click to toggle source
# File lib/rash/prompt/irb.rb, line 41 def continued_prompt=(prompt) @prompt[:prompt_c] = case prompt when Proc prompt else err_msg = "expecting stringable type or method that resolves to string" raise ArgumentError.new(err_msg) unless prompt.respond_to?(:to_s) lambda {prompt.to_s} end @prompt[:PROMPT_C] = "".tap {|s| def s.dup; $env.prompt[:prompt_c].call; end} end
dirs()
click to toggle source
# File lib/rash.rb, line 29 def dirs @directory_stack end
dispatch(m, *args)
click to toggle source
# File lib/rash.rb, line 74 def dispatch(m, *args) if @in_pipeline add_pipeline(m, *args) else system_command(m, *args) end end
indent_prompt=(prompt)
click to toggle source
# File lib/rash/prompt/irb.rb, line 15 def indent_prompt=(prompt) @prompt[:prompt_n] = case prompt when Proc prompt else err_msg = "expecting stringable type or method that resolves to string" raise ArgumentError.new(err_msg) unless prompt.respond_to?(:to_s) lambda {prompt.to_s} end @prompt[:PROMPT_N] = "".tap {|s| def s.dup; $env.prompt[:prompt_n].call; end} end
jobs()
click to toggle source
# File lib/rash/jobcontrol.rb, line 2 def jobs @active_jobs.keep_if{|pid| Process.kill(0, pid) rescue false}.dup end
local_call(name, *args, &block)
click to toggle source
This ultimately behaves similarly to a lambda in function if that ever changes, then the proc needs to be converted to a lambda.
# File lib/rash/ext/filesystem.rb, line 39 def local_call(name, *args, &block) @working_directory.local_method(name).call(*args, &block) end
local_def(name, locked: false, &block)
click to toggle source
# File lib/rash/ext/filesystem.rb, line 19 def local_def(name, locked: false, &block) @working_directory.add_local_method(name, &block) @working_directory.lock_method(name) if locked name.to_sym end
local_method?(name)
click to toggle source
# File lib/rash/ext/filesystem.rb, line 29 def local_method?(name) @working_directory.local_method?(name) end
local_methods()
click to toggle source
# File lib/rash/ext/filesystem.rb, line 33 def local_methods @working_directory.local_methods end
local_undef(name)
click to toggle source
# File lib/rash/ext/filesystem.rb, line 25 def local_undef(name) @working_directory.clear_local_method(name) end
local_var(name, v = nil, locked: false)
click to toggle source
# File lib/rash/ext/filesystem.rb, line 43 def local_var(name, v = nil, locked: false) res = nil if v.nil? res = @working_directory.local_variable(name) else @working_directory.set_local_variable(name, v) res = v end @working_directory.lock_variable(name) if locked res end
local_var?(name)
click to toggle source
# File lib/rash/ext/filesystem.rb, line 55 def local_var?(name) @working_directory.local_variable?(name) end
local_vars()
click to toggle source
# File lib/rash/ext/filesystem.rb, line 59 def local_vars @working_directory.local_variables end
make_alias(new_func, old_func)
click to toggle source
# File lib/rash/aliasing.rb, line 2 def make_alias(new_func, old_func) @aliases[new_func.to_sym] = old_func.to_s.split(" ") end
make_pipeline(&block)
click to toggle source
# File lib/rash/pipeline.rb, line 11 def make_pipeline(&block) raise IOError.new("pipelining already enabled") if @in_pipeline start_pipeline begin block.call ensure end_pipeline end nil end
make_sync_pipeline(&block)
click to toggle source
# File lib/rash/pipeline.rb, line 22 def make_sync_pipeline(&block) raise IOError.new("pipelining already enabled") if @in_pipeline start_sync_pipeline begin block.call ensure end_sync_pipeline end nil end
method_missing(m, *args, &block)
click to toggle source
Calls superclass method
Object::method_missing
# File lib/rash.rb, line 37 def method_missing(m, *args, &block) if args.length == 0 && !block_given? ENV[m.to_s.upcase] elsif m.to_s[-1] == "=" && args.length == 1 && !block_given? ENV[m.to_s.upcase.delete_suffix("=")] = args[0].to_s else super end end
name?(v)
click to toggle source
# File lib/rash.rb, line 82 def name?(v) v.kind_of?(String) || v.kind_of?(Symbol) end
pipelined?()
click to toggle source
# File lib/rash/pipeline.rb, line 3 def pipelined? @in_pipeline end
pop_dir()
click to toggle source
# File lib/rash.rb, line 25 def pop_dir self.chdir(@directory_stack.pop) if @directory_stack.size > 0 end
push_dir(dir = nil)
click to toggle source
Note that this works regardless of which version of chdir is used.
# File lib/rash.rb, line 20 def push_dir(dir = nil) @directory_stack.push(Dir.pwd) self.chdir(dir) end
reset_io()
click to toggle source
# File lib/rash/redirection.rb, line 3 def reset_io reset_stdout reset_stderr reset_stdin end
reset_stderr()
click to toggle source
# File lib/rash/redirection.rb, line 49 def reset_stderr $stderr.flush $stderr.close unless standard_stream?($stderr) $stderr = DEFAULT_IO[:err] end
reset_stdin()
click to toggle source
# File lib/rash/redirection.rb, line 69 def reset_stdin $stdin.close unless standard_stream?($stdin) $stdin = DEFAULT_IO[:in] end
reset_stdout()
click to toggle source
# File lib/rash/redirection.rb, line 26 def reset_stdout $stdout.flush $stdout.close unless standard_stream?($stdout) $stdout = DEFAULT_IO[:out] end
return_value_header=(prompt)
click to toggle source
This method can only be run from .rashrc. Anywhere else and it will simply do nothing
# File lib/rash/prompt/irb.rb, line 55 def return_value_header=(prompt) @prompt[:RETURN] = prompt end
standard_prompt=(prompt)
click to toggle source
# File lib/rash/prompt/irb.rb, line 3 def standard_prompt=(prompt) @prompt[:prompt_i] = case prompt when Proc prompt else err_msg = "expecting stringable type or method that resolves to string" raise ArgumentError.new(err_msg) unless prompt.respond_to?(:to_s) lambda {prompt.to_s} end @prompt[:PROMPT_I] = "".tap {|s| def s.dup; $env.prompt[:prompt_i].call; end} end
stderr=(file)
click to toggle source
# File lib/rash/redirection.rb, line 32 def stderr=(file) $stderr.flush old_stderr = $stderr case file when String $stderr = File.new(file, "w") when :out $stderr = STDOUT when :err $stderr = STDERR else raise ArgumentError.new("not an output stream - #{file}") unless file.is_a?(IO) $stderr = file end old_stderr.close unless standard_stream?(old_stderr) end
stdin=(file)
click to toggle source
# File lib/rash/redirection.rb, line 55 def stdin=(file) old_stdin = $stdin case file when String $stdin = File.new(file, "r") when :in $stdin = STDIN else raise ArgumentError.new("not an input stream - #{file}") unless file.is_a?(IO) $stdin = file end old_stdin.close unless standard_stream?(old_stdin) end
stdout=(file)
click to toggle source
# File lib/rash/redirection.rb, line 9 def stdout=(file) $stdout.flush old_stdout = $stdout case file when String $stdout = File.new(file, "w") when :out $stdout = STDOUT when :err $stdout = STDERR else raise ArgumentError.new("not an output stream - #{file}") unless file.is_a?(IO) $stdout = file end old_stdout.close unless standard_stream?(old_stdout) end
string_prompt=(prompt)
click to toggle source
# File lib/rash/prompt/irb.rb, line 28 def string_prompt=(prompt) @prompt[:prompt_s] = case prompt when Proc prompt else err_msg = "expecting stringable type or method that resolves to string" raise ArgumentError.new(err_msg) unless prompt.respond_to?(:to_s) lambda {prompt.to_s} end @prompt[:PROMPT_S] = "".tap {|s| def s.dup; $env.prompt[:prompt_s].call; end} end
synced_pipeline?()
click to toggle source
# File lib/rash/pipeline.rb, line 7 def synced_pipeline? @in_pipeline && @synchronous_pipeline end
umask=(mask)
click to toggle source
# File lib/rash.rb, line 47 def umask=(mask) File.umask(mask) @umask = mask end
use_irb_prompt()
click to toggle source
# File lib/rash/prompt/irb.rb, line 59 def use_irb_prompt if $0 == "irb" IRB.conf[:PROMPT][:RASH] = @prompt IRB.conf[:PROMPT_MODE] = :RASH end end
with_aliasing() { || ... }
click to toggle source
# File lib/rash/aliasing.rb, line 30 def with_aliasing old_aliasing = @aliasing_disabled @aliasing_disabled = false if block_given? begin yield ensure @aliasing_disabled = old_aliasing end end end
with_limits(limits, &block)
click to toggle source
# File lib/rash.rb, line 61 def with_limits(limits, &block) if block_given? pid = fork do limits.each {|resource, limit| Process.setrlimit(resource, *limit)} block.call exit!(true) end Process.wait(pid) else limits.each {|resource, limit| Process.setrlimit(resource, *limit)} end end
without_aliasing() { || ... }
click to toggle source
# File lib/rash/aliasing.rb, line 18 def without_aliasing old_aliasing = @aliasing_disabled @aliasing_disabled = true if block_given? begin yield ensure @aliasing_disabled = old_aliasing end end end
Private Instance Methods
add_pipeline(m, *args)
click to toggle source
special method to be referenced from Environment#dispatch
. Do not use directly
# File lib/rash/pipeline.rb, line 128 def add_pipeline(m, *args) raise IOError.new("pipelining not enabled") unless @in_pipeline return add_sync_pipeline(m, *args) if @synchronous_pipeline input = (@active_pipelines.empty? ? $stdin : @active_pipelines.last.reader) @active_pipelines << Pipeline.new output = @active_pipelines.last.writer error = ($stderr == $stdout ? output : $stderr) pid = fork do # might not be necessary, spawn might cover it. Not risking it before testing system_command(m, *args, out: output, input: input, err: error, except: true) output.close exit!(true) end output.close @active_pipelines.last.link_process(pid) end
add_sync_pipeline(m, *args)
click to toggle source
# File lib/rash/pipeline.rb, line 145 def add_sync_pipeline(m, *args) raise IOError.new("pipelining not enabled") unless @in_pipeline raise IOError.new("pipeline is not synchronous") unless @synchronous_pipeline # Ensure pipe is empty for writing @next_pipe.close @next_pipe = Pipeline.new @prev_pipe.writer.close input = (@first_sync_command ? $stdin : @prev_pipe.reader) @first_sync_command = false error = ($stderr == $stdout ? @next_pipe.writer : $stdin) system_command(m, *args, out: @next_pipe.writer, input: input, err: error, except: true) @prev_pipe, @next_pipe = @next_pipe, @prev_pipe nil end
common_init()
click to toggle source
# File lib/rash.rb, line 88 def common_init @working_directory = Dir.pwd @umask = File.umask @aliases = {} @aliasing_disabled = false @active_jobs = [] @active_pipelines = [] @directory_stack = [] @prompt = { AUTO_INDENT: true, RETURN: "" } end
end_pipeline()
click to toggle source
# File lib/rash/pipeline.rb, line 99 def end_pipeline raise IOError.new("pipelining not enabled") unless @in_pipeline @in_pipeline = false if @active_pipelines.size > 0 begin Process.wait(@active_pipelines.last.pid) @active_pipelines.last.writer.close # probably redundant, but leaving it for now IO.copy_stream(@active_pipelines.last.reader, $stdout) @active_pipelines.pop.close @active_pipelines.reverse_each {|pipe| pipe.terminate} ensure @active_pipelines.clear end end end
end_sync_pipeline()
click to toggle source
# File lib/rash/pipeline.rb, line 115 def end_sync_pipeline raise IOError.new("pipelining not enabled") unless @in_pipeline raise IOError.new("pipeline is not synchronous") unless @synchronous_pipeline @next_pipe.close @prev_pipe.writer.close IO.copy_stream(@prev_pipe.reader, $stdout) @prev_pipe.close @next_pipe = @prev_pipe = @first_sync_command = nil @synchronous_pipeline = @in_pipeline = false end
resolve_alias(f)
click to toggle source
Unless given a compelling reason, this doesn't need to be public. For most purposes, some combination of `alias?` and `aliases` should be sufficient.
# File lib/rash/aliasing.rb, line 46 def resolve_alias(f) result = [f.to_s] aliases = @aliases.dup found_alias = true while found_alias found_alias = false if aliases.has_key?(result[0].to_sym) found_alias = true match = result[0].to_sym result[0] = aliases[match] aliases.delete(match) result.flatten! end end result end
resolve_command(m, *args, literal: false)
click to toggle source
# File lib/rash.rb, line 106 def resolve_command(m, *args, literal: false) (literal ? [m.to_s] : resolve_alias(m)) + args.flatten.map{|a| a.to_s} end
standard_stream?(f)
click to toggle source
# File lib/rash/redirection.rb, line 76 def standard_stream?(f) DEFAULT_IO.values.include?(f) end
start_pipeline()
click to toggle source
# File lib/rash/pipeline.rb, line 87 def start_pipeline @in_pipeline = true end
start_sync_pipeline()
click to toggle source
# File lib/rash/pipeline.rb, line 91 def start_sync_pipeline @in_pipeline = true @synchronous_pipeline = true @first_sync_command = true @prev_pipe = Pipeline.new @next_pipe = Pipeline.new end
system_command(m, *args, except: false, literal: false, out: nil, input: nil, err: nil)
click to toggle source
# File lib/rash.rb, line 110 def system_command(m, *args, except: false, literal: false, out: nil, input: nil, err: nil) command = resolve_command(m, *args, literal: literal) command.unshift("sudo") if @superuser_mode opts = {out: out || $stdout, err: err || $stderr, in: input || $stdin, exception: except || @superuser_mode, umask: @umask} system(*command, opts) end
traverse_filetree(from, to)
click to toggle source
from and to are strings
# File lib/rash/ext/filesystem.rb, line 66 def traverse_filetree(from, to) abs_from = File.expand_path(from) abs_to = File.expand_path(to) raise SystemCallError.new(from, Errno::ENOENT::Errno) unless Dir.exists?(abs_from) raise SystemCallError.new(to, Errno::ENOENT::Errno) unless Dir.exists?(abs_to) from_parts = (abs_from == "/" ? [""] : abs_from.split(File::SEPARATOR)) to_parts = (abs_to == "/" ? [""] : abs_to.split(File::SEPARATOR)) common_path = from_parts.filter.with_index {|p, i| p == to_parts[i]} from_parts = from_parts.drop(common_path.size) to_parts = to_parts.drop(common_path.size) from_parts.each do |p| @working_directory.add_parent(File.expand_path("..")) if @working_directory.root? @working_directory = @working_directory.parent Dir.chdir(@working_directory.to_s) end to_parts.each do |p| @working_directory = @working_directory.child(File.expand_path(p, @working_directory.to_s)) Dir.chdir(@working_directory.to_s) # rashrc_local = @working_directory.to_s + File::SEPARATOR + RASH_LOCAL_FILE load RASH_LOCAL_FILE if File.exists?(RASH_LOCAL_FILE) && !File.directory?(RASH_LOCAL_FILE) end Dir.pwd end