From 2a4e0b193f01c9481ff2b5099e940af5bd8c068a Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Thu, 27 Feb 2014 22:52:54 +0400 Subject: [PATCH 001/119] Some cleanup and retab --- bin/rush | 4 +- lib/rush/access.rb | 239 ++++++++++++++-------------- lib/rush/box.rb | 172 ++++++++++---------- lib/rush/commands.rb | 77 +++++---- lib/rush/config.rb | 302 +++++++++++++++++------------------ lib/rush/dir.rb | 10 +- lib/rush/shell.rb | 364 +++++++++++++++++++++---------------------- tags | 312 +++++++++++++++++++++++++++++++++++++ 8 files changed, 888 insertions(+), 592 deletions(-) create mode 100644 tags diff --git a/bin/rush b/bin/rush index 2cb5d5f..9228938 100755 --- a/bin/rush +++ b/bin/rush @@ -1,7 +1,7 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../lib/rush' -require File.dirname(__FILE__) + '/../lib/rush/shell' +require_relative '../lib/rush' +require_relative '../lib/rush/shell' shell = Rush::Shell.new diff --git a/lib/rush/access.rb b/lib/rush/access.rb index 6cca3c9..f824e0e 100644 --- a/lib/rush/access.rb +++ b/lib/rush/access.rb @@ -1,130 +1,117 @@ # A class to hold permissions (read, write, execute) for files and dirs. # See Rush::Entry#access= for information on the public-facing interface. class Rush::Access - attr_accessor :user_can_read, :user_can_write, :user_can_execute - attr_accessor :group_can_read, :group_can_write, :group_can_execute - attr_accessor :other_can_read, :other_can_write, :other_can_execute - - def self.roles - %w(user group other) - end - - def self.permissions - %w(read write execute) - end - - def parse(options) - options.each do |key, value| - next unless m = key.to_s.match(/(.*)_can$/) - key = m[1].to_sym - roles = extract_list('role', key, self.class.roles) - perms = extract_list('permission', value, self.class.permissions) - set_matrix(perms, roles) - end - self - end - - def self.parse(options) - new.parse(options) - end - - def apply(full_path) - FileUtils.chmod(octal_permissions, full_path) - rescue Errno::ENOENT - raise Rush::DoesNotExist, full_path - end - - def to_hash - hash = {} - self.class.roles.each do |role| - self.class.permissions.each do |perm| - key = "#{role}_can_#{perm}".to_sym - hash[key] = send(key) ? 1 : 0 - end - end - hash - end - - def display_hash - hash = {} - to_hash.each do |key, value| - hash[key] = true if value == 1 - end - hash - end - - def from_hash(hash) - self.class.roles.each do |role| - self.class.permissions.each do |perm| - key = "#{role}_can_#{perm}" - send("#{key}=".to_sym, hash[key.to_sym].to_i == 1 ? true : false) - end - end - self - end - - def self.from_hash(hash) - new.from_hash(hash) - end - - def octal_permissions - perms = [ 0, 0, 0 ] - perms[0] += 4 if user_can_read - perms[0] += 2 if user_can_write - perms[0] += 1 if user_can_execute - - perms[1] += 4 if group_can_read - perms[1] += 2 if group_can_write - perms[1] += 1 if group_can_execute - - perms[2] += 4 if other_can_read - perms[2] += 2 if other_can_write - perms[2] += 1 if other_can_execute - - eval("0" + perms.join) - end - - def from_octal(mode) - perms = octal_integer_array(mode) - - self.user_can_read = (perms[0] & 4) > 0 ? true : false - self.user_can_write = (perms[0] & 2) > 0 ? true : false - self.user_can_execute = (perms[0] & 1) > 0 ? true : false - - self.group_can_read = (perms[1] & 4) > 0 ? true : false - self.group_can_write = (perms[1] & 2) > 0 ? true : false - self.group_can_execute = (perms[1] & 1) > 0 ? true : false - - self.other_can_read = (perms[2] & 4) > 0 ? true : false - self.other_can_write = (perms[2] & 2) > 0 ? true : false - self.other_can_execute = (perms[2] & 1) > 0 ? true : false - - self - end - - def octal_integer_array(mode) - mode %= 01000 # filter out everything but the bottom three digits - mode = sprintf("%o", mode) # convert to string - mode.split("").map { |p| p.to_i } # and finally, array of integers - end - - def set_matrix(perms, roles) - perms.each do |perm| - roles.each do |role| - meth = "#{role}_can_#{perm}=".to_sym - send(meth, true) - end - end - end - - def extract_list(type, value, choices) - list = parts_from(value) - list.each do |value| - raise(Rush::BadAccessSpecifier, "Unrecognized #{type}: #{value}") unless choices.include? value - end - end - - def parts_from(value) - value.to_s.split('_').reject { |r| r == 'and' } - end + ROLES = %w(user group other) + PERMISSIONS = %w(read write execute) + ACCESS_UNITS = ROLES.product(PERMISSIONS). + map { |r, p| "#{r}_can_#{p}".to_sym } + + attr_accessor *ACCESS_UNITS + + def self.roles + ROLES + end + + def self.permissions + PERMISSIONS + end + + def parse(options) + options.each do |key, value| + next unless m = key.to_s.match(/(.*)_can$/) + key = m[1].to_sym + roles = extract_list('role', key, self.class.roles) + perms = extract_list('permission', value, self.class.permissions) + set_matrix(perms, roles) + end + self + end + + def self.parse(options) + new.parse(options) + end + + def apply(full_path) + FileUtils.chmod(octal_permissions, full_path) + rescue Errno::ENOENT + raise Rush::DoesNotExist, full_path + end + + def to_hash + ACCESS_UNITS.inject({}) do |hash, unit| + hash.merge(unit => send(unit) ? 1 : 0) + end + end + + def display_hash + to_hash.select { |_, v| v == 1 }. + inject({}) { |r, k, _| r.merge k => true } + end + + def from_hash(hash) + ACCESS_UNITS.each do |unit| + send("#{unit}=".to_sym, hash[unit].to_i == 1 ? true : false) + end + self + end + + def self.from_hash(hash) + new.from_hash(hash) + end + + def octal_permissions + perms = [ 0, 0, 0 ] + perms[0] += 4 if user_can_read + perms[0] += 2 if user_can_write + perms[0] += 1 if user_can_execute + + perms[1] += 4 if group_can_read + perms[1] += 2 if group_can_write + perms[1] += 1 if group_can_execute + + perms[2] += 4 if other_can_read + perms[2] += 2 if other_can_write + perms[2] += 1 if other_can_execute + + eval("0" + perms.join) + end + + def from_octal(mode) + perms = octal_integer_array(mode) + + self.user_can_read = (perms[0] & 4) > 0 ? true : false + self.user_can_write = (perms[0] & 2) > 0 ? true : false + self.user_can_execute = (perms[0] & 1) > 0 ? true : false + + self.group_can_read = (perms[1] & 4) > 0 ? true : false + self.group_can_write = (perms[1] & 2) > 0 ? true : false + self.group_can_execute = (perms[1] & 1) > 0 ? true : false + + self.other_can_read = (perms[2] & 4) > 0 ? true : false + self.other_can_write = (perms[2] & 2) > 0 ? true : false + self.other_can_execute = (perms[2] & 1) > 0 ? true : false + + self + end + + def octal_integer_array(mode) + mode %= 01000 # filter out everything but the bottom three digits + mode = sprintf("%o", mode) # convert to string + mode.split("").map { |p| p.to_i } # and finally, array of integers + end + + def set_matrix(perms, roles) + ACCESS_UNITS.each { |unit| send unit, true } + end + + def extract_list(type, value, choices) + list = parts_from(value) + list.each do |value| + raise(Rush::BadAccessSpecifier, "Unrecognized #{type}: #{value}") unless choices.include? value + end + end + + def parts_from(value) + value.to_s.split('_').reject { |r| r == 'and' } + end end diff --git a/lib/rush/box.rb b/lib/rush/box.rb index a194a61..83264dd 100644 --- a/lib/rush/box.rb +++ b/lib/rush/box.rb @@ -11,105 +11,105 @@ # local.processes # class Rush::Box - attr_reader :host + attr_reader :host - # Instantiate a box. No action is taken to make a connection until you try - # to perform an action. If the box is remote, an ssh tunnel will be opened. - # Specify a username with the host if the remote ssh user is different from - # the local one (e.g. Rush::Box.new('user@host')). - def initialize(host='localhost') - @host = host - end + # Instantiate a box. No action is taken to make a connection until you try + # to perform an action. If the box is remote, an ssh tunnel will be opened. + # Specify a username with the host if the remote ssh user is different from + # the local one (e.g. Rush::Box.new('user@host')). + def initialize(host='localhost') + @host = host + end - def to_s # :nodoc: - host - end + def to_s # :nodoc: + host + end - def inspect # :nodoc: - host - end + def inspect # :nodoc: + host + end - # Access / on the box. - def filesystem - Rush::Entry.factory('/', self) - end + # Access / on the box. + def filesystem + Rush::Entry.factory('/', self) + end - # Look up an entry on the filesystem, e.g. box['/path/to/some/file']. - # Returns a subclass of Rush::Entry - either Rush::Dir if you specifiy - # trailing slash, or Rush::File otherwise. - def [](key) - filesystem[key] - end + # Look up an entry on the filesystem, e.g. box['/path/to/some/file']. + # Returns a subclass of Rush::Entry - either Rush::Dir if you specifiy + # trailing slash, or Rush::File otherwise. + def [](key) + filesystem[key] + end - # Get the list of processes running on the box, not unlike "ps aux" in bash. - # Returns a Rush::ProcessSet. - def processes - Rush::ProcessSet.new( - connection.processes.map do |ps| - Rush::Process.new(ps, self) - end - ) - end + # Get the list of processes running on the box, not unlike "ps aux" in bash. + # Returns a Rush::ProcessSet. + def processes + Rush::ProcessSet.new( + connection.processes.map { |ps| Rush::Process.new(ps, self) } + ) + end - # Execute a command in the standard unix shell. Returns the contents of - # stdout if successful, or raises Rush::BashFailed with the output of stderr - # if the shell returned a non-zero value. Options: - # - # :user => unix username to become via sudo - # :env => hash of environment variables - # :background => run in the background (returns Rush::Process instead of stdout) - # - # Examples: - # - # box.bash '/etc/init.d/mysql restart', :user => 'root' - # box.bash 'rake db:migrate', :user => 'www', :env => { :RAILS_ENV => 'production' } - # box.bash 'mongrel_rails start', :background => true - # box.bash 'rake db:migrate', :user => 'www', :env => { :RAILS_ENV => 'production' }, :reset_environment => true - # - def bash(command, options={}) - cmd_with_env = command_with_environment(command, options[:env]) - options[:reset_environment] ||= false + # Execute a command in the standard unix shell. Returns the contents of + # stdout if successful, or raises Rush::BashFailed with the output of stderr + # if the shell returned a non-zero value. Options: + # + # :user => unix username to become via sudo + # :env => hash of environment variables + # :background => run in the background (returns Rush::Process instead of stdout) + # + # Examples: + # + # box.bash '/etc/init.d/mysql restart', :user => 'root' + # box.bash 'rake db:migrate', :user => 'www', :env => { :RAILS_ENV => 'production' } + # box.bash 'mongrel_rails start', :background => true + # box.bash 'rake db:migrate', :user => 'www', :env => { :RAILS_ENV => 'production' }, :reset_environment => true + # + def bash(command, options={}) + cmd_with_env = command_with_environment(command, options[:env]) + options[:reset_environment] ||= false - if options[:background] - pid = connection.bash(cmd_with_env, options[:user], true, options[:reset_environment]) - processes.find_by_pid(pid) - else - connection.bash(cmd_with_env, options[:user], false, options[:reset_environment]) - end - end + if options[:background] + pid = connection.bash(cmd_with_env, options[:user], true, options[:reset_environment]) + processes.find_by_pid(pid) + else + connection.bash(cmd_with_env, options[:user], false, options[:reset_environment]) + end + end - def command_with_environment(command, env) # :nodoc: - return command unless env + def command_with_environment(command, env) # :nodoc: + return command unless env - vars = env.map do |key, value| - escaped = value.to_s.gsub('"', '\\"').gsub('`', '\\\`') - "export #{key}=\"#{escaped}\"" - end - vars.push(command).join("\n") - end + vars = env.map do |key, value| + escaped = value.to_s.gsub('"', '\\"').gsub('`', '\\\`') + "export #{key}=\"#{escaped}\"" + end + vars.push(command).join("\n") + end - # Returns true if the box is responding to commands. - def alive? - connection.alive? - end + # Returns true if the box is responding to commands. + def alive? + connection.alive? + end - # This is called automatically the first time an action is invoked, but you - # may wish to call it manually ahead of time in order to have the tunnel - # already set up and running. You can also use this to pass a timeout option, - # either :timeout => (seconds) or :timeout => :infinite. - def establish_connection(options={}) - connection.ensure_tunnel(options) - end + # This is called automatically the first time an action is invoked, but you + # may wish to call it manually ahead of time in order to have the tunnel + # already set up and running. You can also use this to pass a timeout option, + # either :timeout => (seconds) or :timeout => :infinite. + def establish_connection(options={}) + connection.ensure_tunnel(options) + end - def connection # :nodoc: - @connection ||= make_connection - end + def connection # :nodoc: + @connection ||= make_connection + end - def make_connection # :nodoc: - host == 'localhost' ? Rush::Connection::Local.new : Rush::Connection::Remote.new(host) - end + def make_connection # :nodoc: + host == 'localhost' ? + Rush::Connection::Local.new : + Rush::Connection::Remote.new(host) + end - def ==(other) # :nodoc: - host == other.host - end + def ==(other) # :nodoc: + host == other.host + end end diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 89e0ee2..a0d991b 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -8,48 +8,47 @@ # box['/etc/'].search /localhost/ # entire directory # box['/etc/**/*.conf'].search /localhost/ # arbitrary list module Rush::Commands - # The entries command must return an array of Rush::Entry items. This - # varies by class that it is mixed in to. - def entries - raise "must define me in class mixed in to for command use" - end + # The entries command must return an array of Rush::Entry items. This + # varies by class that it is mixed in to. + def entries + raise "must define me in class mixed in to for command use" + end - # Search file contents for a regular expression. A Rush::SearchResults - # object is returned. - def search(pattern) - results = Rush::SearchResults.new(pattern) - entries.each do |entry| - if !entry.dir? and matches = entry.search(pattern) - results.add(entry, matches) - end - end - results - end + # Search file contents for a regular expression. A Rush::SearchResults + # object is returned. + def search(pattern) + results = Rush::SearchResults.new(pattern) + entries.each do |entry| + if !entry.dir? and matches = entry.search(pattern) + results.add(entry, matches) + end + end + results + end - # Search and replace file contents. - def replace_contents!(pattern, with_text) - entries.each do |entry| - entry.replace_contents!(pattern, with_text) unless entry.dir? - end - end + # Search and replace file contents. + def replace_contents!(pattern, with_text) + entries.each do |entry| + entry.replace_contents!(pattern, with_text) unless entry.dir? + end + end - # Count the number of lines in the contained files. - def line_count - entries.inject(0) do |count, entry| - count += entry.lines.size if !entry.dir? - count - end - end + # Count the number of lines in the contained files. + def line_count + entries.inject(0) do |count, entry| + count + entry.dir? ? 0 : entry.lines.size + end + end - # Invoke vi on one or more files - only works locally. - def vi(*args) - names = entries.map { |f| f.quoted_path }.join(' ') - system "vim #{names} #{args.join(' ')}" - end + # Invoke vi on one or more files - only works locally. + def vi(*args) + names = entries.map { |f| f.quoted_path }.join(' ') + system "vim #{names} #{args.join(' ')}" + end - # Invoke TextMate on one or more files - only works locally. - def mate(*args) - names = entries.map { |f| f.quoted_path }.join(' ') - system "mate #{names} #{args.join(' ')}" - end + # Invoke TextMate on one or more files - only works locally. + def mate(*args) + names = entries.map { |f| f.quoted_path }.join(' ') + system "mate #{names} #{args.join(' ')}" + end end diff --git a/lib/rush/config.rb b/lib/rush/config.rb index 31da070..25bcdf3 100644 --- a/lib/rush/config.rb +++ b/lib/rush/config.rb @@ -1,154 +1,154 @@ # The config class accesses files in ~/.rush to load and save user preferences. class Rush::Config - DefaultPort = 7770 - - attr_reader :dir - - # By default, reads from the dir ~/.rush, but an optional parameter allows - # using another location. - def initialize(location=nil) - @dir = Rush::Dir.new(location || "#{ENV['HOME']}/.rush") - @dir.create - end - - # History is a flat file of past commands in the interactive shell, - # equivalent to .bash_history. - def history_file - dir['history'] - end - - def save_history(array) - history_file.write(array.join("\n") + "\n") - end - - def load_history - history_file.contents_or_blank.split("\n") - end - - # The environment file is executed when the interactive shell starts up. - # Put aliases and your own functions here; it is the equivalent of .bashrc - # or .profile. - # - # Example ~/.rush/env.rb: - # - # server = Rush::Box.new('www@my.server') - # myproj = home['projects/myproj/'] - def env_file - dir['env.rb'] - end - - def load_env - env_file.contents_or_blank - end - - # Commands are mixed in to Array and Rush::Entry, alongside the default - # commands from Rush::Commands. Any methods here should reference "entries" - # to get the list of entries to operate on. - # - # Example ~/.rush/commands.rb: - # - # def destroy_svn(*args) - # entries.select { |e| e.name == '.svn' }.destroy - # end - def commands_file - dir['commands.rb'] - end - - def load_commands - commands_file.contents_or_blank - end - - # Passwords contains a list of username:password combinations used for - # remote access via rushd. You can fill this in manually, or let the remote - # connection publish it automatically. - def passwords_file - dir['passwords'] - end - - def passwords - hash = {} - passwords_file.lines_or_empty.each do |line| - user, password = line.split(":", 2) - hash[user] = password - end - hash - end - - # Credentials is the client-side equivalent of passwords. It contains only - # one username:password combination that is transmitted to the server when - # connecting. This is also autogenerated if it does not exist. - def credentials_file - dir['credentials'] - end - - def credentials - credentials_file.lines.first.split(":", 2) - end - - def save_credentials(user, password) - credentials_file.write("#{user}:#{password}\n") - end - - def credentials_user - credentials[0] - end - - def credentials_password - credentials[1] - end - - def ensure_credentials_exist - generate_credentials if credentials_file.contents_or_blank == "" - end - - def generate_credentials - save_credentials(generate_user, generate_password) - end - - def generate_user - generate_secret(4, 8) - end - - def generate_password - generate_secret(8, 15) - end - - def generate_secret(min, max) - chars = self.secret_characters - len = rand(max - min + 1) + min - password = "" - len.times do |index| - password += chars[rand(chars.length)] - end - password - end - - def secret_characters - [ ('a'..'z'), ('1'..'9') ].inject([]) do |chars, range| - chars += range.to_a - end - end - - # ~/.rush/tunnels contains a list of previously created ssh tunnels. The - # format is host:port, where port is the local port that the tunnel is - # listening on. - def tunnels_file - dir['tunnels'] - end - - def tunnels - tunnels_file.lines_or_empty.inject({}) do |hash, line| - host, port = line.split(':', 2) - hash[host] = port.to_i - hash - end - end - - def save_tunnels(hash) - string = "" - hash.each do |host, port| - string += "#{host}:#{port}\n" - end - tunnels_file.write string - end + DefaultPort = 7770 + + attr_reader :dir + + # By default, reads from the dir ~/.rush, but an optional parameter allows + # using another location. + def initialize(location=nil) + @dir = Rush::Dir.new(location || "#{ENV['HOME']}/.rush") + @dir.create + end + + # History is a flat file of past commands in the interactive shell, + # equivalent to .bash_history. + def history_file + dir['history'] + end + + def save_history(array) + history_file.write(array.join("\n") + "\n") + end + + def load_history + history_file.contents_or_blank.split("\n") + end + + # The environment file is executed when the interactive shell starts up. + # Put aliases and your own functions here; it is the equivalent of .bashrc + # or .profile. + # + # Example ~/.rush/env.rb: + # + # server = Rush::Box.new('www@my.server') + # myproj = home['projects/myproj/'] + def env_file + dir['env.rb'] + end + + def load_env + env_file.contents_or_blank + end + + # Commands are mixed in to Array and Rush::Entry, alongside the default + # commands from Rush::Commands. Any methods here should reference "entries" + # to get the list of entries to operate on. + # + # Example ~/.rush/commands.rb: + # + # def destroy_svn(*args) + # entries.select { |e| e.name == '.svn' }.destroy + # end + def commands_file + dir['commands.rb'] + end + + def load_commands + commands_file.contents_or_blank + end + + # Passwords contains a list of username:password combinations used for + # remote access via rushd. You can fill this in manually, or let the remote + # connection publish it automatically. + def passwords_file + dir['passwords'] + end + + def passwords + hash = {} + passwords_file.lines_or_empty.each do |line| + user, password = line.split(":", 2) + hash[user] = password + end + hash + end + + # Credentials is the client-side equivalent of passwords. It contains only + # one username:password combination that is transmitted to the server when + # connecting. This is also autogenerated if it does not exist. + def credentials_file + dir['credentials'] + end + + def credentials + credentials_file.lines.first.split(":", 2) + end + + def save_credentials(user, password) + credentials_file.write("#{user}:#{password}\n") + end + + def credentials_user + credentials[0] + end + + def credentials_password + credentials[1] + end + + def ensure_credentials_exist + generate_credentials if credentials_file.contents_or_blank == "" + end + + def generate_credentials + save_credentials(generate_user, generate_password) + end + + def generate_user + generate_secret(4, 8) + end + + def generate_password + generate_secret(8, 15) + end + + def generate_secret(min, max) + chars = self.secret_characters + len = rand(max - min + 1) + min + password = "" + len.times do |index| + password += chars[rand(chars.length)] + end + password + end + + def secret_characters + [ ('a'..'z'), ('1'..'9') ].inject([]) do |chars, range| + chars += range.to_a + end + end + + # ~/.rush/tunnels contains a list of previously created ssh tunnels. The + # format is host:port, where port is the local port that the tunnel is + # listening on. + def tunnels_file + dir['tunnels'] + end + + def tunnels + tunnels_file.lines_or_empty.inject({}) do |hash, line| + host, port = line.split(':', 2) + hash[host] = port.to_i + hash + end + end + + def save_tunnels(hash) + string = "" + hash.each do |host, port| + string += "#{host}:#{port}\n" + end + tunnels_file.write string + end end diff --git a/lib/rush/dir.rb b/lib/rush/dir.rb index 52567f7..897fc98 100644 --- a/lib/rush/dir.rb +++ b/lib/rush/dir.rb @@ -27,15 +27,15 @@ def contents # Files contained in this dir only. def files - contents.select { |entry| !entry.dir? } + contents.reject(&:dir?) end # Other dirs contained in this dir only. def dirs - contents.select { |entry| entry.dir? } + contents.select(&:dir?) end - # Access subentries with square brackets, e.g. dir['subdir/file'] + # Access subentries with square brackets, e.g. dir['subdir/file'] def [](key) key = key.to_s if key == '**' @@ -115,9 +115,7 @@ def nonhidden_dirs # Contained files that are not hidden. def nonhidden_files - files.select do |file| - !file.hidden? - end + files.reject(&:hidden?) end # Run a bash command starting in this directory. Options are the same as Rush::Box#bash. diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index aa74436..df8e756 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -2,186 +2,186 @@ # Rush::Shell is used to create an interactive shell. It is invoked by the rush binary. module Rush - class Shell - attr_accessor :suppress_output - # Set up the user's environment, including a pure binding into which - # env.rb and commands.rb are mixed. - def initialize - root = Rush::Dir.new('/') - home = Rush::Dir.new(ENV['HOME']) if ENV['HOME'] - pwd = Rush::Dir.new(ENV['PWD']) if ENV['PWD'] - - @config = Rush::Config.new - - @config.load_history.each do |item| - Readline::HISTORY.push(item) - end - - Readline.basic_word_break_characters = "" - Readline.completion_append_character = nil - Readline.completion_proc = completion_proc - - @box = Rush::Box.new - @pure_binding = @box.instance_eval "binding" - $last_res = nil - - eval @config.load_env, @pure_binding - - commands = @config.load_commands - Rush::Dir.class_eval commands - Array.class_eval commands - end - - # Run a single command. - def execute(cmd) - res = eval(cmd, @pure_binding) - $last_res = res - eval("_ = $last_res", @pure_binding) - print_result res - rescue Rush::Exception => e - puts "Exception #{e.class} -> #{e.message}" - rescue ::Exception => e - puts "Exception #{e.class} -> #{e.message}" - e.backtrace.each do |t| - puts " #{::File.expand_path(t)}" - end - end - - # Run the interactive shell using readline. - def run - loop do - cmd = Readline.readline('rush> ') - - finish if cmd.nil? or cmd == 'exit' - next if cmd == "" - Readline::HISTORY.push(cmd) - - execute(cmd) - end - end - - # Save history to ~/.rush/history when the shell exists. - def finish - @config.save_history(Readline::HISTORY.to_a) - puts - exit - end - - # Nice printing of different return types, particularly Rush::SearchResults. - def print_result(res) - return if self.suppress_output - if res.kind_of? String - puts res - elsif res.kind_of? Rush::SearchResults - widest = res.entries.map { |k| k.full_path.length }.max - res.entries_with_lines.each do |entry, lines| - print entry.full_path - print ' ' * (widest - entry.full_path.length + 2) - print "=> " - print res.colorize(lines.first.strip.head(30)) - print "..." if lines.first.strip.length > 30 - if lines.size > 1 - print " (plus #{lines.size - 1} more matches)" - end - print "\n" - end - puts "#{res.entries.size} matching files with #{res.lines.size} matching lines" - elsif res.respond_to? :each - counts = {} - res.each do |item| - puts item - counts[item.class] ||= 0 - counts[item.class] += 1 - end - if counts == {} - puts "=> (empty set)" - else - count_s = counts.map do |klass, count| - "#{count} x #{klass}" - end.join(', ') - puts "=> #{count_s}" - end - else - puts "=> #{res.inspect}" - end - end - - def path_parts(input) # :nodoc: - case input - when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)([\[\/])(['"])([^\3]*)$/ - $~.to_a.slice(1, 4).push($~.pre_match) - when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)(\.)(\w*)$/ - $~.to_a.slice(1, 3).push($~.pre_match) - when /((?:@{1,2}|\$|)\w+)$/ - $~.to_a.slice(1, 1).push(nil).push($~.pre_match) - else - [ nil, nil, nil ] - end - end - - def complete_method(receiver, dot, partial_name, pre) - path = eval("#{receiver}.full_path", @pure_binding) rescue nil - box = eval("#{receiver}.box", @pure_binding) rescue nil - if path and box - (box[path].methods - Object.methods).select do |e| - e.match(/^#{Regexp.escape(partial_name)}/) - end.map do |e| - (pre || '') + receiver + dot + e - end - end - end - - def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc: - original_var, fixed_path = possible_var, '' - if /^(.+\/)([^\/]*)$/ === partial_path - fixed_path, partial_path = $~.captures - possible_var += "['#{fixed_path}']" - end - full_path = eval("#{possible_var}.full_path", @pure_binding) rescue nil - box = eval("#{possible_var}.box", @pure_binding) rescue nil - if full_path and box - Rush::Dir.new(full_path, box).entries.select do |e| - e.name.match(/^#{Regexp.escape(partial_path)}/) - end.map do |e| - (pre || '') + original_var + accessor + quote + fixed_path + e.name + (e.dir? ? "/" : "") - end - end - end - - def complete_variable(partial_name, pre) - lvars = eval('local_variables', @pure_binding) - gvars = eval('global_variables', @pure_binding) - ivars = eval('instance_variables', @pure_binding) - (lvars + gvars + ivars).select do |e| - e.match(/^#{Regexp.escape(partial_name)}/) - end.map do |e| - (pre || '') + e - end - end - - # Try to do tab completion on dir square brackets and slash accessors. - # - # Example: - # - # dir['subd # presing tab here will produce dir['subdir/ if subdir exists - # dir/'subd # presing tab here will produce dir/'subdir/ if subdir exists - # - # This isn't that cool yet, because it can't do multiple levels of subdirs. - # It does work remotely, though, which is pretty sweet. - def completion_proc - proc do |input| - receiver, accessor, *rest = path_parts(input) - if receiver - case accessor - when /^[\[\/]$/ - complete_path(receiver, accessor, *rest) - when /^\.$/ - complete_method(receiver, accessor, *rest) - when nil - complete_variable(receiver, *rest) - end - end - end - end - end + class Shell + attr_accessor :suppress_output + # Set up the user's environment, including a pure binding into which + # env.rb and commands.rb are mixed. + def initialize + root = Rush::Dir.new('/') + home = Rush::Dir.new(ENV['HOME']) if ENV['HOME'] + pwd = Rush::Dir.new(ENV['PWD']) if ENV['PWD'] + + @config = Rush::Config.new + + @config.load_history.each do |item| + Readline::HISTORY.push(item) + end + + Readline.basic_word_break_characters = "" + Readline.completion_append_character = nil + Readline.completion_proc = completion_proc + + @box = Rush::Box.new + @pure_binding = @box.instance_eval "binding" + $last_res = nil + + eval @config.load_env, @pure_binding + + commands = @config.load_commands + Rush::Dir.class_eval commands + Array.class_eval commands + end + + # Run a single command. + def execute(cmd) + res = eval(cmd, @pure_binding) + $last_res = res + eval("_ = $last_res", @pure_binding) + print_result res + rescue Rush::Exception => e + puts "Exception #{e.class} -> #{e.message}" + rescue ::Exception => e + puts "Exception #{e.class} -> #{e.message}" + e.backtrace.each do |t| + puts " #{::File.expand_path(t)}" + end + end + + # Run the interactive shell using readline. + def run + loop do + cmd = Readline.readline('rush> ') + + finish if cmd.nil? or cmd == 'exit' + next if cmd == "" + Readline::HISTORY.push(cmd) + + execute(cmd) + end + end + + # Save history to ~/.rush/history when the shell exists. + def finish + @config.save_history(Readline::HISTORY.to_a) + puts + exit + end + + # Nice printing of different return types, particularly Rush::SearchResults. + def print_result(res) + return if self.suppress_output + if res.kind_of? String + puts res + elsif res.kind_of? Rush::SearchResults + widest = res.entries.map { |k| k.full_path.length }.max + res.entries_with_lines.each do |entry, lines| + print entry.full_path + print ' ' * (widest - entry.full_path.length + 2) + print "=> " + print res.colorize(lines.first.strip.head(30)) + print "..." if lines.first.strip.length > 30 + if lines.size > 1 + print " (plus #{lines.size - 1} more matches)" + end + print "\n" + end + puts "#{res.entries.size} matching files with #{res.lines.size} matching lines" + elsif res.respond_to? :each + counts = {} + res.each do |item| + puts item + counts[item.class] ||= 0 + counts[item.class] += 1 + end + if counts == {} + puts "=> (empty set)" + else + count_s = counts.map do |klass, count| + "#{count} x #{klass}" + end.join(', ') + puts "=> #{count_s}" + end + else + puts "=> #{res.inspect}" + end + end + + def path_parts(input) # :nodoc: + case input + when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)([\[\/])(['"])([^\3]*)$/ + $~.to_a.slice(1, 4).push($~.pre_match) + when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)(\.)(\w*)$/ + $~.to_a.slice(1, 3).push($~.pre_match) + when /((?:@{1,2}|\$|)\w+)$/ + $~.to_a.slice(1, 1).push(nil).push($~.pre_match) + else + [ nil, nil, nil ] + end + end + + def complete_method(receiver, dot, partial_name, pre) + path = eval("#{receiver}.full_path", @pure_binding) rescue nil + box = eval("#{receiver}.box", @pure_binding) rescue nil + if path and box + (box[path].methods - Object.methods).select do |e| + e.match(/^#{Regexp.escape(partial_name)}/) + end.map do |e| + (pre || '') + receiver + dot + e + end + end + end + + def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc: + original_var, fixed_path = possible_var, '' + if /^(.+\/)([^\/]*)$/ === partial_path + fixed_path, partial_path = $~.captures + possible_var += "['#{fixed_path}']" + end + full_path = eval("#{possible_var}.full_path", @pure_binding) rescue nil + box = eval("#{possible_var}.box", @pure_binding) rescue nil + if full_path and box + Rush::Dir.new(full_path, box).entries.select do |e| + e.name.match(/^#{Regexp.escape(partial_path)}/) + end.map do |e| + (pre || '') + original_var + accessor + quote + fixed_path + e.name + (e.dir? ? "/" : "") + end + end + end + + def complete_variable(partial_name, pre) + lvars = eval('local_variables', @pure_binding) + gvars = eval('global_variables', @pure_binding) + ivars = eval('instance_variables', @pure_binding) + (lvars + gvars + ivars).select do |e| + e.match(/^#{Regexp.escape(partial_name)}/) + end.map do |e| + (pre || '') + e + end + end + + # Try to do tab completion on dir square brackets and slash accessors. + # + # Example: + # + # dir['subd # presing tab here will produce dir['subdir/ if subdir exists + # dir/'subd # presing tab here will produce dir/'subdir/ if subdir exists + # + # This isn't that cool yet, because it can't do multiple levels of subdirs. + # It does work remotely, though, which is pretty sweet. + def completion_proc + proc do |input| + receiver, accessor, *rest = path_parts(input) + if receiver + case accessor + when /^[\[\/]$/ + complete_path(receiver, accessor, *rest) + when /^\.$/ + complete_method(receiver, accessor, *rest) + when nil + complete_variable(receiver, *rest) + end + end + end + end + end end diff --git a/tags b/tags new file mode 100644 index 0000000..85680d6 --- /dev/null +++ b/tags @@ -0,0 +1,312 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.9~svn20110310 // +== lib/rush/box.rb /^ def ==(other) # :nodoc:$/;" f class:Rush +== lib/rush/entry.rb /^ def ==(other) # :nodoc:$/;" f class:Rush +== lib/rush/process.rb /^ def ==(other) # :nodoc:$/;" f class:Rush +== lib/rush/process_set.rb /^ def ==(other)$/;" f class:Rush +Array lib/rush/array_ext.rb /^class Array$/;" c +BadAccessSpecifier lib/rush/exceptions.rb /^ class BadAccessSpecifier < Exception; end$/;" c class:Rush +BashFailed lib/rush/exceptions.rb /^ class BashFailed < Exception; end$/;" c class:Rush +DoesNotExist lib/rush/exceptions.rb /^ class DoesNotExist < Exception; end$/;" c class:Rush +EmbeddableShell lib/rush/embeddable_shell.rb /^ class EmbeddableShell$/;" c class:Rush +Exception lib/rush/exceptions.rb /^ class Exception < ::RuntimeError; end$/;" c class:Rush +Exception lib/rush/server.rb /^class Exception$/;" c +FailedTransmit lib/rush/exceptions.rb /^ class FailedTransmit < Exception; end$/;" c class:Rush +Fixnum lib/rush/fixnum_ext.rb /^class Fixnum$/;" c +Foo spec/find_by_spec.rb /^ class Foo$/;" c +NameAlreadyExists lib/rush/entry.rb /^ class NameAlreadyExists < Exception; end$/;" c class:Rush +NameAlreadyExists lib/rush/exceptions.rb /^ class NameAlreadyExists < Exception; end$/;" c class:Rush +NameCannotContainSlash lib/rush/entry.rb /^ class NameCannotContainSlash < Exception; end$/;" c class:Rush +NameCannotContainSlash lib/rush/exceptions.rb /^ class NameCannotContainSlash < Exception; end$/;" c class:Rush +NoPortSelectedYet lib/rush/ssh_tunnel.rb /^ class NoPortSelectedYet < Exception; end$/;" c class:Rush +NotADir lib/rush/exceptions.rb /^ class NotADir < Exception; end$/;" c class:Rush +NotAuthorized lib/rush/exceptions.rb /^ class NotAuthorized < Exception; end$/;" c class:Rush +Rush lib/rush.rb /^module Rush$/;" m +Rush lib/rush.rb /^module Rush::Connection; end$/;" m +Rush lib/rush/access.rb /^class Rush::Access$/;" c +Rush lib/rush/box.rb /^class Rush::Box$/;" c +Rush lib/rush/commands.rb /^module Rush::Commands$/;" m +Rush lib/rush/config.rb /^class Rush::Config$/;" c +Rush lib/rush/dir.rb /^class Rush::Dir < Rush::Entry$/;" c +Rush lib/rush/embeddable_shell.rb /^module Rush$/;" m +Rush lib/rush/entry.rb /^class Rush::Entry$/;" c +Rush lib/rush/exceptions.rb /^module Rush$/;" m +Rush lib/rush/file.rb /^class Rush::File < Rush::Entry$/;" c +Rush lib/rush/find_by.rb /^module Rush::FindBy$/;" m +Rush lib/rush/head_tail.rb /^module Rush::HeadTail$/;" m +Rush lib/rush/local.rb /^class Rush::Connection::Local$/;" c +Rush lib/rush/process.rb /^class Rush::Process$/;" c +Rush lib/rush/process_set.rb /^class Rush::ProcessSet$/;" c +Rush lib/rush/remote.rb /^class Rush::Connection::Remote$/;" c +Rush lib/rush/search_results.rb /^class Rush::SearchResults$/;" c +Rush lib/rush/shell.rb /^module Rush$/;" m +Rush lib/rush/ssh_tunnel.rb /^class Rush::SshTunnel$/;" c +RushHandler lib/rush/server.rb /^class RushHandler < Mongrel::HttpHandler$/;" c +RushServer lib/rush/server.rb /^class RushServer$/;" c +RushdNotRunning lib/rush/exceptions.rb /^ class RushdNotRunning < Exception; end$/;" c class:Rush +Shell lib/rush/shell.rb /^ class Shell$/;" c class:Rush +SshFailed lib/rush/ssh_tunnel.rb /^ class SshFailed < Exception; end$/;" c class:Rush +String lib/rush/string_ext.rb /^class String$/;" c +UnknownAction lib/rush/local.rb /^ class UnknownAction < Exception; end$/;" c +[] lib/rush.rb /^ def self.[](key)$/;" F class:Rush +[] lib/rush/box.rb /^ def [](key)$/;" f class:Rush +[] lib/rush/dir.rb /^ def [](key)$/;" f class:Rush +access lib/rush/entry.rb /^ def access$/;" f class:Rush +access= lib/rush/entry.rb /^ def access=(options)$/;" f class:Rush +add lib/rush/search_results.rb /^ def add(entry, lines)$/;" f class:Rush +alive? lib/rush/box.rb /^ def alive?$/;" f class:Rush +alive? lib/rush/local.rb /^ def alive?$/;" f +alive? lib/rush/process.rb /^ def alive?$/;" f class:Rush +alive? lib/rush/process_set.rb /^ def alive?$/;" f class:Rush +alive? lib/rush/remote.rb /^ def alive?$/;" f class:Rush +all lib/rush/process.rb /^ def self.all$/;" F class:Rush +append lib/rush/file.rb /^ def append(contents)$/;" f class:Rush +append_to_file lib/rush/local.rb /^ def append_to_file(full_path, contents)$/;" f class:Rush +append_to_file lib/rush/remote.rb /^ def append_to_file(full_path, contents)$/;" f class:Rush +apply lib/rush/access.rb /^ def apply(full_path)$/;" f class:Rush +authorize lib/rush/server.rb /^ def authorize(auth)$/;" f +bash lib/rush.rb /^ def self.bash(command, options={})$/;" F class:Rush +bash lib/rush/box.rb /^ def bash(command, options={})$/;" f class:Rush +bash lib/rush/dir.rb /^ def bash(command, options={})$/;" f class:Rush +bash lib/rush/local.rb /^ def bash(command, user=nil, background=false, reset_environment=false)$/;" f +bash lib/rush/remote.rb /^ def bash(command, user, background, reset_environment)$/;" f class:Rush +bash_background lib/rush/local.rb /^ def bash_background(command, user, reset_environment)$/;" f +box lib/rush.rb /^ def self.box$/;" F class:Rush +box lib/rush/server.rb /^ def box$/;" f +changed_at lib/rush/entry.rb /^ def changed_at$/;" f class:Rush +children lib/rush/process.rb /^ def children$/;" f class:Rush +close_all_descriptors lib/rush/local.rb /^ def close_all_descriptors(keep_open = [])$/;" f +colorize lib/rush/search_results.rb /^ def colorize(line)$/;" f class:Rush +command_with_environment lib/rush/box.rb /^ def command_with_environment(command, env) # :nodoc:$/;" f class:Rush +commands_file lib/rush/config.rb /^ def commands_file$/;" f class:Rush +compare_or_match lib/rush/find_by.rb /^ def compare_or_match(value, against)$/;" f class:Rush +complete_method lib/rush/shell.rb /^ def complete_method(receiver, dot, partial_name, pre)$/;" f class:Rush.Shell.print_result +complete_path lib/rush/shell.rb /^ def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc:$/;" f class:Rush.Shell.print_result.complete_method +complete_variable lib/rush/shell.rb /^ def complete_variable(partial_name, pre)$/;" f class:Rush.Shell.print_result.complete_method.complete_path +completion_proc lib/rush/shell.rb /^ def completion_proc$/;" f class:Rush.Shell.print_result.complete_method.complete_path.complete_variable +config lib/rush/remote.rb /^ def config$/;" f class:Rush +config lib/rush/server.rb /^ def config$/;" f +config lib/rush/ssh_tunnel.rb /^ def config$/;" f class:Rush +connection lib/rush/box.rb /^ def connection # :nodoc:$/;" f class:Rush +connection lib/rush/entry.rb /^ def connection$/;" f class:Rush +contents lib/rush/dir.rb /^ def contents$/;" f class:Rush +contents lib/rush/file.rb /^ def contents$/;" f class:Rush +contents_or_blank lib/rush/file.rb /^ def contents_or_blank$/;" f class:Rush +copy lib/rush/local.rb /^ def copy(src, dst)$/;" f class:Rush +copy lib/rush/remote.rb /^ def copy(src, dst)$/;" f class:Rush +copy_to lib/rush/entry.rb /^ def copy_to(dir)$/;" f class:Rush +create lib/rush/dir.rb /^ def create$/;" f class:Rush +create lib/rush/file.rb /^ def create$/;" f class:Rush +create_dir lib/rush/dir.rb /^ def create_dir(name)$/;" f class:Rush +create_dir lib/rush/local.rb /^ def create_dir(full_path)$/;" f class:Rush +create_dir lib/rush/remote.rb /^ def create_dir(full_path)$/;" f class:Rush +create_file lib/rush/dir.rb /^ def create_file(name)$/;" f class:Rush +credentials lib/rush/config.rb /^ def credentials$/;" f class:Rush +credentials_file lib/rush/config.rb /^ def credentials_file$/;" f class:Rush +credentials_password lib/rush/config.rb /^ def credentials_password$/;" f class:Rush +credentials_user lib/rush/config.rb /^ def credentials_user$/;" f class:Rush +destroy lib/rush/entry.rb /^ def destroy$/;" f class:Rush +destroy lib/rush/local.rb /^ def destroy(full_path)$/;" f class:Rush +destroy lib/rush/remote.rb /^ def destroy(full_path)$/;" f class:Rush +dir lib/rush.rb /^ def self.dir(filename)$/;" F class:Rush +dir? lib/rush/dir.rb /^ def dir?$/;" f class:Rush +dir? lib/rush/file.rb /^ def dir?$/;" f class:Rush +dirs lib/rush/dir.rb /^ def dirs$/;" f class:Rush +dirs_flattened lib/rush/dir.rb /^ def dirs_flattened$/;" f class:Rush +display lib/rush/ssh_tunnel.rb /^ def display(msg)$/;" f class:Rush +display_hash lib/rush/access.rb /^ def display_hash$/;" f class:Rush +duplicate lib/rush/entry.rb /^ def duplicate(new_name)$/;" f class:Rush +each lib/rush/process_set.rb /^ def each$/;" f class:Rush +each lib/rush/search_results.rb /^ def each(&block)$/;" f class:Rush +ensure_credentials_exist lib/rush/config.rb /^ def ensure_credentials_exist$/;" f class:Rush +ensure_tunnel lib/rush/local.rb /^ def ensure_tunnel(options={})$/;" f +ensure_tunnel lib/rush/remote.rb /^ def ensure_tunnel(options={})$/;" f class:Rush +ensure_tunnel lib/rush/ssh_tunnel.rb /^ def ensure_tunnel(options={})$/;" f class:Rush +entries lib/rush/array_ext.rb /^ def entries$/;" f class:Array +entries lib/rush/commands.rb /^ def entries$/;" f class:Rush +entries lib/rush/dir.rb /^ def entries$/;" f class:Rush +entries lib/rush/file.rb /^ def entries$/;" f class:Rush +entries_tree lib/rush/dir.rb /^ def entries_tree$/;" f class:Rush +env_file lib/rush/config.rb /^ def env_file$/;" f class:Rush +establish_connection lib/rush/box.rb /^ def establish_connection(options={})$/;" f class:Rush +establish_tunnel lib/rush/ssh_tunnel.rb /^ def establish_tunnel(options={})$/;" f class:Rush +execute lib/rush/shell.rb /^ def execute(cmd)$/;" f class:Rush.Shell +execute_in_shell lib/rush/embeddable_shell.rb /^ def execute_in_shell(&block)$/;" f class:Rush.EmbeddableShell +exists? lib/rush/entry.rb /^ def exists?$/;" f class:Rush +extract_list lib/rush/access.rb /^ def extract_list(type, value, choices)$/;" f class:Rush +factory lib/rush/entry.rb /^ def self.factory(full_path, box=nil)$/;" F class:Rush +file_contents lib/rush/local.rb /^ def file_contents(full_path)$/;" f class:Rush +file_contents lib/rush/remote.rb /^ def file_contents(full_path)$/;" f class:Rush +files lib/rush/dir.rb /^ def files$/;" f class:Rush +files_flattened lib/rush/dir.rb /^ def files_flattened$/;" f class:Rush +filesystem lib/rush/box.rb /^ def filesystem$/;" f class:Rush +filter lib/rush/process_set.rb /^ def filter(conditions)$/;" f class:Rush +filtered_backtrace lib/rush/server.rb /^ def filtered_backtrace$/;" f class:Exception +find_all_by lib/rush/find_by.rb /^ def find_all_by(field, arg)$/;" f class:Rush +find_by lib/rush/find_by.rb /^ def find_by(field, arg)$/;" f class:Rush +find_by_glob lib/rush/dir.rb /^ def find_by_glob(glob) # :nodoc:$/;" f class:Rush +find_by_name lib/rush/dir.rb /^ def find_by_name(name) # :nodoc:$/;" f class:Rush +finish lib/rush/shell.rb /^ def finish$/;" f class:Rush.Shell +from_hash lib/rush/access.rb /^ def from_hash(hash)$/;" f class:Rush +from_hash lib/rush/access.rb /^ def self.from_hash(hash)$/;" F class:Rush +from_octal lib/rush/access.rb /^ def from_octal(mode)$/;" f class:Rush +full_display lib/rush/server.rb /^ def full_display$/;" f class:Exception +full_path lib/rush/dir.rb /^ def full_path$/;" f class:Rush +full_path lib/rush/entry.rb /^ def full_path$/;" f class:Rush +gb lib/rush/fixnum_ext.rb /^ def gb$/;" f class:Fixnum +generate_credentials lib/rush/config.rb /^ def generate_credentials$/;" f class:Rush +generate_password lib/rush/config.rb /^ def generate_password$/;" f class:Rush +generate_secret lib/rush/config.rb /^ def generate_secret(min, max)$/;" f class:Rush +generate_user lib/rush/config.rb /^ def generate_user$/;" f class:Rush +git lib/rush/dir.rb /^ def git(*args)$/;" f class:Rush +head lib/rush/head_tail.rb /^ def head(n)$/;" f class:Rush +hidden? lib/rush/entry.rb /^ def hidden?$/;" f class:Rush +hilight lib/rush/search_results.rb /^ def hilight$/;" f class:Rush +history_file lib/rush/config.rb /^ def history_file$/;" f class:Rush +host lib/rush/ssh_tunnel.rb /^ def host$/;" f class:Rush +index lib/rush/local.rb /^ def index(base_path, glob)$/;" f +index lib/rush/remote.rb /^ def index(base_path, glob)$/;" f class:Rush +initialize lib/rush/box.rb /^ def initialize(host='localhost')$/;" f class:Rush +initialize lib/rush/config.rb /^ def initialize(location=nil)$/;" f class:Rush +initialize lib/rush/embeddable_shell.rb /^ def initialize(suppress_output = true)$/;" f class:Rush.EmbeddableShell +initialize lib/rush/entry.rb /^ def initialize(full_path, box=nil)$/;" f class:Rush +initialize lib/rush/process.rb /^ def initialize(params, box)$/;" f class:Rush +initialize lib/rush/process_set.rb /^ def initialize(processes)$/;" f class:Rush +initialize lib/rush/remote.rb /^ def initialize(host)$/;" f class:Rush +initialize lib/rush/search_results.rb /^ def initialize(pattern)$/;" f class:Rush +initialize lib/rush/shell.rb /^ def initialize$/;" f class:Rush.Shell +initialize lib/rush/ssh_tunnel.rb /^ def initialize(real_host)$/;" f class:Rush +initialize spec/find_by_spec.rb /^ def initialize(bar)$/;" f class:Foo +inspect lib/rush/box.rb /^ def inspect # :nodoc:$/;" f class:Rush +inspect lib/rush/entry.rb /^ def inspect # :nodoc:$/;" f class:Rush +inspect lib/rush/process.rb /^ def inspect # :nodoc:$/;" f class:Rush +kb lib/rush/fixnum_ext.rb /^ def kb$/;" f class:Fixnum +kill lib/rush/process.rb /^ def kill(options={})$/;" f class:Rush +kill lib/rush/process_set.rb /^ def kill(options={})$/;" f class:Rush +kill_process lib/rush/local.rb /^ def kill_process(pid, options={})$/;" f +kill_process lib/rush/remote.rb /^ def kill_process(pid, options={})$/;" f class:Rush +last_accessed lib/rush/entry.rb /^ def last_accessed$/;" f class:Rush +last_modified lib/rush/entry.rb /^ def last_modified$/;" f class:Rush +launch_dir lib/rush.rb /^ def self.launch_dir$/;" F class:Rush +launch_rushd lib/rush/ssh_tunnel.rb /^ def launch_rushd$/;" f class:Rush +line_count lib/rush/commands.rb /^ def line_count$/;" f class:Rush +line_count lib/rush/file.rb /^ def line_count$/;" f class:Rush +lines lib/rush/file.rb /^ def lines$/;" f class:Rush +lines_or_empty lib/rush/file.rb /^ def lines_or_empty$/;" f class:Rush +linux_processes lib/rush/local.rb /^ def linux_processes$/;" f +load_commands lib/rush/config.rb /^ def load_commands$/;" f class:Rush +load_env lib/rush/config.rb /^ def load_env$/;" f class:Rush +load_history lib/rush/config.rb /^ def load_history$/;" f class:Rush +log lib/rush/server.rb /^ def log(msg)$/;" f +lowlight lib/rush/search_results.rb /^ def lowlight$/;" f class:Rush +ls lib/rush/dir.rb /^ def ls$/;" f class:Rush +make_connection lib/rush/box.rb /^ def make_connection # :nodoc:$/;" f class:Rush +make_entries lib/rush/dir.rb /^ def make_entries(filenames)$/;" f class:Rush +make_ssh_tunnel lib/rush/ssh_tunnel.rb /^ def make_ssh_tunnel(options={})$/;" f class:Rush +mate lib/rush/commands.rb /^ def mate(*args)$/;" f class:Rush +mb lib/rush/fixnum_ext.rb /^ def mb$/;" f class:Fixnum +method_missing lib/rush/embeddable_shell.rb /^ def method_missing(sym, *args, &block)$/;" f class:Rush.EmbeddableShell +method_missing lib/rush/find_by.rb /^ def method_missing(meth, *args)$/;" f class:Rush +method_missing lib/rush/process_set.rb /^ def method_missing(meth, *args)$/;" f class:Rush +mimic lib/rush/entry.rb /^ def mimic(from) # :nodoc:$/;" f class:Rush +mock_config spec/base.rb /^def mock_config(&block)$/;" f +mock_config_cleanup spec/base.rb /^def mock_config_cleanup$/;" f +mock_config_sandbox_dir spec/base.rb /^def mock_config_sandbox_dir$/;" f +mock_config_start spec/base.rb /^def mock_config_start$/;" f +move_to lib/rush/entry.rb /^ def move_to(dir)$/;" f class:Rush +my_process lib/rush.rb /^ def self.my_process$/;" F class:Rush +next_available_port lib/rush/ssh_tunnel.rb /^ def next_available_port$/;" f class:Rush +nonhidden_dirs lib/rush/dir.rb /^ def nonhidden_dirs$/;" f class:Rush +nonhidden_files lib/rush/dir.rb /^ def nonhidden_files$/;" f class:Rush +normal lib/rush/search_results.rb /^ def normal$/;" f class:Rush +octal_integer_array lib/rush/access.rb /^ def octal_integer_array(mode)$/;" f class:Rush +octal_permissions lib/rush/access.rb /^ def octal_permissions$/;" f class:Rush +os_x_processes lib/rush/local.rb /^ def os_x_processes$/;" f +os_x_raw_ps lib/rush/local.rb /^ def os_x_raw_ps$/;" f +parent lib/rush/entry.rb /^ def parent$/;" f class:Rush +parent lib/rush/process.rb /^ def parent$/;" f class:Rush +parse lib/rush/access.rb /^ def parse(options)$/;" f class:Rush +parse lib/rush/access.rb /^ def self.parse(options)$/;" F class:Rush +parse_exception lib/rush/remote.rb /^ def parse_exception(body)$/;" f class:Rush +parse_oleprocinfo lib/rush/local.rb /^ def parse_oleprocinfo(proc_info)$/;" f +parse_ps lib/rush/local.rb /^ def parse_ps(line)$/;" f +parts_from lib/rush/access.rb /^ def parts_from(value)$/;" f class:Rush +passwords lib/rush/config.rb /^ def passwords$/;" f class:Rush +passwords_file lib/rush/config.rb /^ def passwords_file$/;" f class:Rush +path_parts lib/rush/shell.rb /^ def path_parts(input) # :nodoc:$/;" f class:Rush.Shell.print_result +permissions lib/rush/access.rb /^ def self.permissions$/;" F class:Rush +port lib/rush/ssh_tunnel.rb /^ def port$/;" f class:Rush +print_result lib/rush/shell.rb /^ def print_result(res)$/;" f class:Rush.Shell +process lib/rush/server.rb /^ def process(request, response)$/;" f class:RushHandler +process_alive lib/rush/local.rb /^ def process_alive(pid)$/;" f +process_alive lib/rush/remote.rb /^ def process_alive(pid)$/;" f class:Rush +process_result lib/rush/remote.rb /^ def process_result(code, body)$/;" f class:Rush +processes lib/rush.rb /^ def self.processes$/;" F class:Rush +processes lib/rush/box.rb /^ def processes$/;" f class:Rush +processes lib/rush/local.rb /^ def processes$/;" f +processes lib/rush/remote.rb /^ def processes$/;" f class:Rush +purge lib/rush/dir.rb /^ def purge$/;" f class:Rush +purge lib/rush/local.rb /^ def purge(full_path)$/;" f class:Rush +purge lib/rush/remote.rb /^ def purge(full_path)$/;" f class:Rush +push_credentials lib/rush/ssh_tunnel.rb /^ def push_credentials$/;" f class:Rush +quote lib/rush.rb /^ def self.quote(path)$/;" F class:Rush +quoted_path lib/rush/entry.rb /^ def quoted_path$/;" f class:Rush +rake lib/rush/dir.rb /^ def rake(*args)$/;" f class:Rush +read_archive lib/rush/local.rb /^ def read_archive(full_path)$/;" f class:Rush +read_archive lib/rush/remote.rb /^ def read_archive(full_path)$/;" f class:Rush +read_proc_file lib/rush/local.rb /^ def read_proc_file(file)$/;" f +receive lib/rush/local.rb /^ def receive(params)$/;" f +rename lib/rush/entry.rb /^ def rename(new_name)$/;" f class:Rush +rename lib/rush/local.rb /^ def rename(path, name, new_name)$/;" f class:Rush +rename lib/rush/remote.rb /^ def rename(path, name, new_name)$/;" f class:Rush +replace_contents! lib/rush/commands.rb /^ def replace_contents!(pattern, with_text)$/;" f class:Rush +replace_contents! lib/rush/file.rb /^ def replace_contents!(pattern, replace_with)$/;" f class:Rush +resolve_unix_uid_to_user lib/rush/local.rb /^ def resolve_unix_uid_to_user(uid)$/;" f +resolve_unix_uids lib/rush/local.rb /^ def resolve_unix_uids(list)$/;" f +roles lib/rush/access.rb /^ def self.roles$/;" F class:Rush +run lib/rush/server.rb /^ def run$/;" f class:RushServer +run lib/rush/shell.rb /^ def run$/;" f class:Rush.Shell +save_credentials lib/rush/config.rb /^ def save_credentials(user, password)$/;" f class:Rush +save_history lib/rush/config.rb /^ def save_history(array)$/;" f class:Rush +save_tunnels lib/rush/config.rb /^ def save_tunnels(hash)$/;" f class:Rush +search lib/rush/commands.rb /^ def search(pattern)$/;" f class:Rush +search lib/rush/file.rb /^ def search(pattern)$/;" f class:Rush +secret_characters lib/rush/config.rb /^ def secret_characters$/;" f class:Rush +set_access lib/rush/local.rb /^ def set_access(full_path, access)$/;" f +set_access lib/rush/remote.rb /^ def set_access(full_path, access)$/;" f class:Rush +set_matrix lib/rush/access.rb /^ def set_matrix(perms, roles)$/;" f class:Rush +setup_everything lib/rush/ssh_tunnel.rb /^ def setup_everything(options={})$/;" f class:Rush +size lib/rush/dir.rb /^ def size$/;" f class:Rush +size lib/rush/file.rb /^ def size$/;" f class:Rush +size lib/rush/local.rb /^ def size(full_path)$/;" f +size lib/rush/remote.rb /^ def size(full_path)$/;" f class:Rush +ssh lib/rush/ssh_tunnel.rb /^ def ssh(command)$/;" f class:Rush +ssh_append_to_credentials lib/rush/ssh_tunnel.rb /^ def ssh_append_to_credentials(string)$/;" f class:Rush +ssh_stall_command lib/rush/ssh_tunnel.rb /^ def ssh_stall_command(options={})$/;" f class:Rush +ssh_tunnel_command lib/rush/ssh_tunnel.rb /^ def ssh_tunnel_command(options={})$/;" f class:Rush +ssh_tunnel_command_without_stall lib/rush/ssh_tunnel.rb /^ def ssh_tunnel_command_without_stall$/;" f class:Rush +stat lib/rush/entry.rb /^ def stat$/;" f class:Rush +stat lib/rush/local.rb /^ def stat(full_path)$/;" f +stat lib/rush/remote.rb /^ def stat(full_path)$/;" f class:Rush +tail lib/rush/head_tail.rb /^ def tail(n)$/;" f class:Rush +to_hash lib/rush/access.rb /^ def to_hash$/;" f class:Rush +to_s lib/rush/box.rb /^ def to_s # :nodoc:$/;" f class:Rush +to_s lib/rush/entry.rb /^ def to_s # :nodoc:$/;" f class:Rush +to_s lib/rush/process.rb /^ def to_s # :nodoc:$/;" f class:Rush +transmit lib/rush/remote.rb /^ def transmit(params)$/;" f class:Rush +tunnel_alive? lib/rush/ssh_tunnel.rb /^ def tunnel_alive?$/;" f class:Rush +tunnel_count_command lib/rush/ssh_tunnel.rb /^ def tunnel_count_command$/;" f class:Rush +tunnel_options lib/rush/ssh_tunnel.rb /^ def tunnel_options$/;" f class:Rush +tunnels lib/rush/config.rb /^ def tunnels$/;" f class:Rush +tunnels_file lib/rush/config.rb /^ def tunnels_file$/;" f class:Rush +vi lib/rush/commands.rb /^ def vi(*args)$/;" f class:Rush +windows_processes lib/rush/local.rb /^ def windows_processes$/;" f +write lib/rush/file.rb /^ def write(new_contents)$/;" f class:Rush +write_archive lib/rush/local.rb /^ def write_archive(archive, dir)$/;" f class:Rush +write_archive lib/rush/remote.rb /^ def write_archive(archive, dir)$/;" f class:Rush +write_file lib/rush/local.rb /^ def write_file(full_path, contents)$/;" f class:Rush +write_file lib/rush/remote.rb /^ def write_file(full_path, contents)$/;" f class:Rush From 66645cfc9c50ed168ced0104caca67b543f9adab Mon Sep 17 00:00:00 2001 From: Pavel Ivanov Date: Thu, 27 Feb 2014 23:40:59 +0400 Subject: [PATCH 002/119] Ignore .swp files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f4a845e..7b42034 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ rdoc pkg .rbenv-version +*.swp From 46bd804fd4d4c1213b8c289e06da1822d3ce5a1e Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 2 Mar 2014 11:08:59 +0400 Subject: [PATCH 003/119] Retab, hack to run bash commands without pain --- lib/rush.rb | 90 ++-- lib/rush/box.rb | 18 + lib/rush/embeddable_shell.rb | 46 +-- lib/rush/entry.rb | 352 ++++++++-------- lib/rush/exceptions.rb | 40 +- lib/rush/file.rb | 134 +++--- lib/rush/find_by.rb | 52 +-- lib/rush/fixnum_ext.rb | 18 +- lib/rush/local.rb | 771 +++++++++++++++++------------------ lib/rush/process.rb | 114 +++--- lib/rush/process_set.rb | 106 ++--- lib/rush/search_results.rb | 88 ++-- lib/rush/server.rb | 198 ++++----- lib/rush/shell.rb | 3 +- 14 files changed, 1019 insertions(+), 1011 deletions(-) diff --git a/lib/rush.rb b/lib/rush.rb index c192d91..ff17f1c 100644 --- a/lib/rush.rb +++ b/lib/rush.rb @@ -3,62 +3,62 @@ # The top-level Rush module has some convenience methods for accessing the # local box. module Rush - # Access the root filesystem of the local box. Example: - # - # Rush['/etc/hosts'].contents - # - def self.[](key) - box[key] - end + # Access the root filesystem of the local box. Example: + # + # Rush['/etc/hosts'].contents + # + def self.[](key) + box[key] + end - # Create a dir object from the path of a provided file. Example: - # - # Rush.dir(__FILE__).files - # - def self.dir(filename) - box[::File.expand_path(::File.dirname(filename)) + '/'] - end + # Create a dir object from the path of a provided file. Example: + # + # Rush.dir(__FILE__).files + # + def self.dir(filename) + box[::File.expand_path(::File.dirname(filename)) + '/'] + end - # Create a dir object based on the shell's current working directory at the - # time the program was run. Example: - # - # Rush.launch_dir.files - # - def self.launch_dir - box[::Dir.pwd + '/'] - end + # Create a dir object based on the shell's current working directory at the + # time the program was run. Example: + # + # Rush.launch_dir.files + # + def self.launch_dir + box[::Dir.pwd + '/'] + end - # Run a bash command in the root of the local machine. Equivalent to - # Rush::Box.new.bash. - def self.bash(command, options={}) - box.bash(command, options) - end + # Run a bash command in the root of the local machine. Equivalent to + # Rush::Box.new.bash. + def self.bash(command, options={}) + box.bash(command, options) + end - # Pull the process list for the local machine. Example: + # Pull the process list for the local machine. Example: # # Rush.processes.filter(:cmdline => /ruby/) - # - def self.processes - box.processes - end + # + def self.processes + box.processes + end - # Get the process object for this program's PID. Example: + # Get the process object for this program's PID. Example: # # puts "I'm using #{Rush.my_process.mem} blocks of memory" - # - def self.my_process - box.processes.filter(:pid => ::Process.pid).first - end + # + def self.my_process + box.processes.filter(:pid => ::Process.pid).first + end - # Create a box object for localhost. - def self.box - @@box = Rush::Box.new - end + # Create a box object for localhost. + def self.box + @@box = Rush::Box.new + end - # Quote a path for use in backticks, say. - def self.quote(path) - path.gsub(/(?=[^a-zA-Z0-9_.\/\-\x7F-\xFF\n])/n, '\\').gsub(/\n/, "'\n'").sub(/^$/, "''") - end + # Quote a path for use in backticks, say. + def self.quote(path) + path.gsub(/(?=[^a-zA-Z0-9_.\/\-\x7F-\xFF\n])/n, '\\').gsub(/\n/, "'\n'").sub(/^$/, "''") + end end module Rush::Connection; end diff --git a/lib/rush/box.rb b/lib/rush/box.rb index 83264dd..ac6e868 100644 --- a/lib/rush/box.rb +++ b/lib/rush/box.rb @@ -49,6 +49,24 @@ def processes ) end + # FAST HACK! + # Guess if method missing then it's bash command. + # The logic is the following: + # Ruby has Symbol type for similar things that bash has '-'-keys. + # Ruby also use Symbol for keys like '--key', but bash supports + # sequences like 'ls -lah' and I don't know how to distinguish them. + # + # Usage: + # ls :lah + # ls '--help' + # + def method_missing(meth, *args) + command = args.map do |c| + Symbol === c ? c.to_s.chars.map { |x| '-' << x } : c + end.flatten.join(' ') + system meth.to_s << ' ' << command + end + # Execute a command in the standard unix shell. Returns the contents of # stdout if successful, or raises Rush::BashFailed with the output of stderr # if the shell returned a non-zero value. Options: diff --git a/lib/rush/embeddable_shell.rb b/lib/rush/embeddable_shell.rb index 71f5770..114c56f 100644 --- a/lib/rush/embeddable_shell.rb +++ b/lib/rush/embeddable_shell.rb @@ -1,26 +1,26 @@ require 'rush/shell' module Rush - # This is a class that can be embedded in other applications - # rake tasks, utility scripts, etc - # - # Delegates unknown method calls to a Rush::Shell instance - class EmbeddableShell - attr_accessor :shell - def initialize(suppress_output = true) - self.shell = Rush::Shell.new - shell.suppress_output = suppress_output - end - - # evalutes and unkown method call agains the rush shell - def method_missing(sym, *args, &block) - shell.execute sym.to_s - $last_res - end - - # take a whole block and execute it as if it were inside a shell - def execute_in_shell(&block) - self.instance_eval(&block) - end - end -end \ No newline at end of file + # This is a class that can be embedded in other applications + # rake tasks, utility scripts, etc + # + # Delegates unknown method calls to a Rush::Shell instance + class EmbeddableShell + attr_accessor :shell + def initialize(suppress_output = true) + self.shell = Rush::Shell.new + shell.suppress_output = suppress_output + end + + # evalutes and unkown method call agains the rush shell + def method_missing(sym, *args, &block) + shell.execute sym.to_s + $last_res + end + + # take a whole block and execute it as if it were inside a shell + def execute_in_shell(&block) + self.instance_eval(&block) + end + end +end diff --git a/lib/rush/entry.rb b/lib/rush/entry.rb index 7c7fc57..434aa08 100644 --- a/lib/rush/entry.rb +++ b/lib/rush/entry.rb @@ -3,183 +3,183 @@ # filesystem on a box, as well as any other operation that returns an entry or # list of entries. class Rush::Entry - attr_reader :box, :name, :path - - # Initialize with full path to the file or dir, and the box it resides on. - def initialize(full_path, box=nil) - full_path = ::File.expand_path(full_path, '/') - @path = ::File.dirname(full_path) - @name = ::File.basename(full_path) - @box = box || Rush::Box.new('localhost') - end - - # The factory checks to see if the full path has a trailing slash for - # creating a Rush::Dir rather than the default Rush::File. - def self.factory(full_path, box=nil) - if full_path.tail(1) == '/' - Rush::Dir.new(full_path, box) - elsif File.directory?(full_path) - Rush::Dir.new(full_path, box) - else - Rush::File.new(full_path, box) - end - end - - def to_s # :nodoc: - if box.host == 'localhost' - "#{full_path}" - else - inspect - end - end - - def inspect # :nodoc: - "#{box}:#{full_path}" - end - - def connection - box ? box.connection : Rush::Connection::Local.new - end - - # The parent dir. For example, box['/etc/hosts'].parent == box['etc/'] - def parent - @parent ||= Rush::Dir.new(@path) - end - - def full_path - "#{@path}/#{@name}" - end - - def quoted_path - Rush.quote(full_path) - end - - # Return true if the entry currently exists on the filesystem of the box. - def exists? - stat - true - rescue Rush::DoesNotExist - false - end - - # Timestamp of most recent change to the entry (permissions, contents, etc). - def changed_at - stat[:ctime] - end - - # Timestamp of last modification of the contents. - def last_modified - stat[:mtime] - end - - # Timestamp that entry was last accessed (read from or written to). - def last_accessed - stat[:atime] - end - - # Attempts to rename, copy, or otherwise place an entry into a dir that already contains an entry by that name will fail with this exception. - class NameAlreadyExists < Exception; end - - # Do not use rename or duplicate with a slash; use copy_to or move_to instead. - class NameCannotContainSlash < Exception; end - - # Rename an entry to another name within the same dir. The object's name - # will be updated to match the change on the filesystem. - def rename(new_name) - connection.rename(@path, @name, new_name) - @name = new_name - self - end - - # Rename an entry to another name within the same dir. The existing object - # will not be affected, but a new object representing the newly-created - # entry will be returned. - def duplicate(new_name) - raise Rush::NameCannotContainSlash if new_name.match(/\//) - new_full_path = "#{@path}/#{new_name}" - connection.copy(full_path, new_full_path) - self.class.new(new_full_path, box) - end - - # Copy the entry to another dir. Returns an object representing the new - # copy. - def copy_to(dir) - raise Rush::NotADir unless dir.class == Rush::Dir - - if box == dir.box - connection.copy(full_path, dir.full_path) - else - archive = connection.read_archive(full_path) - dir.box.connection.write_archive(archive, dir.full_path) - end - - new_full_path = "#{dir.full_path}#{name}" - self.class.new(new_full_path, dir.box) - end - - # Move the entry to another dir. The object will be updated to show its new - # location. - def move_to(dir) - moved = copy_to(dir) - destroy - mimic(moved) - end - - def mimic(from) # :nodoc: - @box = from.box - @path = from.path - @name = from.name - end - - # Unix convention considers entries starting with a . to be hidden. - def hidden? - name.slice(0, 1) == '.' - end - - # Set the access permissions for the entry. - # - # Permissions are set by role and permissions combinations which can be specified individually - # or grouped together. :user_can => :read, :user_can => :write is the same - # as :user_can => :read_write. - # - # You can also insert 'and' if you find it reads better, like :user_and_group_can => :read_and_write. - # - # Any permission excluded is set to deny access. The access call does not set partial - # permissions which combine with the existing state of the entry, like "chmod o+r" would. - # - # Examples: - # - # file.access = { :user_can => :read_write, :group_other_can => :read } - # dir.access = { :user => 'adam', :group => 'users', :read_write_execute => :user_group } - # - def access=(options) - connection.set_access(full_path, Rush::Access.parse(options)) - end - - # Returns a hash with up to nine values, combining user/group/other with read/write/execute. - # The key is omitted if the value is false. - # - # Examples: - # - # entry.access # -> { :user_can_read => true, :user_can_write => true, :group_can_read => true } - # entry.access[:other_can_read] # -> true or nil - # - def access - Rush::Access.new.from_octal(stat[:mode]).display_hash - end - - # Destroy the entry. If it is a dir, everything inside it will also be destroyed. - def destroy - connection.destroy(full_path) - end - - def ==(other) # :nodoc: - full_path == other.full_path and box == other.box - end + attr_reader :box, :name, :path + + # Initialize with full path to the file or dir, and the box it resides on. + def initialize(full_path, box=nil) + full_path = ::File.expand_path(full_path, '/') + @path = ::File.dirname(full_path) + @name = ::File.basename(full_path) + @box = box || Rush::Box.new('localhost') + end + + # The factory checks to see if the full path has a trailing slash for + # creating a Rush::Dir rather than the default Rush::File. + def self.factory(full_path, box=nil) + if full_path.tail(1) == '/' + Rush::Dir.new(full_path, box) + elsif File.directory?(full_path) + Rush::Dir.new(full_path, box) + else + Rush::File.new(full_path, box) + end + end + + def to_s # :nodoc: + if box.host == 'localhost' + "#{full_path}" + else + inspect + end + end + + def inspect # :nodoc: + "#{box}:#{full_path}" + end + + def connection + box ? box.connection : Rush::Connection::Local.new + end + + # The parent dir. For example, box['/etc/hosts'].parent == box['etc/'] + def parent + @parent ||= Rush::Dir.new(@path) + end + + def full_path + "#{@path}/#{@name}" + end + + def quoted_path + Rush.quote(full_path) + end + + # Return true if the entry currently exists on the filesystem of the box. + def exists? + stat + true + rescue Rush::DoesNotExist + false + end + + # Timestamp of most recent change to the entry (permissions, contents, etc). + def changed_at + stat[:ctime] + end + + # Timestamp of last modification of the contents. + def last_modified + stat[:mtime] + end + + # Timestamp that entry was last accessed (read from or written to). + def last_accessed + stat[:atime] + end + + # Attempts to rename, copy, or otherwise place an entry into a dir that already contains an entry by that name will fail with this exception. + class NameAlreadyExists < Exception; end + + # Do not use rename or duplicate with a slash; use copy_to or move_to instead. + class NameCannotContainSlash < Exception; end + + # Rename an entry to another name within the same dir. The object's name + # will be updated to match the change on the filesystem. + def rename(new_name) + connection.rename(@path, @name, new_name) + @name = new_name + self + end + + # Rename an entry to another name within the same dir. The existing object + # will not be affected, but a new object representing the newly-created + # entry will be returned. + def duplicate(new_name) + raise Rush::NameCannotContainSlash if new_name.match(/\//) + new_full_path = "#{@path}/#{new_name}" + connection.copy(full_path, new_full_path) + self.class.new(new_full_path, box) + end + + # Copy the entry to another dir. Returns an object representing the new + # copy. + def copy_to(dir) + raise Rush::NotADir unless dir.class == Rush::Dir + + if box == dir.box + connection.copy(full_path, dir.full_path) + else + archive = connection.read_archive(full_path) + dir.box.connection.write_archive(archive, dir.full_path) + end + + new_full_path = "#{dir.full_path}#{name}" + self.class.new(new_full_path, dir.box) + end + + # Move the entry to another dir. The object will be updated to show its new + # location. + def move_to(dir) + moved = copy_to(dir) + destroy + mimic(moved) + end + + def mimic(from) # :nodoc: + @box = from.box + @path = from.path + @name = from.name + end + + # Unix convention considers entries starting with a . to be hidden. + def hidden? + name.slice(0, 1) == '.' + end + + # Set the access permissions for the entry. + # + # Permissions are set by role and permissions combinations which can be specified individually + # or grouped together. :user_can => :read, :user_can => :write is the same + # as :user_can => :read_write. + # + # You can also insert 'and' if you find it reads better, like :user_and_group_can => :read_and_write. + # + # Any permission excluded is set to deny access. The access call does not set partial + # permissions which combine with the existing state of the entry, like "chmod o+r" would. + # + # Examples: + # + # file.access = { :user_can => :read_write, :group_other_can => :read } + # dir.access = { :user => 'adam', :group => 'users', :read_write_execute => :user_group } + # + def access=(options) + connection.set_access(full_path, Rush::Access.parse(options)) + end + + # Returns a hash with up to nine values, combining user/group/other with read/write/execute. + # The key is omitted if the value is false. + # + # Examples: + # + # entry.access # -> { :user_can_read => true, :user_can_write => true, :group_can_read => true } + # entry.access[:other_can_read] # -> true or nil + # + def access + Rush::Access.new.from_octal(stat[:mode]).display_hash + end + + # Destroy the entry. If it is a dir, everything inside it will also be destroyed. + def destroy + connection.destroy(full_path) + end + + def ==(other) # :nodoc: + full_path == other.full_path and box == other.box + end private - def stat - connection.stat(full_path) - end + def stat + connection.stat(full_path) + end end diff --git a/lib/rush/exceptions.rb b/lib/rush/exceptions.rb index 2492f33..f0ed3cc 100644 --- a/lib/rush/exceptions.rb +++ b/lib/rush/exceptions.rb @@ -1,31 +1,31 @@ module Rush - # Base class for all rush exceptions. - class Exception < ::RuntimeError; end + # Base class for all rush exceptions. + class Exception < ::RuntimeError; end - # Client was not authorized by remote server; check credentials. - class NotAuthorized < Exception; end + # Client was not authorized by remote server; check credentials. + class NotAuthorized < Exception; end - # rushd is not running on the remote box. - class RushdNotRunning < Exception; end + # rushd is not running on the remote box. + class RushdNotRunning < Exception; end - # An unrecognized status code was returned by rushd. - class FailedTransmit < Exception; end + # An unrecognized status code was returned by rushd. + class FailedTransmit < Exception; end - # The entry (file or dir) referenced does not exist. Message is the entry's full path. - class DoesNotExist < Exception; end + # The entry (file or dir) referenced does not exist. Message is the entry's full path. + class DoesNotExist < Exception; end - # The bash command had a non-zero return value. Message is stderr. - class BashFailed < Exception; end + # The bash command had a non-zero return value. Message is stderr. + class BashFailed < Exception; end - # There's already an entry by the given name in the given dir. - class NameAlreadyExists < Exception; end + # There's already an entry by the given name in the given dir. + class NameAlreadyExists < Exception; end - # The name cannot contain a slash; use two operations, rename and then move, instead. - class NameCannotContainSlash < Exception; end + # The name cannot contain a slash; use two operations, rename and then move, instead. + class NameCannotContainSlash < Exception; end - # You cannot move or copy entries to a path that is not a dir (should end with trailing slash). - class NotADir < Exception; end + # You cannot move or copy entries to a path that is not a dir (should end with trailing slash). + class NotADir < Exception; end - # A user or permission value specified to set access was not valid. - class BadAccessSpecifier < Exception; end + # A user or permission value specified to set access was not valid. + class BadAccessSpecifier < Exception; end end diff --git a/lib/rush/file.rb b/lib/rush/file.rb index eb4c418..084e073 100644 --- a/lib/rush/file.rb +++ b/lib/rush/file.rb @@ -1,85 +1,85 @@ # Files are a subclass of Rush::Entry. Most of the file-specific operations # relate to manipulating the file's contents, like search and replace. class Rush::File < Rush::Entry - def dir? - false - end + def dir? + false + end - # Create a blank file. - def create - write('') - self - end + # Create a blank file. + def create + write('') + self + end - # Size in bytes on disk. - def size - stat[:size] - end + # Size in bytes on disk. + def size + stat[:size] + end - # Raw contents of the file. For non-text files, you probably want to avoid - # printing this on the screen. - def contents - connection.file_contents(full_path) - end + # Raw contents of the file. For non-text files, you probably want to avoid + # printing this on the screen. + def contents + connection.file_contents(full_path) + end - alias :read :contents + alias :read :contents - # Write to the file, overwriting whatever was already in it. - # - # Example: file.write "hello, world\n" - def write(new_contents) - connection.write_file(full_path, new_contents) - end + # Write to the file, overwriting whatever was already in it. + # + # Example: file.write "hello, world\n" + def write(new_contents) + connection.write_file(full_path, new_contents) + end - # Append new contents to the end of the file, keeping what was in it. - def append(contents) - connection.append_to_file(full_path, contents) - end - alias_method :<<, :append + # Append new contents to the end of the file, keeping what was in it. + def append(contents) + connection.append_to_file(full_path, contents) + end + alias_method :<<, :append - # Return an array of lines from the file, similar to stdlib's File#readlines. - def lines - contents.split("\n") - end + # Return an array of lines from the file, similar to stdlib's File#readlines. + def lines + contents.split("\n") + end - # Search the file's for a regular expression. Returns nil if no match, or - # each of the matching lines in its entirety. - # - # Example: box['/etc/hosts'].search(/localhost/) # -> [ "127.0.0.1 localhost\n", "::1 localhost\n" ] - def search(pattern) - matching_lines = lines.select { |line| line.match(pattern) } - matching_lines.size == 0 ? nil : matching_lines - end + # Search the file's for a regular expression. Returns nil if no match, or + # each of the matching lines in its entirety. + # + # Example: box['/etc/hosts'].search(/localhost/) # -> [ "127.0.0.1 localhost\n", "::1 localhost\n" ] + def search(pattern) + matching_lines = lines.select { |line| line.match(pattern) } + matching_lines.size == 0 ? nil : matching_lines + end - # Search-and-replace file contents. - # - # Example: box['/etc/hosts'].replace_contents!(/localhost/, 'local.host') - def replace_contents!(pattern, replace_with) - write contents.gsub(pattern, replace_with) - end + # Search-and-replace file contents. + # + # Example: box['/etc/hosts'].replace_contents!(/localhost/, 'local.host') + def replace_contents!(pattern, replace_with) + write contents.gsub(pattern, replace_with) + end - # Return the file's contents, or if it doesn't exist, a blank string. - def contents_or_blank - contents - rescue Rush::DoesNotExist - "" - end + # Return the file's contents, or if it doesn't exist, a blank string. + def contents_or_blank + contents + rescue Rush::DoesNotExist + "" + end - # Count the number of lines in the file. - def line_count - lines.size - end + # Count the number of lines in the file. + def line_count + lines.size + end - # Return an array of lines, or an empty array if the file does not exist. - def lines_or_empty - lines - rescue Rush::DoesNotExist - [] - end + # Return an array of lines, or an empty array if the file does not exist. + def lines_or_empty + lines + rescue Rush::DoesNotExist + [] + end - include Rush::Commands + include Rush::Commands - def entries - [ self ] - end + def entries + [ self ] + end end diff --git a/lib/rush/find_by.rb b/lib/rush/find_by.rb index 175c410..71c3769 100644 --- a/lib/rush/find_by.rb +++ b/lib/rush/find_by.rb @@ -7,33 +7,33 @@ # processes.find_all_by_cmdline(/mongrel_rails/) # module Rush::FindBy - def method_missing(meth, *args) - if m = meth.to_s.match(/^find_by_([a-z_]+)$/) - find_by(m[1], args.first) - elsif m = meth.to_s.match(/^find_all_by_([a-z_]+)$/) - find_all_by(m[1], args.first) - else - super - end - end + def method_missing(meth, *args) + if m = meth.to_s.match(/^find_by_([a-z_]+)$/) + find_by(m[1], args.first) + elsif m = meth.to_s.match(/^find_all_by_([a-z_]+)$/) + find_all_by(m[1], args.first) + else + super + end + end - def find_by(field, arg) - detect do |item| - item.respond_to?(field) and compare_or_match(item.send(field), arg) - end - end + def find_by(field, arg) + detect do |item| + item.respond_to?(field) and compare_or_match(item.send(field), arg) + end + end - def find_all_by(field, arg) - select do |item| - item.respond_to?(field) and compare_or_match(item.send(field), arg) - end - end + def find_all_by(field, arg) + select do |item| + item.respond_to?(field) and compare_or_match(item.send(field), arg) + end + end - def compare_or_match(value, against) - if against.class == Regexp - value.match(against) ? true : false - else - value == against - end - end + def compare_or_match(value, against) + if against.class == Regexp + value.match(against) ? true : false + else + value == against + end + end end diff --git a/lib/rush/fixnum_ext.rb b/lib/rush/fixnum_ext.rb index 3468698..e2af848 100644 --- a/lib/rush/fixnum_ext.rb +++ b/lib/rush/fixnum_ext.rb @@ -4,15 +4,15 @@ # # box['/assets/'].files_flattened.select { |f| f.size > 10.mb } class Fixnum - def kb - self * 1024 - end + def kb + self * 1024 + end - def mb - kb * 1024 - end + def mb + kb * 1024 + end - def gb - mb * 1024 - end + def gb + mb * 1024 + end end diff --git a/lib/rush/local.rb b/lib/rush/local.rb index 2cb5d5a..95d4de2 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -14,395 +14,384 @@ # of parameters to an executed method is handled by # Rush::Connection::Local#receive. class Rush::Connection::Local - # Write raw bytes to a file. - def write_file(full_path, contents) - ::File.open(full_path, 'w') do |f| - f.write contents - end - true - end - - # Append contents to a file - def append_to_file(full_path, contents) - ::File.open(full_path, 'a') do |f| - f.write contents - end - true - end - - # Read raw bytes from a file. - def file_contents(full_path) - ::File.read(full_path) - rescue Errno::ENOENT - raise Rush::DoesNotExist, full_path - end - - # Destroy a file or dir. - def destroy(full_path) - raise "No." if full_path == '/' - FileUtils.rm_rf(full_path) - true - end - - # Purge the contents of a dir. - def purge(full_path) - raise "No." if full_path == '/' - Dir.chdir(full_path) do - all = Dir.glob("*", File::FNM_DOTMATCH).reject { |f| f == '.' or f == '..' } - FileUtils.rm_rf all - end - true - end - - # Create a dir. - def create_dir(full_path) - FileUtils.mkdir_p(full_path) - true - end - - # Rename an entry within a dir. - def rename(path, name, new_name) - raise(Rush::NameCannotContainSlash, "#{path} rename #{name} to #{new_name}") if new_name.match(/\//) - old_full_path = "#{path}/#{name}" - new_full_path = "#{path}/#{new_name}" - raise(Rush::NameAlreadyExists, "#{path} rename #{name} to #{new_name}") if ::File.exists?(new_full_path) - FileUtils.mv(old_full_path, new_full_path) - true - end - - # Copy ane entry from one path to another. - def copy(src, dst) - FileUtils.cp_r(src, dst) - true - rescue Errno::ENOENT - raise Rush::DoesNotExist, File.dirname(dst) - rescue RuntimeError - raise Rush::DoesNotExist, src - end - - # Create an in-memory archive (tgz) of a file or dir, which can be - # transmitted to another server for a copy or move. Note that archive - # operations have the dir name implicit in the archive. - def read_archive(full_path) - `cd #{Rush.quote(::File.dirname(full_path))}; tar c #{Rush.quote(::File.basename(full_path))}` - end - - # Extract an in-memory archive to a dir. - def write_archive(archive, dir) - IO.popen("cd #{Rush::quote(dir)}; tar x", "w") do |p| - p.write archive - end - end - - # Get an index of files from the given path with the glob. Could return - # nested values if the glob contains a doubleglob. The return value is an - # array of full paths, with directories listed first. - def index(base_path, glob) - glob = '*' if glob == '' or glob.nil? - dirs = [] - files = [] - ::Dir.chdir(base_path) do - ::Dir.glob(glob).each do |fname| - if ::File.directory?(fname) - dirs << fname + '/' - else - files << fname - end - end - end - dirs.sort + files.sort - rescue Errno::ENOENT - raise Rush::DoesNotExist, base_path - end - - # Fetch stats (size, ctime, etc) on an entry. Size will not be accurate for dirs. - def stat(full_path) - s = ::File.stat(full_path) - { - :size => s.size, - :ctime => s.ctime, - :atime => s.atime, - :mtime => s.mtime, - :mode => s.mode - } - rescue Errno::ENOENT - raise Rush::DoesNotExist, full_path - end - - def set_access(full_path, access) - access.apply(full_path) - end - - # Fetch the size of a dir, since a standard file stat does not include the - # size of the contents. - def size(full_path) - `du -sb #{Rush.quote(full_path)}`.match(/(\d+)/)[1].to_i - end - - # Get the list of processes as an array of hashes. - def processes - if ::File.directory? "/proc" - resolve_unix_uids(linux_processes) - elsif ::File.directory? "C:/WINDOWS" - windows_processes - else - os_x_processes - end - end - - # Process list on Linux using /proc. - def linux_processes - list = [] - ::Dir["/proc/*/stat"].select { |file| file =~ /\/proc\/\d+\// }.each do |file| - begin - list << read_proc_file(file) - rescue - # process died between the dir listing and accessing the file - end - end - list - end - - def resolve_unix_uids(list) - @uid_map = {} # reset the cache between uid resolutions. - list.each do |process| - process[:user] = resolve_unix_uid_to_user(process[:uid]) - end - list - end - - # resolve uid to user - def resolve_unix_uid_to_user(uid) - require 'etc' - - @uid_map ||= {} - uid = uid.to_i - - return @uid_map[uid] if !@uid_map[uid].nil? - - begin - record = Etc.getpwuid(uid) - rescue ArgumentError - return nil - end - - @uid_map[uid] = record.name - @uid_map[uid] - end - - # Read a single file in /proc and store the parsed values in a hash suitable - # for use in the Rush::Process#new. - def read_proc_file(file) - stat_contents = ::File.read(file) - stat_contents.gsub!(/\((.*)\)/, "") - command = $1 - data = stat_contents.split(" ") - uid = ::File.stat(file).uid - pid = data[0] - cmdline = ::File.read("/proc/#{pid}/cmdline").gsub(/\0/, ' ') - parent_pid = data[2].to_i - utime = data[12].to_i - ktime = data[13].to_i - vss = data[21].to_i / 1024 - rss = data[22].to_i * 4 - time = utime + ktime - - { - :pid => pid, - :uid => uid, - :command => command, - :cmdline => cmdline, - :parent_pid => parent_pid, - :mem => rss, - :cpu => time, - } - end - - # Process list on OS X or other unixes without a /proc. - def os_x_processes - raw = os_x_raw_ps.split("\n").slice(1, 99999) - raw.map do |line| - parse_ps(line) - end - end - - # ps command used to generate list of processes on non-/proc unixes. - def os_x_raw_ps - `COLUMNS=9999 ps ax -o "pid uid ppid rss cpu command"` - end - - # Parse a single line of the ps command and return the values in a hash - # suitable for use in the Rush::Process#new. - def parse_ps(line) - m = line.split(" ", 6) - params = {} - params[:pid] = m[0] - params[:uid] = m[1] - params[:parent_pid] = m[2].to_i - params[:mem] = m[3].to_i - params[:cpu] = m[4].to_i - params[:cmdline] = m[5] - params[:command] = params[:cmdline].split(" ").first - params - end - - # Process list on Windows. - def windows_processes - require 'win32ole' - wmi = WIN32OLE.connect("winmgmts://") - wmi.ExecQuery("select * from win32_process").map do |proc_info| - parse_oleprocinfo(proc_info) - end - end - - # Parse the Windows OLE process info. - def parse_oleprocinfo(proc_info) - command = proc_info.Name - pid = proc_info.ProcessId - uid = 0 - cmdline = proc_info.CommandLine - rss = proc_info.MaximumWorkingSetSize - time = proc_info.KernelModeTime.to_i + proc_info.UserModeTime.to_i - - { - :pid => pid, - :uid => uid, - :command => command, - :cmdline => cmdline, - :mem => rss, - :cpu => time, - } - end - - # Returns true if the specified pid is running. - def process_alive(pid) - ::Process.kill(0, pid) - true - rescue Errno::ESRCH - false - end - - # Terminate a process, by pid. - def kill_process(pid, options={}) - # time to wait before terminating the process, in seconds - wait = options[:wait] || 3 - - if wait > 0 - ::Process.kill('TERM', pid) - - # keep trying until it's dead (technique borrowed from god) - begin - Timeout.timeout(wait) do - loop do - return if !process_alive(pid) - sleep 0.5 - ::Process.kill('TERM', pid) rescue nil - end - end - rescue Timeout::Error - end - end - - ::Process.kill('KILL', pid) rescue nil - - rescue Errno::ESRCH - # if it's dead, great - do nothing - end - - def bash(command, user=nil, background=false, reset_environment=false) - return bash_background(command, user, reset_environment) if background - - require 'session' - - sh = Session::Bash.new - - shell = reset_environment ? "env -i bash" : "bash" - - if user and user != "" - out, err = sh.execute "cd /; sudo -H -u #{user} \"#{shell}\"", :stdin => command - else - out, err = sh.execute shell, :stdin => command - end - - retval = sh.status - sh.close! - - raise(Rush::BashFailed, err) if retval != 0 - - out - end - - def bash_background(command, user, reset_environment) - pid = fork do - inpipe, outpipe = IO.pipe - - outpipe.write command - outpipe.close - STDIN.reopen(inpipe) - - close_all_descriptors([inpipe.to_i]) - - shell = reset_environment ? "env -i bash" : "bash" - - if user and user != '' - exec "cd /; sudo -H -u #{user} \"#{shell}\"" - else - exec shell - end - end - - Process::detach pid - - pid - end - - def close_all_descriptors(keep_open = []) - 3.upto(256) do |fd| - next if keep_open.include?(fd) - IO::new(fd).close rescue nil - end - end - - #################################### - - # Raised when the action passed in by RushServer is not known. - class UnknownAction < Exception; end - - # RushServer uses this method to transform a hash (:action plus parameters - # specific to that action type) into a method call on the connection. The - # returned value must be text so that it can be transmitted across the wire - # as an HTTP response. - def receive(params) - case params[:action] - when 'write_file' then write_file(params[:full_path], params[:payload]) - when 'append_to_file' then append_to_file(params[:full_path], params[:payload]) - when 'file_contents' then file_contents(params[:full_path]) - when 'destroy' then destroy(params[:full_path]) - when 'purge' then purge(params[:full_path]) - when 'create_dir' then create_dir(params[:full_path]) - when 'rename' then rename(params[:path], params[:name], params[:new_name]) - when 'copy' then copy(params[:src], params[:dst]) - when 'read_archive' then read_archive(params[:full_path]) - when 'write_archive' then write_archive(params[:payload], params[:dir]) - when 'index' then index(params[:base_path], params[:glob]).join("\n") + "\n" - when 'stat' then YAML.dump(stat(params[:full_path])) - when 'set_access' then set_access(params[:full_path], Rush::Access.from_hash(params)) - when 'size' then size(params[:full_path]) - when 'processes' then YAML.dump(processes) - when 'process_alive' then process_alive(params[:pid]) ? '1' : '0' - when 'kill_process' then kill_process(params[:pid].to_i, YAML.load(params[:payload])) - when 'bash' then bash(params[:payload], params[:user], params[:background] == 'true', params[:reset_environment] == 'true') - else - raise UnknownAction - end - end - - # No-op for duck typing with remote connection. - def ensure_tunnel(options={}) - end - - # Local connections are always alive. - def alive? - true - end + # Write raw bytes to a file. + def write_file(full_path, contents) + ::File.open(full_path, 'w') do |f| + f.write contents + end + true + end + + # Append contents to a file + def append_to_file(full_path, contents) + ::File.open(full_path, 'a') do |f| + f.write contents + end + true + end + + # Read raw bytes from a file. + def file_contents(full_path) + ::File.read(full_path) + rescue Errno::ENOENT + raise Rush::DoesNotExist, full_path + end + + # Destroy a file or dir. + def destroy(full_path) + raise "No." if full_path == '/' + FileUtils.rm_rf(full_path) + true + end + + # Purge the contents of a dir. + def purge(full_path) + raise "No." if full_path == '/' + Dir.chdir(full_path) do + all = Dir.glob("*", File::FNM_DOTMATCH).reject { |f| f == '.' or f == '..' } + FileUtils.rm_rf all + end + true + end + + # Create a dir. + def create_dir(full_path) + FileUtils.mkdir_p(full_path) + true + end + + # Rename an entry within a dir. + def rename(path, name, new_name) + raise(Rush::NameCannotContainSlash, "#{path} rename #{name} to #{new_name}") if new_name.match(/\//) + old_full_path = "#{path}/#{name}" + new_full_path = "#{path}/#{new_name}" + raise(Rush::NameAlreadyExists, "#{path} rename #{name} to #{new_name}") if ::File.exists?(new_full_path) + FileUtils.mv(old_full_path, new_full_path) + true + end + + # Copy ane entry from one path to another. + def copy(src, dst) + FileUtils.cp_r(src, dst) + true + rescue Errno::ENOENT + raise Rush::DoesNotExist, File.dirname(dst) + rescue RuntimeError + raise Rush::DoesNotExist, src + end + + # Create an in-memory archive (tgz) of a file or dir, which can be + # transmitted to another server for a copy or move. Note that archive + # operations have the dir name implicit in the archive. + def read_archive(full_path) + `cd #{Rush.quote(::File.dirname(full_path))}; tar c #{Rush.quote(::File.basename(full_path))}` + end + + # Extract an in-memory archive to a dir. + def write_archive(archive, dir) + IO.popen("cd #{Rush::quote(dir)}; tar x", "w") do |p| + p.write archive + end + end + + # Get an index of files from the given path with the glob. Could return + # nested values if the glob contains a doubleglob. The return value is an + # array of full paths, with directories listed first. + def index(base_path, glob) + glob = '*' if glob == '' or glob.nil? + dirs = [] + files = [] + ::Dir.chdir(base_path) do + ::Dir.glob(glob).each do |fname| + if ::File.directory?(fname) + dirs << fname + '/' + else + files << fname + end + end + end + dirs.sort + files.sort + rescue Errno::ENOENT + raise Rush::DoesNotExist, base_path + end + + # Fetch stats (size, ctime, etc) on an entry. Size will not be accurate for dirs. + def stat(full_path) + s = ::File.stat(full_path) + { + :size => s.size, + :ctime => s.ctime, + :atime => s.atime, + :mtime => s.mtime, + :mode => s.mode + } + rescue Errno::ENOENT + raise Rush::DoesNotExist, full_path + end + + def set_access(full_path, access) + access.apply(full_path) + end + + # Fetch the size of a dir, since a standard file stat does not include the + # size of the contents. + def size(full_path) + `du -sb #{Rush.quote(full_path)}`.match(/(\d+)/)[1].to_i + end + + # Get the list of processes as an array of hashes. + def processes + if ::File.directory? "/proc" + resolve_unix_uids(linux_processes) + elsif ::File.directory? "C:/WINDOWS" + windows_processes + else + os_x_processes + end + end + + # Process list on Linux using /proc. + def linux_processes + ::Dir["/proc/*/stat"]. + select { |file| file =~ /\/proc\/\d+\// }. + inject([]) do |list, file| + list << read_proc_file rescue list + end + end + + def resolve_unix_uids(list) + @uid_map = {} # reset the cache between uid resolutions. + list.each do |process| + process[:user] = resolve_unix_uid_to_user(process[:uid]) + end + list + end + + # resolve uid to user + def resolve_unix_uid_to_user(uid) + require 'etc' + + @uid_map ||= {} + uid = uid.to_i + + return @uid_map[uid] if !@uid_map[uid].nil? + + begin + record = Etc.getpwuid(uid) + rescue ArgumentError + return nil + end + + @uid_map[uid] = record.name + @uid_map[uid] + end + + # Read a single file in /proc and store the parsed values in a hash suitable + # for use in the Rush::Process#new. + def read_proc_file(file) + stat_contents = ::File.read(file) + stat_contents.gsub!(/\((.*)\)/, "") + command = $1 + data = stat_contents.split(" ") + uid = ::File.stat(file).uid + pid = data[0] + cmdline = ::File.read("/proc/#{pid}/cmdline").gsub(/\0/, ' ') + parent_pid = data[2].to_i + utime = data[12].to_i + ktime = data[13].to_i + vss = data[21].to_i / 1024 + rss = data[22].to_i * 4 + time = utime + ktime + + { + :pid => pid, + :uid => uid, + :command => command, + :cmdline => cmdline, + :parent_pid => parent_pid, + :mem => rss, + :cpu => time, + } + end + + # Process list on OS X or other unixes without a /proc. + def os_x_processes + raw = os_x_raw_ps.split("\n").slice(1, 99999) + raw.map do |line| + parse_ps(line) + end + end + + # ps command used to generate list of processes on non-/proc unixes. + def os_x_raw_ps + `COLUMNS=9999 ps ax -o "pid uid ppid rss cpu command"` + end + + # Parse a single line of the ps command and return the values in a hash + # suitable for use in the Rush::Process#new. + def parse_ps(line) + m = line.split(" ", 6) + params = {} + params[:pid] = m[0] + params[:uid] = m[1] + params[:parent_pid] = m[2].to_i + params[:mem] = m[3].to_i + params[:cpu] = m[4].to_i + params[:cmdline] = m[5] + params[:command] = params[:cmdline].split(" ").first + params + end + + # Process list on Windows. + def windows_processes + require 'win32ole' + wmi = WIN32OLE.connect("winmgmts://") + wmi.ExecQuery("select * from win32_process").map do |proc_info| + parse_oleprocinfo(proc_info) + end + end + + # Parse the Windows OLE process info. + def parse_oleprocinfo(proc_info) + { + pid: proc_info.ProcessId, + uid: 0, + command: proc_info.Name, + cmdline: proc_info.CommandLine, + mem: proc_info.MaximumWorkingSetSize, + cpu: proc_info.KernelModeTime.to_i + proc_info.UserModeTime.to_i + } + end + + # Returns true if the specified pid is running. + def process_alive(pid) + ::Process.kill(0, pid) + true + rescue Errno::ESRCH + false + end + + # Terminate a process, by pid. + def kill_process(pid, options={}) + # time to wait before terminating the process, in seconds + wait = options[:wait] || 3 + + if wait > 0 + ::Process.kill('TERM', pid) + + # keep trying until it's dead (technique borrowed from god) + begin + Timeout.timeout(wait) do + loop do + return if !process_alive(pid) + sleep 0.5 + ::Process.kill('TERM', pid) rescue nil + end + end + rescue Timeout::Error + end + end + + ::Process.kill('KILL', pid) rescue nil + + rescue Errno::ESRCH + # if it's dead, great - do nothing + end + + def bash(command, user=nil, background=false, reset_environment=false) + return bash_background(command, user, reset_environment) if background + + require 'session' + + sh = Session::Bash.new + + shell = reset_environment ? "env -i bash" : "bash" + + if user and user != "" + out, err = sh.execute "cd /; sudo -H -u #{user} \"#{shell}\"", :stdin => command + else + out, err = sh.execute shell, :stdin => command + end + + retval = sh.status + sh.close! + + raise(Rush::BashFailed, err) if retval != 0 + + out + end + + def bash_background(command, user, reset_environment) + pid = fork do + inpipe, outpipe = IO.pipe + + outpipe.write command + outpipe.close + STDIN.reopen(inpipe) + + close_all_descriptors([inpipe.to_i]) + + shell = reset_environment ? "env -i bash" : "bash" + + if user and user != '' + exec "cd /; sudo -H -u #{user} \"#{shell}\"" + else + exec shell + end + end + + Process::detach pid + + pid + end + + def close_all_descriptors(keep_open = []) + 3.upto(256) do |fd| + next if keep_open.include?(fd) + IO::new(fd).close rescue nil + end + end + + #################################### + + # Raised when the action passed in by RushServer is not known. + class UnknownAction < Exception; end + + # RushServer uses this method to transform a hash (:action plus parameters + # specific to that action type) into a method call on the connection. The + # returned value must be text so that it can be transmitted across the wire + # as an HTTP response. + def receive(params) + case params[:action] + when 'write_file' then write_file(params[:full_path], params[:payload]) + when 'append_to_file' then append_to_file(params[:full_path], params[:payload]) + when 'file_contents' then file_contents(params[:full_path]) + when 'destroy' then destroy(params[:full_path]) + when 'purge' then purge(params[:full_path]) + when 'create_dir' then create_dir(params[:full_path]) + when 'rename' then rename(params[:path], params[:name], params[:new_name]) + when 'copy' then copy(params[:src], params[:dst]) + when 'read_archive' then read_archive(params[:full_path]) + when 'write_archive' then write_archive(params[:payload], params[:dir]) + when 'index' then index(params[:base_path], params[:glob]).join("\n") + "\n" + when 'stat' then YAML.dump(stat(params[:full_path])) + when 'set_access' then set_access(params[:full_path], Rush::Access.from_hash(params)) + when 'size' then size(params[:full_path]) + when 'processes' then YAML.dump(processes) + when 'process_alive' then process_alive(params[:pid]) ? '1' : '0' + when 'kill_process' then kill_process(params[:pid].to_i, YAML.load(params[:payload])) + when 'bash' then bash(params[:payload], params[:user], params[:background] == 'true', params[:reset_environment] == 'true') + else + raise UnknownAction + end + end + + # No-op for duck typing with remote connection. + def ensure_tunnel(options={}) + end + + # Local connections are always alive. + def alive? + true + end end diff --git a/lib/rush/process.rb b/lib/rush/process.rb index 9612a27..e1bcd96 100644 --- a/lib/rush/process.rb +++ b/lib/rush/process.rb @@ -1,59 +1,59 @@ # An array of these objects is returned by Rush::Box#processes. class Rush::Process - attr_reader :box, :pid, :uid, :parent_pid, :command, :cmdline, :mem, :cpu, :user - - # params is a hash returned by the system-specific method of looking up the - # process list. - def initialize(params, box) - @box = box - - @pid = params[:pid].to_i - @uid = params[:uid].to_i - @user = params[:user] - @command = params[:command] - @cmdline = params[:cmdline] - @mem = params[:mem] - @cpu = params[:cpu] - @parent_pid = params[:parent_pid] - end - - def to_s # :nodoc: - inspect - end - - def inspect # :nodoc: - if box.to_s != 'localhost' - "#{box} #{@pid}: #{@cmdline}" - else - "#{@pid}: #{@cmdline}" - end - end - - # Returns the Rush::Process parent of this process. - def parent - box.processes.select { |p| p.pid == parent_pid }.first - end - - # Returns an array of child processes owned by this process. - def children - box.processes.select { |p| p.parent_pid == pid } - end - - # Returns true if the process is currently running. - def alive? - box.connection.process_alive(pid) - end - - # Terminate the process. - def kill(options={}) - box.connection.kill_process(pid, options) - end - - def ==(other) # :nodoc: - pid == other.pid and box == other.box - end - - def self.all - Rush::Box.new('localhost').processes - end -end + attr_reader :box, :pid, :uid, :parent_pid, :command, :cmdline, :mem, :cpu, :user + + # params is a hash returned by the system-specific method of looking up the + # process list. + def initialize(params, box) + @box = box + + @pid = params[:pid].to_i + @uid = params[:uid].to_i + @user = params[:user] + @command = params[:command] + @cmdline = params[:cmdline] + @mem = params[:mem] + @cpu = params[:cpu] + @parent_pid = params[:parent_pid] + end + + def to_s # :nodoc: + inspect + end + + def inspect # :nodoc: + if box.to_s != 'localhost' + "#{box} #{@pid}: #{@cmdline}" + else + "#{@pid}: #{@cmdline}" + end + end + + # Returns the Rush::Process parent of this process. + def parent + box.processes.detect { |p| p.pid == parent_pid } + end + + # Returns an array of child processes owned by this process. + def children + box.processes.select { |p| p.parent_pid == pid } + end + + # Returns true if the process is currently running. + def alive? + box.connection.process_alive(pid) + end + + # Terminate the process. + def kill(options={}) + box.connection.kill_process(pid, options) + end + + def ==(other) # :nodoc: + pid == other.pid and box == other.box + end + + def self.all + Rush::Box.new('localhost').processes + end +end diff --git a/lib/rush/process_set.rb b/lib/rush/process_set.rb index 6a84a7d..8334d37 100644 --- a/lib/rush/process_set.rb +++ b/lib/rush/process_set.rb @@ -6,57 +6,57 @@ # processes.filter(:cmdline => /mongrel_rails/).kill # class Rush::ProcessSet - attr_reader :processes - - def initialize(processes) - @processes = processes - end - - # Filter by any field that the process responds to. Specify an exact value, - # or a regular expression. All conditions are put together as a boolean - # AND, so these two statements are equivalent: - # - # processes.filter(:uid => 501).filter(:cmdline => /ruby/) - # processes.filter(:uid => 501, :cmdline => /ruby/) - # - def filter(conditions) - Rush::ProcessSet.new( - processes.select do |p| - conditions.all? do |key, value| - value.class == Regexp ? - value.match(p.send(key)) : - p.send(key) == value - end - end - ) - end - - # Kill all processes in the set. - def kill(options={}) - processes.each { |p| p.kill(options) } - end - - # Check status of all processes in the set, returns an array of booleans. - def alive? - processes.map { |p| p.alive? } - end - - include Enumerable - - def each - processes.each { |p| yield p } - end - - def ==(other) - if other.class == self.class - other.processes == processes - else - to_a == other - end - end - - # All other messages (like size or first) are passed through to the array. - def method_missing(meth, *args) - processes.send(meth, *args) - end + attr_reader :processes + + def initialize(processes) + @processes = processes + end + + # Filter by any field that the process responds to. Specify an exact value, + # or a regular expression. All conditions are put together as a boolean + # AND, so these two statements are equivalent: + # + # processes.filter(:uid => 501).filter(:cmdline => /ruby/) + # processes.filter(:uid => 501, :cmdline => /ruby/) + # + def filter(conditions) + Rush::ProcessSet.new( + processes.select do |p| + conditions.all? do |key, value| + value.class == Regexp ? + value.match(p.send(key)) : + p.send(key) == value + end + end + ) + end + + # Kill all processes in the set. + def kill(options={}) + processes.each { |p| p.kill(options) } + end + + # Check status of all processes in the set, returns an array of booleans. + def alive? + processes.map { |p| p.alive? } + end + + include Enumerable + + def each + processes.each { |p| yield p } + end + + def ==(other) + if other.class == self.class + other.processes == processes + else + to_a == other + end + end + + # All other messages (like size or first) are passed through to the array. + def method_missing(meth, *args) + processes.send(meth, *args) + end end diff --git a/lib/rush/search_results.rb b/lib/rush/search_results.rb index 589fc68..8223cf1 100644 --- a/lib/rush/search_results.rb +++ b/lib/rush/search_results.rb @@ -11,48 +11,48 @@ # myproj['**/*.rb'].search(/class/).lines.size # myproj['**/*.rb'].search(/class/).copy_to other_dir class Rush::SearchResults - attr_reader :entries, :lines, :entries_with_lines, :pattern - - # Make a blank container. Track the pattern so that we can colorize the - # output to show what was matched. - def initialize(pattern) - # Duplication of data, but this lets us return everything in the exact - # order it was received. - @pattern = pattern - @entries = [] - @entries_with_lines = {} - @lines = [] - end - - # Add a Rush::Entry and the array of string matches. - def add(entry, lines) - # this assumes that entry is unique - @entries << entry - @entries_with_lines[entry] = lines - @lines += lines - end - - include Rush::Commands - - def each(&block) - @entries.each(&block) - end - - include Enumerable - - def colorize(line) - lowlight + line.gsub(/(#{pattern.source})/, "#{hilight}\\1#{lowlight}") + normal - end - - def hilight - "\e[34;1m" - end - - def lowlight - "\e[37;2m" - end - - def normal - "\e[0m" - end + attr_reader :entries, :lines, :entries_with_lines, :pattern + + # Make a blank container. Track the pattern so that we can colorize the + # output to show what was matched. + def initialize(pattern) + # Duplication of data, but this lets us return everything in the exact + # order it was received. + @pattern = pattern + @entries = [] + @entries_with_lines = {} + @lines = [] + end + + # Add a Rush::Entry and the array of string matches. + def add(entry, lines) + # this assumes that entry is unique + @entries << entry + @entries_with_lines[entry] = lines + @lines += lines + end + + include Rush::Commands + + def each(&block) + @entries.each(&block) + end + + include Enumerable + + def colorize(line) + lowlight + line.gsub(/(#{pattern.source})/, "#{hilight}\\1#{lowlight}") + normal + end + + def hilight + "\e[34;1m" + end + + def lowlight + "\e[37;2m" + end + + def normal + "\e[0m" + end end diff --git a/lib/rush/server.rb b/lib/rush/server.rb index 3b1d521..4b22f7d 100644 --- a/lib/rush/server.rb +++ b/lib/rush/server.rb @@ -6,112 +6,112 @@ # Rush::Connection::Local call. The results are sent back across the wire to # be decoded by Rush::Connection::Remote on the other side. class RushHandler < Mongrel::HttpHandler - def process(request, response) - params = {} - request.params['QUERY_STRING'].split("?").last.split("&").each do |tuple| - key, value = tuple.split("=") - params[key.to_sym] = value - end - - unless authorize(request.params['HTTP_AUTHORIZATION']) - response.start(401) do |head, out| - end - else - payload = request.body.read - - without_action = params - without_action.delete(params[:action]) - - msg = sprintf "%-20s", params[:action] - msg += without_action.inspect - msg += " + #{payload.size} bytes of payload" if payload.size > 0 - log msg - - params[:payload] = payload - - begin - result = box.connection.receive(params) - - response.start(200) do |head, out| - out.write result - end - rescue Rush::Exception => e - response.start(400) do |head, out| - out.write "#{e.class}\n#{e.message}\n" - end - end - end - rescue Exception => e - log e.full_display - end - - def authorize(auth) - unless m = auth.match(/^Basic (.+)$/) - log "Request with no authorization data" - return false - end - - decoded = Base64.decode64(m[1]) - user, password = decoded.split(':', 2) - - if user.nil? or user.length == 0 or password.nil? or password.length == 0 - log "Malformed user or password" - return false - end - - if password == config.passwords[user] - return true - else - log "Access denied to #{user}" - return false - end - end - - def box - @box ||= Rush::Box.new('localhost') - end - - def config - @config ||= Rush::Config.new - end - - def log(msg) - File.open('rushd.log', 'a') do |f| - f.puts "#{Time.now.strftime('%Y-%m-%d %H:%I:%S')} :: #{msg}" - end - end + def process(request, response) + request.params['QUERY_STRING'].split("?").last. + split("&").inject({}) do |params, tuple| + key, value = tuple.split("=") + params[key.to_sym] = value + end + + unless authorize(request.params['HTTP_AUTHORIZATION']) + response.start(401) do |head, out| + end + else + payload = request.body.read + + without_action = params + without_action.delete(params[:action]) + + msg = sprintf "%-20s", params[:action] + msg += without_action.inspect + msg += " + #{payload.size} bytes of payload" if payload.size > 0 + log msg + + params[:payload] = payload + + begin + result = box.connection.receive(params) + + response.start(200) do |head, out| + out.write result + end + rescue Rush::Exception => e + response.start(400) do |head, out| + out.write "#{e.class}\n#{e.message}\n" + end + end + end + rescue Exception => e + log e.full_display + end + + def authorize(auth) + unless m = auth.match(/^Basic (.+)$/) + log "Request with no authorization data" + return false + end + + decoded = Base64.decode64(m[1]) + user, password = decoded.split(':', 2) + + if user.nil? or user.length == 0 or password.nil? or password.length == 0 + log "Malformed user or password" + return false + end + + if password == config.passwords[user] + return true + else + log "Access denied to #{user}" + return false + end + end + + def box + @box ||= Rush::Box.new('localhost') + end + + def config + @config ||= Rush::Config.new + end + + def log(msg) + File.open('rushd.log', 'a') do |f| + f.puts "#{Time.now.strftime('%Y-%m-%d %H:%I:%S')} :: #{msg}" + end + end end # A container class to run the Mongrel server for rushd. class RushServer - def run - host = "127.0.0.1" - port = Rush::Config::DefaultPort + def run + host = "127.0.0.1" + port = Rush::Config::DefaultPort - rushd = RushHandler.new - rushd.log "rushd listening on #{host}:#{port}" + rushd = RushHandler.new + rushd.log "rushd listening on #{host}:#{port}" - h = Mongrel::HttpServer.new(host, port) - h.register("/", rushd) - h.run.join - end + h = Mongrel::HttpServer.new(host, port) + h.register("/", rushd) + h.run.join + end end class Exception - def full_display - out = [] - out << "Exception #{self.class} => #{self}" - out << "Backtrace:" - out << self.filtered_backtrace.collect do |t| - " #{t}" - end - out << "" - out.join("\n") - end - - def filtered_backtrace - backtrace.reject do |bt| - bt.match(/^\/usr\//) - end - end + def full_display + out = [] + out << "Exception #{self.class} => #{self}" + out << "Backtrace:" + out << self.filtered_backtrace.collect do |t| + " #{t}" + end + out << "" + out.join("\n") + end + + def filtered_backtrace + backtrace.reject do |bt| + bt.match(/^\/usr\//) + end + end end diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index df8e756..12055fb 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -50,7 +50,8 @@ def execute(cmd) # Run the interactive shell using readline. def run loop do - cmd = Readline.readline('rush> ') + prompt = "#{`whoami`.chomp}@#{`pwd`.chomp}$ " + cmd = Readline.readline(prompt) finish if cmd.nil? or cmd == 'exit' next if cmd == "" From cbd8021eb3b7dcadf86e384888512bd2596149e4 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 2 Mar 2014 15:53:17 +0400 Subject: [PATCH 004/119] Some bash-like aliases, xdg-open and locate --- Rakefile | 2 +- lib/rush/commands.rb | 8 ++++++++ lib/rush/dir.rb | 16 ++++++++++++++++ lib/rush/entry.rb | 1 + lib/rush/file.rb | 1 + 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 4cae277..31f902c 100644 --- a/Rakefile +++ b/Rakefile @@ -46,7 +46,7 @@ task :default => :spec ###################################################### -require 'rake/rdoctask' +require 'rake/rdoc/task' Rake::RDocTask.new do |t| t.rdoc_dir = 'rdoc' diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index a0d991b..49e475d 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -45,10 +45,18 @@ def vi(*args) names = entries.map { |f| f.quoted_path }.join(' ') system "vim #{names} #{args.join(' ')}" end + alias_method :vim, :vi # Invoke TextMate on one or more files - only works locally. def mate(*args) names = entries.map { |f| f.quoted_path }.join(' ') system "mate #{names} #{args.join(' ')}" end + + # Open file with xdg-open. + def open(*args) + names = entries.map(&:to_s).join(' ') + command = "xdg-open #{names} #{args.join(' ')}" + system command + end end diff --git a/lib/rush/dir.rb b/lib/rush/dir.rb index 897fc98..98ab97e 100644 --- a/lib/rush/dir.rb +++ b/lib/rush/dir.rb @@ -49,6 +49,22 @@ def [](key) # Slashes work as well, e.g. dir/'subdir/file' alias_method :/, :[] + def locate(path) + located = `locate #{path}`.split("\n"). + map { |x| dir?(x) ? Rush::Dir.new(x) : Rush::File.new(x) } + located.size == 1 ? located.first : located + end + + def locate_dir(path) + located = `locate -r #{path}$`.split("\n"). + map { |x| Dir.new x } + located.size == 1 ? located.first : located + end + + def dir?(path) + ::Dir.exists? path + end + def find_by_name(name) # :nodoc: Rush::Entry.factory("#{full_path}/#{name}", box) end diff --git a/lib/rush/entry.rb b/lib/rush/entry.rb index 434aa08..4916143 100644 --- a/lib/rush/entry.rb +++ b/lib/rush/entry.rb @@ -90,6 +90,7 @@ def rename(new_name) @name = new_name self end + alias_method :mv, :rename # Rename an entry to another name within the same dir. The existing object # will not be affected, but a new object representing the newly-created diff --git a/lib/rush/file.rb b/lib/rush/file.rb index 084e073..d5c21d7 100644 --- a/lib/rush/file.rb +++ b/lib/rush/file.rb @@ -23,6 +23,7 @@ def contents end alias :read :contents + alias_method :cat, :contents # Write to the file, overwriting whatever was already in it. # From 914440837e14da3b90ce61bdc3885be6a0d3e110 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 2 Mar 2014 15:54:45 +0400 Subject: [PATCH 005/119] Add usage comment for open --- lib/rush/commands.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 49e475d..322949d 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -54,6 +54,8 @@ def mate(*args) end # Open file with xdg-open. + # Usage: + # home.locate('mai_failz').open def open(*args) names = entries.map(&:to_s).join(' ') command = "xdg-open #{names} #{args.join(' ')}" From d7ef5b67d057fa71e1701c51e22440f351ad46f8 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Mon, 3 Mar 2014 09:02:03 +0400 Subject: [PATCH 006/119] Add open_with function to open files with any application you like --- Rakefile | 2 +- lib/rush/commands.rb | 21 ++++++++++++--------- lib/rush/search_results.rb | 9 +++++---- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Rakefile b/Rakefile index 31f902c..7c435ab 100644 --- a/Rakefile +++ b/Rakefile @@ -46,7 +46,7 @@ task :default => :spec ###################################################### -require 'rake/rdoc/task' +require 'rdoc/task' Rake::RDocTask.new do |t| t.rdoc_dir = 'rdoc' diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 322949d..8bd6cf3 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -17,13 +17,12 @@ def entries # Search file contents for a regular expression. A Rush::SearchResults # object is returned. def search(pattern) - results = Rush::SearchResults.new(pattern) - entries.each do |entry| + entries.inject(Rush::SearchResults.new(pattern)) do |results, entry| if !entry.dir? and matches = entry.search(pattern) results.add(entry, matches) end + results end - results end # Search and replace file contents. @@ -42,23 +41,27 @@ def line_count # Invoke vi on one or more files - only works locally. def vi(*args) - names = entries.map { |f| f.quoted_path }.join(' ') - system "vim #{names} #{args.join(' ')}" + open_with('vim', *args) end alias_method :vim, :vi # Invoke TextMate on one or more files - only works locally. def mate(*args) - names = entries.map { |f| f.quoted_path }.join(' ') - system "mate #{names} #{args.join(' ')}" + open_with('mate', *args) end # Open file with xdg-open. # Usage: # home.locate('mai_failz').open def open(*args) + open_with('xdg-open', *args) + end + + # Open file with any application you like. + # Usage: + # home.locate('timetable').open_witn :vim + def open_with(app, *args) names = entries.map(&:to_s).join(' ') - command = "xdg-open #{names} #{args.join(' ')}" - system command + system "#{app.to_s} #{names} #{args.join(' ')}" end end diff --git a/lib/rush/search_results.rb b/lib/rush/search_results.rb index 8223cf1..bf8a018 100644 --- a/lib/rush/search_results.rb +++ b/lib/rush/search_results.rb @@ -11,6 +11,9 @@ # myproj['**/*.rb'].search(/class/).lines.size # myproj['**/*.rb'].search(/class/).copy_to other_dir class Rush::SearchResults + include Rush::Commands + include Enumerable + attr_reader :entries, :lines, :entries_with_lines, :pattern # Make a blank container. Track the pattern so that we can colorize the @@ -30,16 +33,14 @@ def add(entry, lines) @entries << entry @entries_with_lines[entry] = lines @lines += lines + self end - - include Rush::Commands + alias_method :<<, :add def each(&block) @entries.each(&block) end - include Enumerable - def colorize(line) lowlight + line.gsub(/(#{pattern.source})/, "#{hilight}\\1#{lowlight}") + normal end From e4b32f3de6eaed86feaa9f02f7b580bf5151c7a8 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Mon, 3 Mar 2014 15:31:45 +0400 Subject: [PATCH 007/119] Add multiline input support --- lib/rush/dir.rb | 6 +----- lib/rush/shell.rb | 19 ++++++++++++++----- lib/rush/string_ext.rb | 9 +++++++++ 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/lib/rush/dir.rb b/lib/rush/dir.rb index 98ab97e..33768f6 100644 --- a/lib/rush/dir.rb +++ b/lib/rush/dir.rb @@ -51,7 +51,7 @@ def [](key) def locate(path) located = `locate #{path}`.split("\n"). - map { |x| dir?(x) ? Rush::Dir.new(x) : Rush::File.new(x) } + map { |x| x.dir? ? Rush::Dir.new(x) : Rush::File.new(x) } located.size == 1 ? located.first : located end @@ -61,10 +61,6 @@ def locate_dir(path) located.size == 1 ? located.first : located end - def dir?(path) - ::Dir.exists? path - end - def find_by_name(name) # :nodoc: Rush::Entry.factory("#{full_path}/#{name}", box) end diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 12055fb..9698a66 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -30,14 +30,24 @@ def initialize commands = @config.load_commands Rush::Dir.class_eval commands Array.class_eval commands + + # Multiline commands should be stored somewhere' + @multiline_cmd = '' end # Run a single command. def execute(cmd) - res = eval(cmd, @pure_binding) + res = eval(@multiline_cmd << "\n" << cmd, @pure_binding) $last_res = res eval("_ = $last_res", @pure_binding) + @multiline_cmd = '' print_result res + rescue SyntaxError => e + unless e.message.include? 'unexpected end-of-input' + @multiline_cmd = '' + puts "Exception #{e.class} -> #{e.message}" + end + # Else it should be multiline command. rescue Rush::Exception => e puts "Exception #{e.class} -> #{e.message}" rescue ::Exception => e @@ -88,11 +98,10 @@ def print_result(res) end puts "#{res.entries.size} matching files with #{res.lines.size} matching lines" elsif res.respond_to? :each - counts = {} - res.each do |item| + counts = res.inject(Hash.new(0)) do |result, item| puts item - counts[item.class] ||= 0 - counts[item.class] += 1 + result[item.class] += 1 + result end if counts == {} puts "=> (empty set)" diff --git a/lib/rush/string_ext.rb b/lib/rush/string_ext.rb index 6f11eaa..1073ffc 100644 --- a/lib/rush/string_ext.rb +++ b/lib/rush/string_ext.rb @@ -1,3 +1,12 @@ class String include Rush::HeadTail + + def less + IO.popen('less', 'w') { |f| f.puts self } + end + alias_method :pager, :less + + def dir? + ::Dir.exists? self + end end From 7f2fbdc8f8ac4f49dd63dc4c5d017047dab97977 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 8 Mar 2014 17:04:09 +0400 Subject: [PATCH 008/119] Add pager to printing results, colorize less --- lib/rush/search_results.rb | 12 ++++++++++++ lib/rush/shell.rb | 34 +++++++++++++--------------------- lib/rush/string_ext.rb | 2 +- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/rush/search_results.rb b/lib/rush/search_results.rb index bf8a018..25be554 100644 --- a/lib/rush/search_results.rb +++ b/lib/rush/search_results.rb @@ -37,6 +37,18 @@ def add(entry, lines) end alias_method :<<, :add + def to_s + widest = entries.map { |k| k.full_path.length }.max + entries_with_lines.inject('') do |result, (entry, lines)| + result << entry.full_path + result << ' ' * (widest - entry.full_path.length + 2) + result << "=> " + result << colorize(lines.first.strip.head(30)) + lines.each { |line| result << "\t" << line << "\n" } + result << "\n" + end + end + def each(&block) @entries.each(&block) end diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 9698a66..ddf2788 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -50,11 +50,11 @@ def execute(cmd) # Else it should be multiline command. rescue Rush::Exception => e puts "Exception #{e.class} -> #{e.message}" + @multiline_cmd = '' rescue ::Exception => e puts "Exception #{e.class} -> #{e.message}" - e.backtrace.each do |t| - puts " #{::File.expand_path(t)}" - end + e.backtrace.each { |t| puts "\t#{::File.expand_path(t)}" } + @multiline_cmd = '' end # Run the interactive shell using readline. @@ -82,38 +82,30 @@ def finish def print_result(res) return if self.suppress_output if res.kind_of? String - puts res + output = res elsif res.kind_of? Rush::SearchResults - widest = res.entries.map { |k| k.full_path.length }.max - res.entries_with_lines.each do |entry, lines| - print entry.full_path - print ' ' * (widest - entry.full_path.length + 2) - print "=> " - print res.colorize(lines.first.strip.head(30)) - print "..." if lines.first.strip.length > 30 - if lines.size > 1 - print " (plus #{lines.size - 1} more matches)" - end - print "\n" - end - puts "#{res.entries.size} matching files with #{res.lines.size} matching lines" + widest = res.entries.max_by { |k| k.full_path.length } + output = res.to_s << + "#{res.entries.size} matching files with #{res.lines.size} matching lines" elsif res.respond_to? :each + output = '' counts = res.inject(Hash.new(0)) do |result, item| - puts item + output << item.to_s << "\n" result[item.class] += 1 result end if counts == {} - puts "=> (empty set)" + output = "=> (empty set)" else count_s = counts.map do |klass, count| "#{count} x #{klass}" end.join(', ') - puts "=> #{count_s}" + output << "=> #{count_s}" end else - puts "=> #{res.inspect}" + output = "=> #{res.inspect}" end + output.lines.size > 5 ? output.less : puts(output) end def path_parts(input) # :nodoc: diff --git a/lib/rush/string_ext.rb b/lib/rush/string_ext.rb index 1073ffc..0ecf8e5 100644 --- a/lib/rush/string_ext.rb +++ b/lib/rush/string_ext.rb @@ -2,7 +2,7 @@ class String include Rush::HeadTail def less - IO.popen('less', 'w') { |f| f.puts self } + IO.popen('less -R', 'w') { |f| f.puts self } end alias_method :pager, :less From 9b317383e0655021c8c182dddda7f12c271f04ea Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 9 Mar 2014 08:46:11 +0400 Subject: [PATCH 009/119] Fix autocomplete, make it work with constants --- lib/rush/shell.rb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index ddf2788..29d1fa3 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -84,7 +84,6 @@ def print_result(res) if res.kind_of? String output = res elsif res.kind_of? Rush::SearchResults - widest = res.entries.max_by { |k| k.full_path.length } output = res.to_s << "#{res.entries.size} matching files with #{res.lines.size} matching lines" elsif res.respond_to? :each @@ -128,7 +127,7 @@ def complete_method(receiver, dot, partial_name, pre) (box[path].methods - Object.methods).select do |e| e.match(/^#{Regexp.escape(partial_name)}/) end.map do |e| - (pre || '') + receiver + dot + e + (pre || '') + receiver + dot + e.to_s end end end @@ -154,11 +153,10 @@ def complete_variable(partial_name, pre) lvars = eval('local_variables', @pure_binding) gvars = eval('global_variables', @pure_binding) ivars = eval('instance_variables', @pure_binding) - (lvars + gvars + ivars).select do |e| - e.match(/^#{Regexp.escape(partial_name)}/) - end.map do |e| - (pre || '') + e - end + constants = eval('Object.constants', @pure_binding) + (lvars + gvars + ivars + constants). + select { |e| e.match(/^#{Regexp.escape(partial_name)}/) }. + map { |e| (pre || '') + e.to_s } end # Try to do tab completion on dir square brackets and slash accessors. From c293db9e12b976a3b1cd18e674b469dfc36b49b7 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 16 Mar 2014 12:49:28 +0400 Subject: [PATCH 010/119] Clean code a bit --- lib/rush/commands.rb | 6 +++++- lib/rush/config.rb | 15 ++++----------- lib/rush/local.rb | 40 ++++++++++++++-------------------------- lib/rush/shell.rb | 18 +++++++----------- 4 files changed, 30 insertions(+), 49 deletions(-) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 8bd6cf3..7db39fc 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -41,7 +41,11 @@ def line_count # Invoke vi on one or more files - only works locally. def vi(*args) - open_with('vim', *args) + if self.class == Rush::Dir + system "cd #{full_path}; vim" + else + open_with('vim', *args) + end end alias_method :vim, :vi diff --git a/lib/rush/config.rb b/lib/rush/config.rb index 25bcdf3..658bbac 100644 --- a/lib/rush/config.rb +++ b/lib/rush/config.rb @@ -66,12 +66,10 @@ def passwords_file end def passwords - hash = {} - passwords_file.lines_or_empty.each do |line| + passwords_file.lines_or_empty.inject({}) do |result, line| user, password = line.split(":", 2) - hash[user] = password + result.merge user => password end - hash end # Credentials is the client-side equivalent of passwords. It contains only @@ -116,11 +114,7 @@ def generate_password def generate_secret(min, max) chars = self.secret_characters len = rand(max - min + 1) + min - password = "" - len.times do |index| - password += chars[rand(chars.length)] - end - password + len.times.inject { |r| r += chars[rand(chars.length)] } end def secret_characters @@ -139,8 +133,7 @@ def tunnels_file def tunnels tunnels_file.lines_or_empty.inject({}) do |hash, line| host, port = line.split(':', 2) - hash[host] = port.to_i - hash + hash.merge host => port.to_i end end diff --git a/lib/rush/local.rb b/lib/rush/local.rb index 95d4de2..6fd99af 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -153,10 +153,8 @@ def processes # Process list on Linux using /proc. def linux_processes ::Dir["/proc/*/stat"]. - select { |file| file =~ /\/proc\/\d+\// }. - inject([]) do |list, file| - list << read_proc_file rescue list - end + select { |file| file =~ /\/proc\/\d+\// }. + inject([]) { |list, file| list << read_proc_file rescue list } end def resolve_unix_uids(list) @@ -170,20 +168,10 @@ def resolve_unix_uids(list) # resolve uid to user def resolve_unix_uid_to_user(uid) require 'etc' - @uid_map ||= {} - uid = uid.to_i - - return @uid_map[uid] if !@uid_map[uid].nil? - - begin - record = Etc.getpwuid(uid) - rescue ArgumentError - return nil - end - - @uid_map[uid] = record.name - @uid_map[uid] + return @uid_map[uid] if @uid_map[uid] + record = Etc.getpwuid(uid) rescue (return nil) + @uid_map.merge uid.to_i => record.name end # Read a single file in /proc and store the parsed values in a hash suitable @@ -231,15 +219,15 @@ def os_x_raw_ps # suitable for use in the Rush::Process#new. def parse_ps(line) m = line.split(" ", 6) - params = {} - params[:pid] = m[0] - params[:uid] = m[1] - params[:parent_pid] = m[2].to_i - params[:mem] = m[3].to_i - params[:cpu] = m[4].to_i - params[:cmdline] = m[5] - params[:command] = params[:cmdline].split(" ").first - params + { + pid: m[0], + uid: m[1], + parent_pid: m[2].to_i, + mem: m[3].to_i, + cpu: m[4].to_i, + cmdline: m[5], + command: m[5].split(' ').first + } end # Process list on Windows. diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 29d1fa3..d99e489 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -163,23 +163,19 @@ def complete_variable(partial_name, pre) # # Example: # - # dir['subd # presing tab here will produce dir['subdir/ if subdir exists - # dir/'subd # presing tab here will produce dir/'subdir/ if subdir exists + # dir['subd # presing tab here will produce dir['subdir/ if subdir exists + # dir/'subd # presing tab here will produce dir/'subdir/ if subdir exists # # This isn't that cool yet, because it can't do multiple levels of subdirs. # It does work remotely, though, which is pretty sweet. def completion_proc proc do |input| receiver, accessor, *rest = path_parts(input) - if receiver - case accessor - when /^[\[\/]$/ - complete_path(receiver, accessor, *rest) - when /^\.$/ - complete_method(receiver, accessor, *rest) - when nil - complete_variable(receiver, *rest) - end + return unless receiver + case accessor + when /^[\[\/]$/ then complete_path(receiver, accessor, *rest) + when /^\.$/ then complete_method(receiver, accessor, *rest) + when nil then complete_variable(receiver, *rest) end end end From f48b0a85e420ad4f37e6d997a7ddf3d5b41817cc Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 16 Mar 2014 15:47:14 +0400 Subject: [PATCH 011/119] Specs now running. But not passed --- lib/rush/access.rb | 5 ++++- spec/access_spec.rb | 2 +- spec/array_ext_spec.rb | 2 +- spec/box_spec.rb | 2 +- spec/commands_spec.rb | 2 +- spec/config_spec.rb | 2 +- spec/dir_spec.rb | 2 +- spec/embeddable_shell_spec.rb | 4 ++-- spec/entry_spec.rb | 2 +- spec/file_spec.rb | 2 +- spec/find_by_spec.rb | 2 +- spec/fixnum_ext_spec.rb | 2 +- spec/local_spec.rb | 2 +- spec/process_set_spec.rb | 2 +- spec/process_spec.rb | 2 +- spec/remote_spec.rb | 2 +- spec/rush_spec.rb | 2 +- spec/search_results_spec.rb | 2 +- spec/shell_spec.rb | 2 +- spec/ssh_tunnel_spec.rb | 2 +- spec/string_ext_spec.rb | 2 +- 21 files changed, 25 insertions(+), 22 deletions(-) diff --git a/lib/rush/access.rb b/lib/rush/access.rb index f824e0e..04f872b 100644 --- a/lib/rush/access.rb +++ b/lib/rush/access.rb @@ -101,7 +101,10 @@ def octal_integer_array(mode) end def set_matrix(perms, roles) - ACCESS_UNITS.each { |unit| send unit, true } + ROLES.product(PERMISSIONS). + select { |r, p| perms.include?(p) && roles.include?(r) }. + map { |r, p| "#{r}_can_#{p}".to_sym }. + each { |unit| send unit, true } end def extract_list(type, value, choices) diff --git a/spec/access_spec.rb b/spec/access_spec.rb index 8e232f7..e53ad1f 100644 --- a/spec/access_spec.rb +++ b/spec/access_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::Access do before do diff --git a/spec/array_ext_spec.rb b/spec/array_ext_spec.rb index 56ef5af..c186a52 100644 --- a/spec/array_ext_spec.rb +++ b/spec/array_ext_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Array do it "mixes commands into array" do diff --git a/spec/box_spec.rb b/spec/box_spec.rb index 7fadb61..0e2e94b 100644 --- a/spec/box_spec.rb +++ b/spec/box_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::Box do before do diff --git a/spec/commands_spec.rb b/spec/commands_spec.rb index 8aed8df..0f79757 100644 --- a/spec/commands_spec.rb +++ b/spec/commands_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::Commands do before do diff --git a/spec/config_spec.rb b/spec/config_spec.rb index 301e93e..75bb230 100644 --- a/spec/config_spec.rb +++ b/spec/config_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::Config do before do diff --git a/spec/dir_spec.rb b/spec/dir_spec.rb index 19324e8..d6990bf 100644 --- a/spec/dir_spec.rb +++ b/spec/dir_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::Dir do before do diff --git a/spec/embeddable_shell_spec.rb b/spec/embeddable_shell_spec.rb index 92925d2..67734d1 100644 --- a/spec/embeddable_shell_spec.rb +++ b/spec/embeddable_shell_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::EmbeddableShell do before do @@ -8,7 +8,7 @@ it "should execute unknown methods against a Rush::Shell instance" do @shell.root.class.should == Rush::Dir end - + it "should executes a block as if it were inside the shell" do @shell.execute_in_shell { root.class.should == Rush::Dir diff --git a/spec/entry_spec.rb b/spec/entry_spec.rb index f018b8f..16f1882 100644 --- a/spec/entry_spec.rb +++ b/spec/entry_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::Entry do before do diff --git a/spec/file_spec.rb b/spec/file_spec.rb index de491f1..f6a944d 100644 --- a/spec/file_spec.rb +++ b/spec/file_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::File do before do diff --git a/spec/find_by_spec.rb b/spec/find_by_spec.rb index 6c9743e..46c0475 100644 --- a/spec/find_by_spec.rb +++ b/spec/find_by_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::FindBy do before do diff --git a/spec/fixnum_ext_spec.rb b/spec/fixnum_ext_spec.rb index 4b0aedd..4f9dc61 100644 --- a/spec/fixnum_ext_spec.rb +++ b/spec/fixnum_ext_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Fixnum do before do diff --git a/spec/local_spec.rb b/spec/local_spec.rb index 2b47ca6..167559e 100644 --- a/spec/local_spec.rb +++ b/spec/local_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::Connection::Local do before do diff --git a/spec/process_set_spec.rb b/spec/process_set_spec.rb index 56a3b02..7b72667 100644 --- a/spec/process_set_spec.rb +++ b/spec/process_set_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::ProcessSet do before do diff --git a/spec/process_spec.rb b/spec/process_spec.rb index 7f09feb..fb03dc8 100644 --- a/spec/process_spec.rb +++ b/spec/process_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::Process do before do diff --git a/spec/remote_spec.rb b/spec/remote_spec.rb index d7a6cc1..11ce1a8 100644 --- a/spec/remote_spec.rb +++ b/spec/remote_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::Connection::Local do before do diff --git a/spec/rush_spec.rb b/spec/rush_spec.rb index 0d219ab..7da4b1b 100644 --- a/spec/rush_spec.rb +++ b/spec/rush_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush do it "fetches a local file path" do diff --git a/spec/search_results_spec.rb b/spec/search_results_spec.rb index a57a0fb..8554e6c 100644 --- a/spec/search_results_spec.rb +++ b/spec/search_results_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::SearchResults do before do diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index c08f112..efcba7c 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' require 'rush/shell' describe Rush::Shell do diff --git a/spec/ssh_tunnel_spec.rb b/spec/ssh_tunnel_spec.rb index ec54252..0d9dbc3 100644 --- a/spec/ssh_tunnel_spec.rb +++ b/spec/ssh_tunnel_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe Rush::SshTunnel do before do diff --git a/spec/string_ext_spec.rb b/spec/string_ext_spec.rb index 5f2d3ed..664b51f 100644 --- a/spec/string_ext_spec.rb +++ b/spec/string_ext_spec.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/base' +require __dir__ + '/base' describe String do before do From d5a925657f270b39a5fdc7802114e5f9c406c655 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 22 Mar 2014 12:20:48 +0400 Subject: [PATCH 012/119] Fix access inconsistence --- lib/rush/access.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rush/access.rb b/lib/rush/access.rb index 04f872b..f92a6cb 100644 --- a/lib/rush/access.rb +++ b/lib/rush/access.rb @@ -103,7 +103,7 @@ def octal_integer_array(mode) def set_matrix(perms, roles) ROLES.product(PERMISSIONS). select { |r, p| perms.include?(p) && roles.include?(r) }. - map { |r, p| "#{r}_can_#{p}".to_sym }. + map { |r, p| "#{r}_can_#{p}=".to_sym }. each { |unit| send unit, true } end From 15b249a36057d6929955c67ddde8ee8a97e1029e Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 22 Mar 2014 12:41:30 +0400 Subject: [PATCH 013/119] Fix access specs --- lib/rush/access.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rush/access.rb b/lib/rush/access.rb index f92a6cb..effcff1 100644 --- a/lib/rush/access.rb +++ b/lib/rush/access.rb @@ -45,7 +45,7 @@ def to_hash def display_hash to_hash.select { |_, v| v == 1 }. - inject({}) { |r, k, _| r.merge k => true } + inject({}) { |r, (k, _)| r.merge k => true } end def from_hash(hash) From ca31e95249e76f0157c1d0ce8d6c52b6ced2142d Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 22 Mar 2014 19:30:11 +0400 Subject: [PATCH 014/119] All specs passed --- lib/rush/commands.rb | 2 +- lib/rush/config.rb | 2 +- lib/rush/dir.rb | 2 +- lib/rush/local.rb | 14 +-- lib/rush/ssh_tunnel.rb | 236 ++++++++++++++++++------------------ spec/search_results_spec.rb | 2 +- 6 files changed, 129 insertions(+), 129 deletions(-) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 7db39fc..c964a92 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -35,7 +35,7 @@ def replace_contents!(pattern, with_text) # Count the number of lines in the contained files. def line_count entries.inject(0) do |count, entry| - count + entry.dir? ? 0 : entry.lines.size + count + (entry.dir? ? 0 : entry.lines.size) end end diff --git a/lib/rush/config.rb b/lib/rush/config.rb index 658bbac..958d76d 100644 --- a/lib/rush/config.rb +++ b/lib/rush/config.rb @@ -114,7 +114,7 @@ def generate_password def generate_secret(min, max) chars = self.secret_characters len = rand(max - min + 1) + min - len.times.inject { |r| r += chars[rand(chars.length)] } + len.times.inject('') { |r| r += chars[rand(chars.length)] } end def secret_characters diff --git a/lib/rush/dir.rb b/lib/rush/dir.rb index 33768f6..9628950 100644 --- a/lib/rush/dir.rb +++ b/lib/rush/dir.rb @@ -89,7 +89,7 @@ def dirs_flattened # Given a list of flat filenames, product a list of entries under this dir. # Mostly for internal use. def make_entries(filenames) - filenames.map do |fname| + Array(filenames).map do |fname| Rush::Entry.factory("#{full_path}/#{fname}") end end diff --git a/lib/rush/local.rb b/lib/rush/local.rb index 6fd99af..808906f 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -72,12 +72,10 @@ def rename(path, name, new_name) # Copy ane entry from one path to another. def copy(src, dst) + raise(Rush::DoesNotExist, src) unless File.exists?(src) + raise(Rush::DoesNotExist, File.dirname(dst)) unless File.exists?(File.dirname(dst)) FileUtils.cp_r(src, dst) true - rescue Errno::ENOENT - raise Rush::DoesNotExist, File.dirname(dst) - rescue RuntimeError - raise Rush::DoesNotExist, src end # Create an in-memory archive (tgz) of a file or dir, which can be @@ -154,7 +152,7 @@ def processes def linux_processes ::Dir["/proc/*/stat"]. select { |file| file =~ /\/proc\/\d+\// }. - inject([]) { |list, file| list << read_proc_file rescue list } + inject([]) { |list, file| (list << read_proc_file(file)) rescue list } end def resolve_unix_uids(list) @@ -167,11 +165,13 @@ def resolve_unix_uids(list) # resolve uid to user def resolve_unix_uid_to_user(uid) + uid = uid.to_i require 'etc' @uid_map ||= {} return @uid_map[uid] if @uid_map[uid] record = Etc.getpwuid(uid) rescue (return nil) - @uid_map.merge uid.to_i => record.name + @uid_map.merge uid => record.name + record.name end # Read a single file in /proc and store the parsed values in a hash suitable @@ -198,7 +198,7 @@ def read_proc_file(file) :cmdline => cmdline, :parent_pid => parent_pid, :mem => rss, - :cpu => time, + :cpu => time } end diff --git a/lib/rush/ssh_tunnel.rb b/lib/rush/ssh_tunnel.rb index 5211bf6..be50729 100644 --- a/lib/rush/ssh_tunnel.rb +++ b/lib/rush/ssh_tunnel.rb @@ -1,122 +1,122 @@ # Internal class for managing an ssh tunnel, across which relatively insecure # HTTP commands can be sent by Rush::Connection::Remote. class Rush::SshTunnel - def initialize(real_host) - @real_host = real_host - end - - def host - 'localhost' - end - - def port - @port - end - - def ensure_tunnel(options={}) - return if @port and tunnel_alive? - - @port = config.tunnels[@real_host] - - if !@port or !tunnel_alive? - setup_everything(options) - end - end - - def setup_everything(options={}) - display "Connecting to #{@real_host}..." - push_credentials - launch_rushd - establish_tunnel(options) - end - - def push_credentials - display "Pushing credentials" - config.ensure_credentials_exist - ssh_append_to_credentials(config.credentials_file.contents.strip) - end - - def ssh_append_to_credentials(string) - # the following horror is exactly why rush is needed - passwords_file = "~/.rush/passwords" - string = "'#{string}'" - ssh "M=`grep #{string} #{passwords_file} 2>/dev/null | wc -l`; if [ $M = 0 ]; then mkdir -p .rush; chmod 700 .rush; echo #{string} >> #{passwords_file}; chmod 600 #{passwords_file}; fi" - end - - def launch_rushd - display "Launching rushd" - ssh("if [ `ps aux | grep rushd | grep -v grep | wc -l` -ge 1 ]; then exit; fi; rushd > /dev/null 2>&1 &") - end - - def establish_tunnel(options={}) - display "Establishing ssh tunnel" - @port = next_available_port - - make_ssh_tunnel(options) - - tunnels = config.tunnels - tunnels[@real_host] = @port - config.save_tunnels tunnels - - sleep 0.5 - end - - def tunnel_options - { - :local_port => @port, - :remote_port => Rush::Config::DefaultPort, - :ssh_host => @real_host, - } - end - - def tunnel_alive? - `#{tunnel_count_command}`.to_i > 0 - end - - def tunnel_count_command - "ps x | grep '#{ssh_tunnel_command_without_stall}' | grep -v grep | wc -l" - end - - class SshFailed < Exception; end - class NoPortSelectedYet < Exception; end - - def ssh(command) - raise SshFailed unless system("ssh #{@real_host} '#{command}'") - end - - def make_ssh_tunnel(options={}) - raise SshFailed unless system(ssh_tunnel_command(options)) - end - - def ssh_tunnel_command_without_stall - options = tunnel_options - raise NoPortSelectedYet unless options[:local_port] - "ssh -f -L #{options[:local_port]}:127.0.0.1:#{options[:remote_port]} #{options[:ssh_host]}" - end - - def ssh_stall_command(options={}) - if options[:timeout] == :infinite - "while [ 1 ]; do sleep 1000; done" - elsif options[:timeout].to_i > 10 - "sleep #{options[:timeout].to_i}" - else - "sleep 9000" - end - end - - def ssh_tunnel_command(options={}) - ssh_tunnel_command_without_stall + ' "' + ssh_stall_command(options) + '"' - end - - def next_available_port - (config.tunnels.values.max || Rush::Config::DefaultPort) + 1 - end - - def config - @config ||= Rush::Config.new - end - - def display(msg) - puts msg - end + def initialize(real_host) + @real_host = real_host + end + + def host + 'localhost' + end + + def port + @port + end + + def ensure_tunnel(options={}) + return if @port and tunnel_alive? + + @port = config.tunnels[@real_host] + + if !@port or !tunnel_alive? + setup_everything(options) + end + end + + def setup_everything(options={}) + display "Connecting to #{@real_host}..." + push_credentials + launch_rushd + establish_tunnel(options) + end + + def push_credentials + display "Pushing credentials" + config.ensure_credentials_exist + ssh_append_to_credentials(config.credentials_file.contents.strip) + end + + def ssh_append_to_credentials(string) + # the following horror is exactly why rush is needed + passwords_file = "~/.rush/passwords" + string = "'#{string}'" + ssh "M=`grep #{string} #{passwords_file} 2>/dev/null | wc -l`; if [ $M = 0 ]; then mkdir -p .rush; chmod 700 .rush; echo #{string} >> #{passwords_file}; chmod 600 #{passwords_file}; fi" + end + + def launch_rushd + display "Launching rushd" + ssh("if [ `ps aux | grep rushd | grep -v grep | wc -l` -ge 1 ]; then exit; fi; rushd > /dev/null 2>&1 &") + end + + def establish_tunnel(options={}) + display "Establishing ssh tunnel" + @port = next_available_port + + make_ssh_tunnel(options) + + tunnels = config.tunnels + tunnels[@real_host] = @port + config.save_tunnels tunnels + + sleep 0.5 + end + + def tunnel_options + { + :local_port => @port, + :remote_port => Rush::Config::DefaultPort, + :ssh_host => @real_host, + } + end + + def tunnel_alive? + `#{tunnel_count_command}`.to_i > 0 + end + + def tunnel_count_command + "ps x | grep '#{ssh_tunnel_command_without_stall}' | grep -v grep | wc -l" + end + + class SshFailed < Exception; end + class NoPortSelectedYet < Exception; end + + def ssh(command) + raise SshFailed unless system("ssh #{@real_host} '#{command}'") + end + + def make_ssh_tunnel(options={}) + raise SshFailed unless system(ssh_tunnel_command(options)) + end + + def ssh_tunnel_command_without_stall + options = tunnel_options + raise NoPortSelectedYet unless options[:local_port] + "ssh -f -L #{options[:local_port]}:127.0.0.1:#{options[:remote_port]} #{options[:ssh_host]}" + end + + def ssh_stall_command(options={}) + if options[:timeout] == :infinite + "while [ 1 ]; do sleep 1000; done" + elsif options[:timeout].to_i > 10 + "sleep #{options[:timeout].to_i}" + else + "sleep 9000" + end + end + + def ssh_tunnel_command(options={}) + ssh_tunnel_command_without_stall + ' "' + ssh_stall_command(options) + '"' + end + + def next_available_port + (config.tunnels.values.max || Rush::Config::DefaultPort) + 1 + end + + def config + @config ||= Rush::Config.new + end + + def display(msg) + puts msg + end end diff --git a/spec/search_results_spec.rb b/spec/search_results_spec.rb index 8554e6c..045396b 100644 --- a/spec/search_results_spec.rb +++ b/spec/search_results_spec.rb @@ -34,7 +34,7 @@ end it "mixes in Commands to operate like a dir or entry array" do - @results.methods.include?("search").should be_true + @results.methods.include?(:search).should be_true end it "mixes in Enumerable to behave like an array" do From 0a5dfe4d7e3660f8fd3a4221d474af43fc212755 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 22 Mar 2014 22:05:30 +0400 Subject: [PATCH 015/119] Now you can tune your prompt --- lib/rush/shell.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index d99e489..5fc0b57 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -60,7 +60,7 @@ def execute(cmd) # Run the interactive shell using readline. def run loop do - prompt = "#{`whoami`.chomp}@#{`pwd`.chomp}$ " + prompt = self.class.prompt || "#{`whoami`.chomp}@#{`pwd`.chomp}$ " cmd = Readline.readline(prompt) finish if cmd.nil? or cmd == 'exit' @@ -71,6 +71,12 @@ def run end end + # Tune the prompt with + # Rush::Shell.prompt = 'hey there! > ' + class << self + attr_accessor :prompt + end + # Save history to ~/.rush/history when the shell exists. def finish @config.save_history(Readline::HISTORY.to_a) From 796dec75886ee07bb478722ff99866850b802668 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 23 Mar 2014 21:05:18 +0400 Subject: [PATCH 016/119] Remove ctags file and add it go .gitignore --- .gitignore | 1 + lib/rush/shell.rb | 9 +- tags | 312 ---------------------------------------------- 3 files changed, 5 insertions(+), 317 deletions(-) delete mode 100644 tags diff --git a/.gitignore b/.gitignore index 7b42034..9fe97de 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ rdoc pkg .rbenv-version *.swp +tags diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 5fc0b57..18355fd 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -147,11 +147,10 @@ def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc: full_path = eval("#{possible_var}.full_path", @pure_binding) rescue nil box = eval("#{possible_var}.box", @pure_binding) rescue nil if full_path and box - Rush::Dir.new(full_path, box).entries.select do |e| - e.name.match(/^#{Regexp.escape(partial_path)}/) - end.map do |e| - (pre || '') + original_var + accessor + quote + fixed_path + e.name + (e.dir? ? "/" : "") - end + Rush::Dir.new(full_path, box).entries. + select { |e| e.name.match(/^#{Regexp.escape(partial_path)}/) }. + map { |e| (pre || '') + original_var + accessor + quote + + fixed_path + e.name + (e.dir? ? "/" : "") } end end diff --git a/tags b/tags deleted file mode 100644 index 85680d6..0000000 --- a/tags +++ /dev/null @@ -1,312 +0,0 @@ -!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ -!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ -!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ -!_TAG_PROGRAM_NAME Exuberant Ctags // -!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ -!_TAG_PROGRAM_VERSION 5.9~svn20110310 // -== lib/rush/box.rb /^ def ==(other) # :nodoc:$/;" f class:Rush -== lib/rush/entry.rb /^ def ==(other) # :nodoc:$/;" f class:Rush -== lib/rush/process.rb /^ def ==(other) # :nodoc:$/;" f class:Rush -== lib/rush/process_set.rb /^ def ==(other)$/;" f class:Rush -Array lib/rush/array_ext.rb /^class Array$/;" c -BadAccessSpecifier lib/rush/exceptions.rb /^ class BadAccessSpecifier < Exception; end$/;" c class:Rush -BashFailed lib/rush/exceptions.rb /^ class BashFailed < Exception; end$/;" c class:Rush -DoesNotExist lib/rush/exceptions.rb /^ class DoesNotExist < Exception; end$/;" c class:Rush -EmbeddableShell lib/rush/embeddable_shell.rb /^ class EmbeddableShell$/;" c class:Rush -Exception lib/rush/exceptions.rb /^ class Exception < ::RuntimeError; end$/;" c class:Rush -Exception lib/rush/server.rb /^class Exception$/;" c -FailedTransmit lib/rush/exceptions.rb /^ class FailedTransmit < Exception; end$/;" c class:Rush -Fixnum lib/rush/fixnum_ext.rb /^class Fixnum$/;" c -Foo spec/find_by_spec.rb /^ class Foo$/;" c -NameAlreadyExists lib/rush/entry.rb /^ class NameAlreadyExists < Exception; end$/;" c class:Rush -NameAlreadyExists lib/rush/exceptions.rb /^ class NameAlreadyExists < Exception; end$/;" c class:Rush -NameCannotContainSlash lib/rush/entry.rb /^ class NameCannotContainSlash < Exception; end$/;" c class:Rush -NameCannotContainSlash lib/rush/exceptions.rb /^ class NameCannotContainSlash < Exception; end$/;" c class:Rush -NoPortSelectedYet lib/rush/ssh_tunnel.rb /^ class NoPortSelectedYet < Exception; end$/;" c class:Rush -NotADir lib/rush/exceptions.rb /^ class NotADir < Exception; end$/;" c class:Rush -NotAuthorized lib/rush/exceptions.rb /^ class NotAuthorized < Exception; end$/;" c class:Rush -Rush lib/rush.rb /^module Rush$/;" m -Rush lib/rush.rb /^module Rush::Connection; end$/;" m -Rush lib/rush/access.rb /^class Rush::Access$/;" c -Rush lib/rush/box.rb /^class Rush::Box$/;" c -Rush lib/rush/commands.rb /^module Rush::Commands$/;" m -Rush lib/rush/config.rb /^class Rush::Config$/;" c -Rush lib/rush/dir.rb /^class Rush::Dir < Rush::Entry$/;" c -Rush lib/rush/embeddable_shell.rb /^module Rush$/;" m -Rush lib/rush/entry.rb /^class Rush::Entry$/;" c -Rush lib/rush/exceptions.rb /^module Rush$/;" m -Rush lib/rush/file.rb /^class Rush::File < Rush::Entry$/;" c -Rush lib/rush/find_by.rb /^module Rush::FindBy$/;" m -Rush lib/rush/head_tail.rb /^module Rush::HeadTail$/;" m -Rush lib/rush/local.rb /^class Rush::Connection::Local$/;" c -Rush lib/rush/process.rb /^class Rush::Process$/;" c -Rush lib/rush/process_set.rb /^class Rush::ProcessSet$/;" c -Rush lib/rush/remote.rb /^class Rush::Connection::Remote$/;" c -Rush lib/rush/search_results.rb /^class Rush::SearchResults$/;" c -Rush lib/rush/shell.rb /^module Rush$/;" m -Rush lib/rush/ssh_tunnel.rb /^class Rush::SshTunnel$/;" c -RushHandler lib/rush/server.rb /^class RushHandler < Mongrel::HttpHandler$/;" c -RushServer lib/rush/server.rb /^class RushServer$/;" c -RushdNotRunning lib/rush/exceptions.rb /^ class RushdNotRunning < Exception; end$/;" c class:Rush -Shell lib/rush/shell.rb /^ class Shell$/;" c class:Rush -SshFailed lib/rush/ssh_tunnel.rb /^ class SshFailed < Exception; end$/;" c class:Rush -String lib/rush/string_ext.rb /^class String$/;" c -UnknownAction lib/rush/local.rb /^ class UnknownAction < Exception; end$/;" c -[] lib/rush.rb /^ def self.[](key)$/;" F class:Rush -[] lib/rush/box.rb /^ def [](key)$/;" f class:Rush -[] lib/rush/dir.rb /^ def [](key)$/;" f class:Rush -access lib/rush/entry.rb /^ def access$/;" f class:Rush -access= lib/rush/entry.rb /^ def access=(options)$/;" f class:Rush -add lib/rush/search_results.rb /^ def add(entry, lines)$/;" f class:Rush -alive? lib/rush/box.rb /^ def alive?$/;" f class:Rush -alive? lib/rush/local.rb /^ def alive?$/;" f -alive? lib/rush/process.rb /^ def alive?$/;" f class:Rush -alive? lib/rush/process_set.rb /^ def alive?$/;" f class:Rush -alive? lib/rush/remote.rb /^ def alive?$/;" f class:Rush -all lib/rush/process.rb /^ def self.all$/;" F class:Rush -append lib/rush/file.rb /^ def append(contents)$/;" f class:Rush -append_to_file lib/rush/local.rb /^ def append_to_file(full_path, contents)$/;" f class:Rush -append_to_file lib/rush/remote.rb /^ def append_to_file(full_path, contents)$/;" f class:Rush -apply lib/rush/access.rb /^ def apply(full_path)$/;" f class:Rush -authorize lib/rush/server.rb /^ def authorize(auth)$/;" f -bash lib/rush.rb /^ def self.bash(command, options={})$/;" F class:Rush -bash lib/rush/box.rb /^ def bash(command, options={})$/;" f class:Rush -bash lib/rush/dir.rb /^ def bash(command, options={})$/;" f class:Rush -bash lib/rush/local.rb /^ def bash(command, user=nil, background=false, reset_environment=false)$/;" f -bash lib/rush/remote.rb /^ def bash(command, user, background, reset_environment)$/;" f class:Rush -bash_background lib/rush/local.rb /^ def bash_background(command, user, reset_environment)$/;" f -box lib/rush.rb /^ def self.box$/;" F class:Rush -box lib/rush/server.rb /^ def box$/;" f -changed_at lib/rush/entry.rb /^ def changed_at$/;" f class:Rush -children lib/rush/process.rb /^ def children$/;" f class:Rush -close_all_descriptors lib/rush/local.rb /^ def close_all_descriptors(keep_open = [])$/;" f -colorize lib/rush/search_results.rb /^ def colorize(line)$/;" f class:Rush -command_with_environment lib/rush/box.rb /^ def command_with_environment(command, env) # :nodoc:$/;" f class:Rush -commands_file lib/rush/config.rb /^ def commands_file$/;" f class:Rush -compare_or_match lib/rush/find_by.rb /^ def compare_or_match(value, against)$/;" f class:Rush -complete_method lib/rush/shell.rb /^ def complete_method(receiver, dot, partial_name, pre)$/;" f class:Rush.Shell.print_result -complete_path lib/rush/shell.rb /^ def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc:$/;" f class:Rush.Shell.print_result.complete_method -complete_variable lib/rush/shell.rb /^ def complete_variable(partial_name, pre)$/;" f class:Rush.Shell.print_result.complete_method.complete_path -completion_proc lib/rush/shell.rb /^ def completion_proc$/;" f class:Rush.Shell.print_result.complete_method.complete_path.complete_variable -config lib/rush/remote.rb /^ def config$/;" f class:Rush -config lib/rush/server.rb /^ def config$/;" f -config lib/rush/ssh_tunnel.rb /^ def config$/;" f class:Rush -connection lib/rush/box.rb /^ def connection # :nodoc:$/;" f class:Rush -connection lib/rush/entry.rb /^ def connection$/;" f class:Rush -contents lib/rush/dir.rb /^ def contents$/;" f class:Rush -contents lib/rush/file.rb /^ def contents$/;" f class:Rush -contents_or_blank lib/rush/file.rb /^ def contents_or_blank$/;" f class:Rush -copy lib/rush/local.rb /^ def copy(src, dst)$/;" f class:Rush -copy lib/rush/remote.rb /^ def copy(src, dst)$/;" f class:Rush -copy_to lib/rush/entry.rb /^ def copy_to(dir)$/;" f class:Rush -create lib/rush/dir.rb /^ def create$/;" f class:Rush -create lib/rush/file.rb /^ def create$/;" f class:Rush -create_dir lib/rush/dir.rb /^ def create_dir(name)$/;" f class:Rush -create_dir lib/rush/local.rb /^ def create_dir(full_path)$/;" f class:Rush -create_dir lib/rush/remote.rb /^ def create_dir(full_path)$/;" f class:Rush -create_file lib/rush/dir.rb /^ def create_file(name)$/;" f class:Rush -credentials lib/rush/config.rb /^ def credentials$/;" f class:Rush -credentials_file lib/rush/config.rb /^ def credentials_file$/;" f class:Rush -credentials_password lib/rush/config.rb /^ def credentials_password$/;" f class:Rush -credentials_user lib/rush/config.rb /^ def credentials_user$/;" f class:Rush -destroy lib/rush/entry.rb /^ def destroy$/;" f class:Rush -destroy lib/rush/local.rb /^ def destroy(full_path)$/;" f class:Rush -destroy lib/rush/remote.rb /^ def destroy(full_path)$/;" f class:Rush -dir lib/rush.rb /^ def self.dir(filename)$/;" F class:Rush -dir? lib/rush/dir.rb /^ def dir?$/;" f class:Rush -dir? lib/rush/file.rb /^ def dir?$/;" f class:Rush -dirs lib/rush/dir.rb /^ def dirs$/;" f class:Rush -dirs_flattened lib/rush/dir.rb /^ def dirs_flattened$/;" f class:Rush -display lib/rush/ssh_tunnel.rb /^ def display(msg)$/;" f class:Rush -display_hash lib/rush/access.rb /^ def display_hash$/;" f class:Rush -duplicate lib/rush/entry.rb /^ def duplicate(new_name)$/;" f class:Rush -each lib/rush/process_set.rb /^ def each$/;" f class:Rush -each lib/rush/search_results.rb /^ def each(&block)$/;" f class:Rush -ensure_credentials_exist lib/rush/config.rb /^ def ensure_credentials_exist$/;" f class:Rush -ensure_tunnel lib/rush/local.rb /^ def ensure_tunnel(options={})$/;" f -ensure_tunnel lib/rush/remote.rb /^ def ensure_tunnel(options={})$/;" f class:Rush -ensure_tunnel lib/rush/ssh_tunnel.rb /^ def ensure_tunnel(options={})$/;" f class:Rush -entries lib/rush/array_ext.rb /^ def entries$/;" f class:Array -entries lib/rush/commands.rb /^ def entries$/;" f class:Rush -entries lib/rush/dir.rb /^ def entries$/;" f class:Rush -entries lib/rush/file.rb /^ def entries$/;" f class:Rush -entries_tree lib/rush/dir.rb /^ def entries_tree$/;" f class:Rush -env_file lib/rush/config.rb /^ def env_file$/;" f class:Rush -establish_connection lib/rush/box.rb /^ def establish_connection(options={})$/;" f class:Rush -establish_tunnel lib/rush/ssh_tunnel.rb /^ def establish_tunnel(options={})$/;" f class:Rush -execute lib/rush/shell.rb /^ def execute(cmd)$/;" f class:Rush.Shell -execute_in_shell lib/rush/embeddable_shell.rb /^ def execute_in_shell(&block)$/;" f class:Rush.EmbeddableShell -exists? lib/rush/entry.rb /^ def exists?$/;" f class:Rush -extract_list lib/rush/access.rb /^ def extract_list(type, value, choices)$/;" f class:Rush -factory lib/rush/entry.rb /^ def self.factory(full_path, box=nil)$/;" F class:Rush -file_contents lib/rush/local.rb /^ def file_contents(full_path)$/;" f class:Rush -file_contents lib/rush/remote.rb /^ def file_contents(full_path)$/;" f class:Rush -files lib/rush/dir.rb /^ def files$/;" f class:Rush -files_flattened lib/rush/dir.rb /^ def files_flattened$/;" f class:Rush -filesystem lib/rush/box.rb /^ def filesystem$/;" f class:Rush -filter lib/rush/process_set.rb /^ def filter(conditions)$/;" f class:Rush -filtered_backtrace lib/rush/server.rb /^ def filtered_backtrace$/;" f class:Exception -find_all_by lib/rush/find_by.rb /^ def find_all_by(field, arg)$/;" f class:Rush -find_by lib/rush/find_by.rb /^ def find_by(field, arg)$/;" f class:Rush -find_by_glob lib/rush/dir.rb /^ def find_by_glob(glob) # :nodoc:$/;" f class:Rush -find_by_name lib/rush/dir.rb /^ def find_by_name(name) # :nodoc:$/;" f class:Rush -finish lib/rush/shell.rb /^ def finish$/;" f class:Rush.Shell -from_hash lib/rush/access.rb /^ def from_hash(hash)$/;" f class:Rush -from_hash lib/rush/access.rb /^ def self.from_hash(hash)$/;" F class:Rush -from_octal lib/rush/access.rb /^ def from_octal(mode)$/;" f class:Rush -full_display lib/rush/server.rb /^ def full_display$/;" f class:Exception -full_path lib/rush/dir.rb /^ def full_path$/;" f class:Rush -full_path lib/rush/entry.rb /^ def full_path$/;" f class:Rush -gb lib/rush/fixnum_ext.rb /^ def gb$/;" f class:Fixnum -generate_credentials lib/rush/config.rb /^ def generate_credentials$/;" f class:Rush -generate_password lib/rush/config.rb /^ def generate_password$/;" f class:Rush -generate_secret lib/rush/config.rb /^ def generate_secret(min, max)$/;" f class:Rush -generate_user lib/rush/config.rb /^ def generate_user$/;" f class:Rush -git lib/rush/dir.rb /^ def git(*args)$/;" f class:Rush -head lib/rush/head_tail.rb /^ def head(n)$/;" f class:Rush -hidden? lib/rush/entry.rb /^ def hidden?$/;" f class:Rush -hilight lib/rush/search_results.rb /^ def hilight$/;" f class:Rush -history_file lib/rush/config.rb /^ def history_file$/;" f class:Rush -host lib/rush/ssh_tunnel.rb /^ def host$/;" f class:Rush -index lib/rush/local.rb /^ def index(base_path, glob)$/;" f -index lib/rush/remote.rb /^ def index(base_path, glob)$/;" f class:Rush -initialize lib/rush/box.rb /^ def initialize(host='localhost')$/;" f class:Rush -initialize lib/rush/config.rb /^ def initialize(location=nil)$/;" f class:Rush -initialize lib/rush/embeddable_shell.rb /^ def initialize(suppress_output = true)$/;" f class:Rush.EmbeddableShell -initialize lib/rush/entry.rb /^ def initialize(full_path, box=nil)$/;" f class:Rush -initialize lib/rush/process.rb /^ def initialize(params, box)$/;" f class:Rush -initialize lib/rush/process_set.rb /^ def initialize(processes)$/;" f class:Rush -initialize lib/rush/remote.rb /^ def initialize(host)$/;" f class:Rush -initialize lib/rush/search_results.rb /^ def initialize(pattern)$/;" f class:Rush -initialize lib/rush/shell.rb /^ def initialize$/;" f class:Rush.Shell -initialize lib/rush/ssh_tunnel.rb /^ def initialize(real_host)$/;" f class:Rush -initialize spec/find_by_spec.rb /^ def initialize(bar)$/;" f class:Foo -inspect lib/rush/box.rb /^ def inspect # :nodoc:$/;" f class:Rush -inspect lib/rush/entry.rb /^ def inspect # :nodoc:$/;" f class:Rush -inspect lib/rush/process.rb /^ def inspect # :nodoc:$/;" f class:Rush -kb lib/rush/fixnum_ext.rb /^ def kb$/;" f class:Fixnum -kill lib/rush/process.rb /^ def kill(options={})$/;" f class:Rush -kill lib/rush/process_set.rb /^ def kill(options={})$/;" f class:Rush -kill_process lib/rush/local.rb /^ def kill_process(pid, options={})$/;" f -kill_process lib/rush/remote.rb /^ def kill_process(pid, options={})$/;" f class:Rush -last_accessed lib/rush/entry.rb /^ def last_accessed$/;" f class:Rush -last_modified lib/rush/entry.rb /^ def last_modified$/;" f class:Rush -launch_dir lib/rush.rb /^ def self.launch_dir$/;" F class:Rush -launch_rushd lib/rush/ssh_tunnel.rb /^ def launch_rushd$/;" f class:Rush -line_count lib/rush/commands.rb /^ def line_count$/;" f class:Rush -line_count lib/rush/file.rb /^ def line_count$/;" f class:Rush -lines lib/rush/file.rb /^ def lines$/;" f class:Rush -lines_or_empty lib/rush/file.rb /^ def lines_or_empty$/;" f class:Rush -linux_processes lib/rush/local.rb /^ def linux_processes$/;" f -load_commands lib/rush/config.rb /^ def load_commands$/;" f class:Rush -load_env lib/rush/config.rb /^ def load_env$/;" f class:Rush -load_history lib/rush/config.rb /^ def load_history$/;" f class:Rush -log lib/rush/server.rb /^ def log(msg)$/;" f -lowlight lib/rush/search_results.rb /^ def lowlight$/;" f class:Rush -ls lib/rush/dir.rb /^ def ls$/;" f class:Rush -make_connection lib/rush/box.rb /^ def make_connection # :nodoc:$/;" f class:Rush -make_entries lib/rush/dir.rb /^ def make_entries(filenames)$/;" f class:Rush -make_ssh_tunnel lib/rush/ssh_tunnel.rb /^ def make_ssh_tunnel(options={})$/;" f class:Rush -mate lib/rush/commands.rb /^ def mate(*args)$/;" f class:Rush -mb lib/rush/fixnum_ext.rb /^ def mb$/;" f class:Fixnum -method_missing lib/rush/embeddable_shell.rb /^ def method_missing(sym, *args, &block)$/;" f class:Rush.EmbeddableShell -method_missing lib/rush/find_by.rb /^ def method_missing(meth, *args)$/;" f class:Rush -method_missing lib/rush/process_set.rb /^ def method_missing(meth, *args)$/;" f class:Rush -mimic lib/rush/entry.rb /^ def mimic(from) # :nodoc:$/;" f class:Rush -mock_config spec/base.rb /^def mock_config(&block)$/;" f -mock_config_cleanup spec/base.rb /^def mock_config_cleanup$/;" f -mock_config_sandbox_dir spec/base.rb /^def mock_config_sandbox_dir$/;" f -mock_config_start spec/base.rb /^def mock_config_start$/;" f -move_to lib/rush/entry.rb /^ def move_to(dir)$/;" f class:Rush -my_process lib/rush.rb /^ def self.my_process$/;" F class:Rush -next_available_port lib/rush/ssh_tunnel.rb /^ def next_available_port$/;" f class:Rush -nonhidden_dirs lib/rush/dir.rb /^ def nonhidden_dirs$/;" f class:Rush -nonhidden_files lib/rush/dir.rb /^ def nonhidden_files$/;" f class:Rush -normal lib/rush/search_results.rb /^ def normal$/;" f class:Rush -octal_integer_array lib/rush/access.rb /^ def octal_integer_array(mode)$/;" f class:Rush -octal_permissions lib/rush/access.rb /^ def octal_permissions$/;" f class:Rush -os_x_processes lib/rush/local.rb /^ def os_x_processes$/;" f -os_x_raw_ps lib/rush/local.rb /^ def os_x_raw_ps$/;" f -parent lib/rush/entry.rb /^ def parent$/;" f class:Rush -parent lib/rush/process.rb /^ def parent$/;" f class:Rush -parse lib/rush/access.rb /^ def parse(options)$/;" f class:Rush -parse lib/rush/access.rb /^ def self.parse(options)$/;" F class:Rush -parse_exception lib/rush/remote.rb /^ def parse_exception(body)$/;" f class:Rush -parse_oleprocinfo lib/rush/local.rb /^ def parse_oleprocinfo(proc_info)$/;" f -parse_ps lib/rush/local.rb /^ def parse_ps(line)$/;" f -parts_from lib/rush/access.rb /^ def parts_from(value)$/;" f class:Rush -passwords lib/rush/config.rb /^ def passwords$/;" f class:Rush -passwords_file lib/rush/config.rb /^ def passwords_file$/;" f class:Rush -path_parts lib/rush/shell.rb /^ def path_parts(input) # :nodoc:$/;" f class:Rush.Shell.print_result -permissions lib/rush/access.rb /^ def self.permissions$/;" F class:Rush -port lib/rush/ssh_tunnel.rb /^ def port$/;" f class:Rush -print_result lib/rush/shell.rb /^ def print_result(res)$/;" f class:Rush.Shell -process lib/rush/server.rb /^ def process(request, response)$/;" f class:RushHandler -process_alive lib/rush/local.rb /^ def process_alive(pid)$/;" f -process_alive lib/rush/remote.rb /^ def process_alive(pid)$/;" f class:Rush -process_result lib/rush/remote.rb /^ def process_result(code, body)$/;" f class:Rush -processes lib/rush.rb /^ def self.processes$/;" F class:Rush -processes lib/rush/box.rb /^ def processes$/;" f class:Rush -processes lib/rush/local.rb /^ def processes$/;" f -processes lib/rush/remote.rb /^ def processes$/;" f class:Rush -purge lib/rush/dir.rb /^ def purge$/;" f class:Rush -purge lib/rush/local.rb /^ def purge(full_path)$/;" f class:Rush -purge lib/rush/remote.rb /^ def purge(full_path)$/;" f class:Rush -push_credentials lib/rush/ssh_tunnel.rb /^ def push_credentials$/;" f class:Rush -quote lib/rush.rb /^ def self.quote(path)$/;" F class:Rush -quoted_path lib/rush/entry.rb /^ def quoted_path$/;" f class:Rush -rake lib/rush/dir.rb /^ def rake(*args)$/;" f class:Rush -read_archive lib/rush/local.rb /^ def read_archive(full_path)$/;" f class:Rush -read_archive lib/rush/remote.rb /^ def read_archive(full_path)$/;" f class:Rush -read_proc_file lib/rush/local.rb /^ def read_proc_file(file)$/;" f -receive lib/rush/local.rb /^ def receive(params)$/;" f -rename lib/rush/entry.rb /^ def rename(new_name)$/;" f class:Rush -rename lib/rush/local.rb /^ def rename(path, name, new_name)$/;" f class:Rush -rename lib/rush/remote.rb /^ def rename(path, name, new_name)$/;" f class:Rush -replace_contents! lib/rush/commands.rb /^ def replace_contents!(pattern, with_text)$/;" f class:Rush -replace_contents! lib/rush/file.rb /^ def replace_contents!(pattern, replace_with)$/;" f class:Rush -resolve_unix_uid_to_user lib/rush/local.rb /^ def resolve_unix_uid_to_user(uid)$/;" f -resolve_unix_uids lib/rush/local.rb /^ def resolve_unix_uids(list)$/;" f -roles lib/rush/access.rb /^ def self.roles$/;" F class:Rush -run lib/rush/server.rb /^ def run$/;" f class:RushServer -run lib/rush/shell.rb /^ def run$/;" f class:Rush.Shell -save_credentials lib/rush/config.rb /^ def save_credentials(user, password)$/;" f class:Rush -save_history lib/rush/config.rb /^ def save_history(array)$/;" f class:Rush -save_tunnels lib/rush/config.rb /^ def save_tunnels(hash)$/;" f class:Rush -search lib/rush/commands.rb /^ def search(pattern)$/;" f class:Rush -search lib/rush/file.rb /^ def search(pattern)$/;" f class:Rush -secret_characters lib/rush/config.rb /^ def secret_characters$/;" f class:Rush -set_access lib/rush/local.rb /^ def set_access(full_path, access)$/;" f -set_access lib/rush/remote.rb /^ def set_access(full_path, access)$/;" f class:Rush -set_matrix lib/rush/access.rb /^ def set_matrix(perms, roles)$/;" f class:Rush -setup_everything lib/rush/ssh_tunnel.rb /^ def setup_everything(options={})$/;" f class:Rush -size lib/rush/dir.rb /^ def size$/;" f class:Rush -size lib/rush/file.rb /^ def size$/;" f class:Rush -size lib/rush/local.rb /^ def size(full_path)$/;" f -size lib/rush/remote.rb /^ def size(full_path)$/;" f class:Rush -ssh lib/rush/ssh_tunnel.rb /^ def ssh(command)$/;" f class:Rush -ssh_append_to_credentials lib/rush/ssh_tunnel.rb /^ def ssh_append_to_credentials(string)$/;" f class:Rush -ssh_stall_command lib/rush/ssh_tunnel.rb /^ def ssh_stall_command(options={})$/;" f class:Rush -ssh_tunnel_command lib/rush/ssh_tunnel.rb /^ def ssh_tunnel_command(options={})$/;" f class:Rush -ssh_tunnel_command_without_stall lib/rush/ssh_tunnel.rb /^ def ssh_tunnel_command_without_stall$/;" f class:Rush -stat lib/rush/entry.rb /^ def stat$/;" f class:Rush -stat lib/rush/local.rb /^ def stat(full_path)$/;" f -stat lib/rush/remote.rb /^ def stat(full_path)$/;" f class:Rush -tail lib/rush/head_tail.rb /^ def tail(n)$/;" f class:Rush -to_hash lib/rush/access.rb /^ def to_hash$/;" f class:Rush -to_s lib/rush/box.rb /^ def to_s # :nodoc:$/;" f class:Rush -to_s lib/rush/entry.rb /^ def to_s # :nodoc:$/;" f class:Rush -to_s lib/rush/process.rb /^ def to_s # :nodoc:$/;" f class:Rush -transmit lib/rush/remote.rb /^ def transmit(params)$/;" f class:Rush -tunnel_alive? lib/rush/ssh_tunnel.rb /^ def tunnel_alive?$/;" f class:Rush -tunnel_count_command lib/rush/ssh_tunnel.rb /^ def tunnel_count_command$/;" f class:Rush -tunnel_options lib/rush/ssh_tunnel.rb /^ def tunnel_options$/;" f class:Rush -tunnels lib/rush/config.rb /^ def tunnels$/;" f class:Rush -tunnels_file lib/rush/config.rb /^ def tunnels_file$/;" f class:Rush -vi lib/rush/commands.rb /^ def vi(*args)$/;" f class:Rush -windows_processes lib/rush/local.rb /^ def windows_processes$/;" f -write lib/rush/file.rb /^ def write(new_contents)$/;" f class:Rush -write_archive lib/rush/local.rb /^ def write_archive(archive, dir)$/;" f class:Rush -write_archive lib/rush/remote.rb /^ def write_archive(archive, dir)$/;" f class:Rush -write_file lib/rush/local.rb /^ def write_file(full_path, contents)$/;" f class:Rush -write_file lib/rush/remote.rb /^ def write_file(full_path, contents)$/;" f class:Rush From 756744d956008a7cc4d7ffb26e941f9896203000 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 30 Mar 2014 09:09:07 +0400 Subject: [PATCH 017/119] More accurate work with bash commands --- lib/rush/box.rb | 8 +++----- lib/rush/commands.rb | 7 ++++++- lib/rush/dir.rb | 30 +++++++++++++----------------- lib/rush/file.rb | 10 +++++++++- lib/rush/local.rb | 41 +++++++++++------------------------------ lib/rush/shell.rb | 2 +- 6 files changed, 43 insertions(+), 55 deletions(-) diff --git a/lib/rush/box.rb b/lib/rush/box.rb index ac6e868..eece3a9 100644 --- a/lib/rush/box.rb +++ b/lib/rush/box.rb @@ -82,7 +82,7 @@ def method_missing(meth, *args) # box.bash 'mongrel_rails start', :background => true # box.bash 'rake db:migrate', :user => 'www', :env => { :RAILS_ENV => 'production' }, :reset_environment => true # - def bash(command, options={}) + def bash(command, options = {}) cmd_with_env = command_with_environment(command, options[:env]) options[:reset_environment] ||= false @@ -96,12 +96,10 @@ def bash(command, options={}) def command_with_environment(command, env) # :nodoc: return command unless env - - vars = env.map do |key, value| + env.map do |key, value| escaped = value.to_s.gsub('"', '\\"').gsub('`', '\\\`') "export #{key}=\"#{escaped}\"" - end - vars.push(command).join("\n") + end.push(command).join("\n") end # Returns true if the box is responding to commands. diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index c964a92..fe1de9d 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -66,6 +66,11 @@ def open(*args) # home.locate('timetable').open_witn :vim def open_with(app, *args) names = entries.map(&:to_s).join(' ') - system "#{app.to_s} #{names} #{args.join(' ')}" + system "cd #{dirname}; #{app.to_s} #{names} #{args.join(' ')}" + end + + def output_of(app, *args) + names = entries.map(&:to_s).join(' ') + `cd #{dirname}; #{app.to_s} #{names} #{args.join(' ')}` end end diff --git a/lib/rush/dir.rb b/lib/rush/dir.rb index 9628950..ddaf059 100644 --- a/lib/rush/dir.rb +++ b/lib/rush/dir.rb @@ -19,6 +19,7 @@ def dir? def full_path "#{super}/" end + alias_method :dirname, :full_path # Entries contained within this dir - not recursive. def contents @@ -38,25 +39,23 @@ def dirs # Access subentries with square brackets, e.g. dir['subdir/file'] def [](key) key = key.to_s - if key == '**' - files_flattened - elsif key.match(/\*/) - find_by_glob(key) - else - find_by_name(key) + case + when key == '**' then files_flattened + when key.match(/\*/) then find_by_glob(key) + else find_by_name(key) end end # Slashes work as well, e.g. dir/'subdir/file' alias_method :/, :[] def locate(path) - located = `locate #{path}`.split("\n"). + located = bash("locate #{path}").split("\n"). map { |x| x.dir? ? Rush::Dir.new(x) : Rush::File.new(x) } located.size == 1 ? located.first : located end def locate_dir(path) - located = `locate -r #{path}$`.split("\n"). + located = bash("locate -r #{path}$").split("\n"). map { |x| Dir.new x } located.size == 1 ? located.first : located end @@ -78,20 +77,19 @@ def entries_tree # Recursively contained files. def files_flattened - entries_tree.select { |e| !e.dir? } + entries_tree.reject(&:dir?) end # Recursively contained dirs. def dirs_flattened - entries_tree.select { |e| e.dir? } + entries_tree.select(&:dir?) end # Given a list of flat filenames, product a list of entries under this dir. # Mostly for internal use. def make_entries(filenames) - Array(filenames).map do |fname| - Rush::Entry.factory("#{full_path}/#{fname}") - end + Array(filenames). + map { |fname| Rush::Entry.factory("#{full_path}/#{fname}") } end # Create a blank file within this dir. @@ -103,7 +101,7 @@ def create_file(name) # Create an empty subdir within this dir. def create_dir(name) - name += '/' unless name.tail(1) == '/' + name += '/' unless name[-1] == '/' self[name].create end @@ -120,9 +118,7 @@ def size # Contained dirs that are not hidden. def nonhidden_dirs - dirs.select do |dir| - !dir.hidden? - end + dirs.reject(&:hidden?) end # Contained files that are not hidden. diff --git a/lib/rush/file.rb b/lib/rush/file.rb index d5c21d7..bb6051e 100644 --- a/lib/rush/file.rb +++ b/lib/rush/file.rb @@ -5,11 +5,16 @@ def dir? false end + def dirname + ::File.dirname full_path + end + # Create a blank file. def create write('') self end + alias_method :touch, :create # Size in bytes on disk. def size @@ -22,7 +27,7 @@ def contents connection.file_contents(full_path) end - alias :read :contents + alias_method :read, :contents alias_method :cat, :contents # Write to the file, overwriting whatever was already in it. @@ -58,6 +63,9 @@ def search(pattern) def replace_contents!(pattern, replace_with) write contents.gsub(pattern, replace_with) end + alias_method :gsub, :replace_contents! + # Because I like vim. + alias_method :s, :replace_contents! # Return the file's contents, or if it doesn't exist, a blank string. def contents_or_blank diff --git a/lib/rush/local.rb b/lib/rush/local.rb index 808906f..d72afa0 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -16,17 +16,13 @@ class Rush::Connection::Local # Write raw bytes to a file. def write_file(full_path, contents) - ::File.open(full_path, 'w') do |f| - f.write contents - end + ::File.open(full_path, 'w') { |f| f.write contents } true end # Append contents to a file def append_to_file(full_path, contents) - ::File.open(full_path, 'a') do |f| - f.write contents - end + ::File.open(full_path, 'a') { |f| f.write contents } true end @@ -48,7 +44,8 @@ def destroy(full_path) def purge(full_path) raise "No." if full_path == '/' Dir.chdir(full_path) do - all = Dir.glob("*", File::FNM_DOTMATCH).reject { |f| f == '.' or f == '..' } + all = Dir.glob("*", File::FNM_DOTMATCH). + reject { |f| f == '.' or f == '..' } FileUtils.rm_rf all end true @@ -288,51 +285,35 @@ def kill_process(pid, options={}) def bash(command, user=nil, background=false, reset_environment=false) return bash_background(command, user, reset_environment) if background - require 'session' - sh = Session::Bash.new - shell = reset_environment ? "env -i bash" : "bash" - - if user and user != "" - out, err = sh.execute "cd /; sudo -H -u #{user} \"#{shell}\"", :stdin => command - else - out, err = sh.execute shell, :stdin => command - end - + out, err = sh.execute sudo(user, shell), :stdin => command retval = sh.status sh.close! - raise(Rush::BashFailed, err) if retval != 0 - out end def bash_background(command, user, reset_environment) pid = fork do inpipe, outpipe = IO.pipe - outpipe.write command outpipe.close STDIN.reopen(inpipe) - close_all_descriptors([inpipe.to_i]) - shell = reset_environment ? "env -i bash" : "bash" - - if user and user != '' - exec "cd /; sudo -H -u #{user} \"#{shell}\"" - else - exec shell - end + exec sudo(user, shell) end - Process::detach pid - pid end + def sudo(user, shell) + return shell if user.nil? || user.empty? + "cd /; sudo -H -u #{user} \"#{shell}\"" + end + def close_all_descriptors(keep_open = []) 3.upto(256) do |fd| next if keep_open.include?(fd) diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 18355fd..f167898 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -110,7 +110,7 @@ def print_result(res) else output = "=> #{res.inspect}" end - output.lines.size > 5 ? output.less : puts(output) + output.lines.count > 5 ? output.less : puts(output) end def path_parts(input) # :nodoc: From 26a1d8f50669b33a58d0c6e88fa09f1706b7620c Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Mon, 31 Mar 2014 22:21:42 +0400 Subject: [PATCH 018/119] Add travis c support --- .travis.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..31b11cd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: ruby +bundler_args: --without development +script: "bundle exec rake" +env: + - CI=true +rvm: + - jruby-20mode + - 2.0.0 + - 2.1.0 +gemfile: + - Gemfile +notifications: + recipients: + - smaginsergey1310@gmail.com +branches: + only: + - master From 68edff798f53dbbdd728db9d38cced670e3b8cc0 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Mon, 31 Mar 2014 22:41:53 +0400 Subject: [PATCH 019/119] Add rake to dependencies --- Gemfile.lock | 54 +++++++++++++++++++++++++++++++++++++++++++--------- Rakefile | 2 +- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e9c191a..9da4cb7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,20 +7,56 @@ PATH GEM remote: http://rubygems.org/ specs: - fattr (2.2.0) - git (1.2.5) - jeweler (1.8.3) - bundler (~> 1.0) + addressable (2.3.6) + atomic (1.1.16) + builder (3.2.2) + descendants_tracker (0.0.4) + thread_safe (~> 0.3, >= 0.3.1) + faraday (0.9.0) + multipart-post (>= 1.2, < 3) + git (1.2.6) + github_api (0.11.3) + addressable (~> 2.3) + descendants_tracker (~> 0.0.1) + faraday (~> 0.8, < 0.10) + hashie (>= 1.2) + multi_json (>= 1.7.5, < 2.0) + nokogiri (~> 1.6.0) + oauth2 + hashie (2.0.5) + highline (1.6.21) + jeweler (2.0.1) + builder + bundler (>= 1.0) git (>= 1.2.5) + github_api + highline (>= 1.6.15) + nokogiri (>= 1.5.10) rake rdoc - json (1.6.5) - rake (0.9.2.2) - rdoc (3.12) + json (1.8.1) + jwt (0.1.11) + multi_json (>= 1.5) + mini_portile (0.5.3) + multi_json (1.9.2) + multi_xml (0.5.5) + multipart-post (2.0.0) + nokogiri (1.6.1) + mini_portile (~> 0.5.0) + oauth2 (0.9.3) + faraday (>= 0.8, < 0.10) + jwt (~> 0.1.8) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (~> 1.2) + rack (1.5.2) + rake (10.2.2) + rdoc (4.1.1) json (~> 1.4) rspec (1.2.9) - session (3.1.0) - fattr + session (3.1.2) + thread_safe (0.3.1) + atomic (>= 1.1.7, < 2) PLATFORMS ruby diff --git a/Rakefile b/Rakefile index 7c435ab..2b19188 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,4 @@ require 'rake' - require 'jeweler' Jeweler::Tasks.new do |s| @@ -14,6 +13,7 @@ Jeweler::Tasks.new do |s| s.has_rdoc = true s.add_dependency 'session' + s.add_dependency 'rake' s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"] end From 704a9f2ae8bad640f824433ba8c0b3edfd6ed0e1 Mon Sep 17 00:00:00 2001 From: Pavel Ivanov Date: Tue, 1 Apr 2014 01:19:17 +0400 Subject: [PATCH 020/119] Update gemspec. Fix travis config --- .travis.yml | 1 - Gemfile.lock | 1 + Rakefile | 53 +++++++++-------- rush.gemspec | 162 +++++++++++++++++++++++---------------------------- 4 files changed, 101 insertions(+), 116 deletions(-) diff --git a/.travis.yml b/.travis.yml index 31b11cd..bf0dc0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,4 @@ language: ruby -bundler_args: --without development script: "bundle exec rake" env: - CI=true diff --git a/Gemfile.lock b/Gemfile.lock index 9da4cb7..b2f71a9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,6 +2,7 @@ PATH remote: . specs: rush (0.6.8) + rush session GEM diff --git a/Rakefile b/Rakefile index 2b19188..a108b69 100644 --- a/Rakefile +++ b/Rakefile @@ -2,20 +2,19 @@ require 'rake' require 'jeweler' Jeweler::Tasks.new do |s| - s.name = "rush" - s.summary = "A Ruby replacement for bash+ssh." - s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." - s.author = "Adam Wiggins" - s.email = "adam@heroku.com" - s.homepage = "http://rush.heroku.com/" - s.executables = [ "rush", "rushd" ] - s.rubyforge_project = "ruby-shell" - s.has_rdoc = true - - s.add_dependency 'session' - s.add_dependency 'rake' - - s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"] + s.name = "rush" + s.summary = "A Ruby replacement for bash+ssh." + s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." + s.author = "Adam Wiggins" + s.email = "adam@heroku.com" + s.homepage = "http://rush.heroku.com/" + s.executables = [ "rush", "rushd" ] + s.rubyforge_project = "ruby-shell" + s.has_rdoc = true + + s.add_dependency 'session' + + s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"] end Jeweler::GemcutterTasks.new @@ -26,20 +25,20 @@ require 'spec/rake/spectask' desc "Run all specs" Spec::Rake::SpecTask.new('spec') do |t| - t.spec_files = FileList['spec/*_spec.rb'] + t.spec_files = FileList['spec/*_spec.rb'] end desc "Print specdocs" Spec::Rake::SpecTask.new(:doc) do |t| - t.spec_opts = ["--format", "specdoc", "--dry-run"] - t.spec_files = FileList['spec/*_spec.rb'] + t.spec_opts = ["--format", "specdoc", "--dry-run"] + t.spec_files = FileList['spec/*_spec.rb'] end desc "Run all examples with RCov" Spec::Rake::SpecTask.new('rcov') do |t| - t.spec_files = FileList['spec/*_spec.rb'] - t.rcov = true - t.rcov_opts = ['--exclude', 'examples'] + t.spec_files = FileList['spec/*_spec.rb'] + t.rcov = true + t.rcov_opts = ['--exclude', 'examples'] end task :default => :spec @@ -49,12 +48,12 @@ task :default => :spec require 'rdoc/task' Rake::RDocTask.new do |t| - t.rdoc_dir = 'rdoc' - t.title = "rush, a Ruby replacement for bash+ssh" - t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object' - t.options << '--charset' << 'utf-8' - t.rdoc_files.include('README.rdoc') - t.rdoc_files.include('lib/rush.rb') - t.rdoc_files.include('lib/rush/*.rb') + t.rdoc_dir = 'rdoc' + t.title = "rush, a Ruby replacement for bash+ssh" + t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object' + t.options << '--charset' << 'utf-8' + t.rdoc_files.include('README.rdoc') + t.rdoc_files.include('lib/rush.rb') + t.rdoc_files.include('lib/rush/*.rb') end diff --git a/rush.gemspec b/rush.gemspec index b7f081a..3c93d63 100644 --- a/rush.gemspec +++ b/rush.gemspec @@ -1,116 +1,102 @@ # Generated by jeweler # DO NOT EDIT THIS FILE DIRECTLY -# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command +# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- +# stub: rush 0.6.8 ruby lib Gem::Specification.new do |s| - s.name = %q{rush} + s.name = "rush" s.version = "0.6.8" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib"] s.authors = ["Adam Wiggins"] - s.date = %q{2010-01-29} - s.description = %q{A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client.} - s.email = %q{adam@heroku.com} + s.date = "2014-03-31" + s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." + s.email = "adam@heroku.com" s.executables = ["rush", "rushd"] s.extra_rdoc_files = [ "README.rdoc" ] s.files = [ + "Gemfile", + "Gemfile.lock", "README.rdoc", - "Rakefile", - "VERSION", - "bin/rush", - "bin/rushd", - "lib/rush.rb", - "lib/rush/access.rb", - "lib/rush/array_ext.rb", - "lib/rush/box.rb", - "lib/rush/commands.rb", - "lib/rush/config.rb", - "lib/rush/dir.rb", - "lib/rush/embeddable_shell.rb", - "lib/rush/entry.rb", - "lib/rush/exceptions.rb", - "lib/rush/file.rb", - "lib/rush/find_by.rb", - "lib/rush/fixnum_ext.rb", - "lib/rush/head_tail.rb", - "lib/rush/local.rb", - "lib/rush/process.rb", - "lib/rush/process_set.rb", - "lib/rush/remote.rb", - "lib/rush/search_results.rb", - "lib/rush/server.rb", - "lib/rush/shell.rb", - "lib/rush/ssh_tunnel.rb", - "lib/rush/string_ext.rb", - "spec/access_spec.rb", - "spec/array_ext_spec.rb", - "spec/base.rb", - "spec/box_spec.rb", - "spec/commands_spec.rb", - "spec/config_spec.rb", - "spec/dir_spec.rb", - "spec/embeddable_shell_spec.rb", - "spec/entry_spec.rb", - "spec/file_spec.rb", - "spec/find_by_spec.rb", - "spec/fixnum_ext_spec.rb", - "spec/local_spec.rb", - "spec/process_set_spec.rb", - "spec/process_spec.rb", - "spec/remote_spec.rb", - "spec/rush_spec.rb", - "spec/search_results_spec.rb", - "spec/shell_spec.rb", - "spec/ssh_tunnel_spec.rb", - "spec/string_ext_spec.rb" - ] - s.homepage = %q{http://rush.heroku.com/} - s.rdoc_options = ["--charset=UTF-8"] - s.require_paths = ["lib"] - s.rubyforge_project = %q{ruby-shell} - s.rubygems_version = %q{1.3.5} - s.summary = %q{A Ruby replacement for bash+ssh.} - s.test_files = [ + "Rakefile", + "VERSION", + "bin/rush", + "bin/rushd", + "lib/rush.rb", + "lib/rush/access.rb", + "lib/rush/array_ext.rb", + "lib/rush/box.rb", + "lib/rush/commands.rb", + "lib/rush/config.rb", + "lib/rush/dir.rb", + "lib/rush/embeddable_shell.rb", + "lib/rush/entry.rb", + "lib/rush/exceptions.rb", + "lib/rush/file.rb", + "lib/rush/find_by.rb", + "lib/rush/fixnum_ext.rb", + "lib/rush/head_tail.rb", + "lib/rush/local.rb", + "lib/rush/process.rb", + "lib/rush/process_set.rb", + "lib/rush/remote.rb", + "lib/rush/search_results.rb", + "lib/rush/server.rb", + "lib/rush/shell.rb", + "lib/rush/ssh_tunnel.rb", + "lib/rush/string_ext.rb", "spec/access_spec.rb", - "spec/array_ext_spec.rb", - "spec/base.rb", - "spec/box_spec.rb", - "spec/commands_spec.rb", - "spec/config_spec.rb", - "spec/dir_spec.rb", - "spec/embeddable_shell_spec.rb", - "spec/entry_spec.rb", - "spec/file_spec.rb", - "spec/find_by_spec.rb", - "spec/fixnum_ext_spec.rb", - "spec/local_spec.rb", - "spec/process_set_spec.rb", - "spec/process_spec.rb", - "spec/remote_spec.rb", - "spec/rush_spec.rb", - "spec/search_results_spec.rb", - "spec/shell_spec.rb", - "spec/ssh_tunnel_spec.rb", - "spec/string_ext_spec.rb" + "spec/array_ext_spec.rb", + "spec/base.rb", + "spec/box_spec.rb", + "spec/commands_spec.rb", + "spec/config_spec.rb", + "spec/dir_spec.rb", + "spec/embeddable_shell_spec.rb", + "spec/entry_spec.rb", + "spec/file_spec.rb", + "spec/find_by_spec.rb", + "spec/fixnum_ext_spec.rb", + "spec/local_spec.rb", + "spec/process_set_spec.rb", + "spec/process_spec.rb", + "spec/remote_spec.rb", + "spec/rush_spec.rb", + "spec/search_results_spec.rb", + "spec/shell_spec.rb", + "spec/ssh_tunnel_spec.rb", + "spec/string_ext_spec.rb" ] - - s.add_development_dependency("rake", [">= 0.9.0"]) - s.add_development_dependency("jeweler", [">= 1.8.3"]) - s.add_development_dependency("rspec", ["~> 1.2.0"]) + s.homepage = "http://rush.heroku.com/" + s.rubyforge_project = "ruby-shell" + s.rubygems_version = "2.2.1" + s.summary = "A Ruby replacement for bash+ssh." if s.respond_to? :specification_version then - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION - s.specification_version = 3 + s.specification_version = 4 - if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_runtime_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0.9.0"]) + s.add_development_dependency(%q, [">= 1.8.3"]) + s.add_development_dependency(%q, ["~> 1.2.0"]) s.add_runtime_dependency(%q, [">= 0"]) else + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0.9.0"]) + s.add_dependency(%q, [">= 1.8.3"]) + s.add_dependency(%q, ["~> 1.2.0"]) s.add_dependency(%q, [">= 0"]) end else + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0.9.0"]) + s.add_dependency(%q, [">= 1.8.3"]) + s.add_dependency(%q, ["~> 1.2.0"]) s.add_dependency(%q, [">= 0"]) end end From e06ce797aa0b2123b3b20f5290fd46889e4f4185 Mon Sep 17 00:00:00 2001 From: Pavel Ivanov Date: Tue, 1 Apr 2014 01:41:00 +0400 Subject: [PATCH 021/119] Add travis badge --- README.rdoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rdoc b/README.rdoc index a92e8e4..1288f12 100644 --- a/README.rdoc +++ b/README.rdoc @@ -2,6 +2,8 @@ rush is a unix integration library and an interactive shell which uses pure Ruby syntax. Walk directory trees; create, copy, search, and destroy files; find and kill processes - everything you'd normally do with shell commands, now in the strict and elegant world of Ruby. +[![Build Status](https://travis-ci.org/s-mage/rush.svg?branch=master)](https://travis-ci.org/s-mage/rush) + == Usage Count the number of classes in your project using bash: From 2ca4cb43b272c42bd3325df2cd6f312b871b5653 Mon Sep 17 00:00:00 2001 From: Pavel Ivanov Date: Tue, 1 Apr 2014 01:43:08 +0400 Subject: [PATCH 022/119] Fix badge --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 1288f12..c04c472 100644 --- a/README.rdoc +++ b/README.rdoc @@ -2,7 +2,7 @@ rush is a unix integration library and an interactive shell which uses pure Ruby syntax. Walk directory trees; create, copy, search, and destroy files; find and kill processes - everything you'd normally do with shell commands, now in the strict and elegant world of Ruby. -[![Build Status](https://travis-ci.org/s-mage/rush.svg?branch=master)](https://travis-ci.org/s-mage/rush) +{Build Status}[https://travis-ci.org/s-mage/rush] == Usage From 5939d36b00498492628045bfbd132c3994520d7d Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 5 Apr 2014 07:55:11 +0400 Subject: [PATCH 023/119] Remove jruby from travis --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bf0dc0f..32c2127 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ script: "bundle exec rake" env: - CI=true rvm: - - jruby-20mode - 2.0.0 - 2.1.0 gemfile: From 2668a9f298637d4300aa05e6d1d13ad7c89334ec Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 5 Apr 2014 08:39:14 +0400 Subject: [PATCH 024/119] Add rubinius to travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 32c2127..73a900b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ env: rvm: - 2.0.0 - 2.1.0 + - rbx-2 gemfile: - Gemfile notifications: From 15732b2f538f04e7792546763a88d9957072e716 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 5 Apr 2014 08:54:10 +0400 Subject: [PATCH 025/119] Fix spec for rubinius --- spec/process_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/process_spec.rb b/spec/process_spec.rb index fb03dc8..af78b59 100644 --- a/spec/process_spec.rb +++ b/spec/process_spec.rb @@ -34,7 +34,7 @@ end it "knows the executed binary" do - @process.command.should match(/ruby/) + @process.command.should match(/(ruby|rbx)/) end it "knows the command line" do From f762aca1d6d673da5e7e1ec35fabf8f12ff47f01 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 6 Apr 2014 20:53:48 +0400 Subject: [PATCH 026/119] Replace readline with coolline. Add syntax highlight --- lib/rush/.shell.rb.swo | Bin 0 -> 16384 bytes lib/rush/shell.rb | 115 +++++++++++++------------ lib/rush/shell.rb~ | 188 +++++++++++++++++++++++++++++++++++++++++ rush.gemspec | 2 + 4 files changed, 249 insertions(+), 56 deletions(-) create mode 100644 lib/rush/.shell.rb.swo create mode 100644 lib/rush/shell.rb~ diff --git a/lib/rush/.shell.rb.swo b/lib/rush/.shell.rb.swo new file mode 100644 index 0000000000000000000000000000000000000000..9f548d64294223ed9a8a6c2275dc9872757fe6db GIT binary patch literal 16384 zcmeHNON<;x87>khgohm-5#j{DXI7e?c-`wqqM%9EyNR=rbFe|#B$n9OOnQ21b~@YB z-E>v&%w%^B2M%0-1CUdYIDixh2`NNE!ndFZ5+o2pLPFw*cqk}b5C?<0@2?W}7jg$_{Z&(yibS z&Da&Olo#E}D9yJ;7K9v6Vz3lPU2`J_JdXXmdzh|V8^?fS;C2`&WdG!+jy`kt%;{4~ ztX?}#AN|NPx6^}b>lknhI0hU8jseGjW56-s7;p^yk24^%JJ{zjhj;&JuCblZd**(x z+4?7J`DCvCPWwE%N&P?mjl30eKaK&%fMdWh;23ZWI0hU8jseGjW56-s7;p^y7Z?b7 zjC~ya-ggK7;Q#;S`Tw1J8T$?JbKqxy1O~uofro%UA7kvtz-z$QfHvR(4d8CzF5ss} z8G8%(HZTC1z$bxE0Cxhv_#k6ffgb{20lp0MfyaOva2$C1184`l3H${3F7Og?8F&=< z7;x?VjQtLH3-~G^fED0D;2z+Q?_=x-z{|iFfJ?wp;4k+u_9x(N;41Jv;9EclJPfpe zW5D0u%h<1hH-M|a_kmY|mw*Vk3_J{+1P$1Fr$E0N(_@0el@e4>W-zz#r~n z?3ciIfG+`Ofe!=k9%1Z9z{|jkKm=?8Yrt{fKH#@_;qp503h+f>3=Dw_z&YRua1Cqv zci>mR8^Atr3D9eOb6X;FjRriSCC^)Ft?r&)JhQ*oUf654M<-Cw-q?7qy}5a!z1h6K z>9u`ebboVUiCIxvxZ=yS6Zj&ILhhYjqz9UQhL1fwRAf2t3Lk`+?-+v9!80y*epK`w=-E1J-+POT*ErD2QdW<2RxHPS*t z){mov3qMSYMAA8WkXERy;6hM~&_;NssGPvG53-GEnQF`DsJ@${uP6~259NN%4#Z=@ z?Jd!)Y$g53BUW6i(Gis>;ev$!LK-DrW04xA3aWyrR^QbU`!vSb+i@VS7q#6%JAEho zVUSTTr9Itr(Uk5n-535`r)Z^|nVQFknVgW|vRTfHDC*meX?dA;_op3IB&)8$NfDbx zQjcZ9MkM*r50%oJbYhuKGhM0w7`_R-=ti(Dp&n}OWIDK+66R(&e*u(x7XmTx6F%}z zHJf^(X6!}X&2*x+`(iCRO!ISf_PJl9eQx(v>A&e~?xd>9`~Zob-oj z1T};d2eb>M%sW9Cf~qv9R+gu546UO!Wo4#{PhC zgmrqlNVqBmt*TRPN(ac9aVMq zP`XbGRQg4nrYcZr{nR?yV)ZMB|&Yx{hh1@J?aWIWm2uTdHBl$BC#Z5;AU zMri`?=O(W0(@B*SxIcuo1Y6w3b$C%&aB8)tB1j%(j<~s^|KT_$XxwnDI;|l&Av%J? zw1|7uxWrgxQ{GdF@MPLQ zsWZEA8B65)B$2^*Eze=IGeqj(WFwaf?b?EFr3T*#Vz1VLe=Z;^>7Z9zq}8mz4yKC` z4ErjryJ}$akL?FbZl#I^+m{Ij`LO(L4_(#)>;?LY1(csxhj^qx=?athS1iRU5tI zH0X88@-n38{;zAnlbtz^%C@t_BTkiTI=e<{xNbUD`!E?KAvgNh>TON9SE^c8O*5csQ<7;@TSsk8{C*L~I(n6|wpGQ# zd6Qvhh{#6B0G1=Q)~NEY))DaMN@cB3)M5(1vp$gkKJm&~v(U{x7g~ZP#6`4*Oh2VJu zktW%R@-!J@w}QQ)4mK)z1PJ;Nww8hl>=H(U$nF8>GXKF1li2{Qt5na1X|I68D)VA0 zYw3NPs-Z{_wCJfFNQgCt`2^U7&8IV;=t<;hftsCk8_m0V!+=aE21>yopCHBM{BuPF zjT>3km_CGQ54mx2U5VKzHJCg9yYv6ts{k_Bp^xS6{6Dh?uTEX={9kT|=U;MQSLDwB z)ms9U<3+g%nR&?qmfZPYMF|K0gtzx2K3dj_-lbmxD4 dYH;WOo;&}mG~>?y 5 ? output.less : puts(output) end + # Try to do tab completion on dir square brackets and slash accessors. + # + # Example: + # + # dir['subd # presing tab here will produce dir['subdir/ if subdir exists + # dir/'subd # presing tab here will produce dir/'subdir/ if subdir exists + # + # This isn't that cool yet, because it can't do multiple levels of subdirs. + # It does work remotely, though, which is pretty sweet. + def complete(input) + receiver, accessor, *rest = path_parts(input) + return [] unless receiver + case accessor + when /^[\[\/]$/ then complete_path(receiver, accessor, *rest) + when /^\.$/ then complete_method(receiver, accessor, *rest) + when nil then complete_variable(receiver, *rest) + else [] + end + end + + # TODO: get string from space to last dot and do something sane with it. + # I just don't want to figure out what's going on there. def path_parts(input) # :nodoc: case input when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)([\[\/])(['"])([^\3]*)$/ @@ -129,13 +150,10 @@ def path_parts(input) # :nodoc: def complete_method(receiver, dot, partial_name, pre) path = eval("#{receiver}.full_path", @pure_binding) rescue nil box = eval("#{receiver}.box", @pure_binding) rescue nil - if path and box - (box[path].methods - Object.methods).select do |e| - e.match(/^#{Regexp.escape(partial_name)}/) - end.map do |e| - (pre || '') + receiver + dot + e.to_s - end - end + return [] unless (path and box) + box[path].methods. # why box[path] ? + select { |e| e.match /^#{Regexp.escape(partial_name)}/ }. + map { |e| (pre || '') + receiver + dot + e.to_s } end def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc: @@ -146,43 +164,28 @@ def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc: end full_path = eval("#{possible_var}.full_path", @pure_binding) rescue nil box = eval("#{possible_var}.box", @pure_binding) rescue nil - if full_path and box - Rush::Dir.new(full_path, box).entries. - select { |e| e.name.match(/^#{Regexp.escape(partial_path)}/) }. - map { |e| (pre || '') + original_var + accessor + quote + - fixed_path + e.name + (e.dir? ? "/" : "") } - end + return [] unless (full_path and box) + Rush::Dir.new(full_path, box).entries. + select { |e| e.name.match(/^#{Regexp.escape(partial_path)}/) }. + map { |e| (pre || '') + original_var + accessor + quote + + fixed_path + e.name + (e.dir? ? "/" : "") } end def complete_variable(partial_name, pre) - lvars = eval('local_variables', @pure_binding) - gvars = eval('global_variables', @pure_binding) - ivars = eval('instance_variables', @pure_binding) - constants = eval('Object.constants', @pure_binding) - (lvars + gvars + ivars + constants). + pre = eval(pre, @pure_binding) + the_binding = pre ? pre.instance_eval('binding') : @pure_binding + lvars = eval('local_variables', the_binding) + gvars = eval('global_variables', the_binding) + ivars = eval('instance_variables', the_binding) + mets = eval('methods', the_binding) || eval('Kernel.methods') + consts = eval('Object.constants', the_binding) + (lvars + gvars + ivars + mets + consts). select { |e| e.match(/^#{Regexp.escape(partial_name)}/) }. map { |e| (pre || '') + e.to_s } end - # Try to do tab completion on dir square brackets and slash accessors. - # - # Example: - # - # dir['subd # presing tab here will produce dir['subdir/ if subdir exists - # dir/'subd # presing tab here will produce dir/'subdir/ if subdir exists - # - # This isn't that cool yet, because it can't do multiple levels of subdirs. - # It does work remotely, though, which is pretty sweet. - def completion_proc - proc do |input| - receiver, accessor, *rest = path_parts(input) - return unless receiver - case accessor - when /^[\[\/]$/ then complete_path(receiver, accessor, *rest) - when /^\.$/ then complete_method(receiver, accessor, *rest) - when nil then complete_variable(receiver, *rest) - end - end + def syntax_highlight(input) + CodeRay.encode input, :ruby, :term end end end diff --git a/lib/rush/shell.rb~ b/lib/rush/shell.rb~ new file mode 100644 index 0000000..7406391 --- /dev/null +++ b/lib/rush/shell.rb~ @@ -0,0 +1,188 @@ +require 'coolline' +require 'coderay' +require 'pp' + +# Rush::Shell is used to create an interactive shell. It is invoked by the rush binary. +module Rush + class Shell + attr_accessor :suppress_output + # Set up the user's environment, including a pure binding into which + # env.rb and commands.rb are mixed. + def initialize + root = Rush::Dir.new('/') + home = Rush::Dir.new(ENV['HOME']) if ENV['HOME'] + pwd = Rush::Dir.new(ENV['PWD']) if ENV['PWD'] + + @config = Rush::Config.new + + @history = Coolline::History.new @config.history_file.full_path + + @readline = Coolline.new do |c| + c.transform_proc = proc { syntax_highlight c.line } + c.completion_proc = proc { complete c.completed_word } + end + + @box = Rush::Box.new + @pure_binding = @box.instance_eval "binding" + $last_res = nil + + eval @config.load_env, @pure_binding + + commands = @config.load_commands + Rush::Dir.class_eval commands + Array.class_eval commands + + # Multiline commands should be stored somewhere' + @multiline_cmd = '' + end + + # Run a single command. + def execute(cmd) + res = eval(@multiline_cmd << "\n" << cmd, @pure_binding) + $last_res = res + eval("_ = $last_res", @pure_binding) + @multiline_cmd = '' + print_result res + rescue SyntaxError => e + unless e.message.include? 'unexpected end-of-input' + @multiline_cmd = '' + puts "Exception #{e.class} -> #{e.message}" + end + # Else it should be multiline command. + rescue Rush::Exception => e + puts "Exception #{e.class} -> #{e.message}" + @multiline_cmd = '' + rescue ::Exception => e + puts "Exception #{e.class} -> #{e.message}" + e.backtrace.each { |t| puts "\t#{::File.expand_path(t)}" } + @multiline_cmd = '' + end + + # Run the interactive shell using coolline. + def run + loop do + prompt = self.class.prompt || "#{`whoami`.chomp} $ " + cmd = @readline.readline prompt + + finish if cmd.nil? or cmd == 'exit' + next if cmd.empty? + @history << cmd + execute cmd + end + end + + # Tune the prompt with + # Rush::Shell.prompt = 'hey there! > ' + class << self + attr_accessor :prompt + end + + # Save history to ~/.rush/history when the shell exists. + def finish + @config.save_history(Coolline::History.to_a) + puts + exit + end + + # Nice printing of different return types, particularly Rush::SearchResults. + def print_result(res) + return if self.suppress_output + if res.kind_of? String + output = res + elsif res.kind_of? Rush::SearchResults + output = res.to_s << + "#{res.entries.size} matching files with #{res.lines.size} matching lines" + elsif res.respond_to? :each + output = '' + counts = res.inject(Hash.new(0)) do |result, item| + output << item.to_s << "\n" + result[item.class] += 1 + result + end + if counts == {} + output = "=> (empty set)" + else + count_s = counts.map do |klass, count| + "#{count} x #{klass}" + end.join(', ') + output << "=> #{count_s}" + end + else + output = "=> #{res.inspect}" + end + output.lines.count > 5 ? output.less : puts(output) + end + + def path_parts(input) # :nodoc: + case input + when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)([\[\/])(['"])([^\3]*)$/ + $~.to_a.slice(1, 4).push($~.pre_match) + when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)(\.)(\w*)$/ + $~.to_a.slice(1, 3).push($~.pre_match) + when /((?:@{1,2}|\$|)\w+)$/ + $~.to_a.slice(1, 1).push(nil).push($~.pre_match) + else + [ nil, nil, nil ] + end + end + + def complete_method(receiver, dot, partial_name, pre) + path = eval("#{receiver}.full_path", @pure_binding) rescue nil + box = eval("#{receiver}.box", @pure_binding) rescue nil + if path and box + (box[path].methods - Object.methods). + select { |e| e.match /^#{Regexp.escape(partial_name)}/ }. + map { |e| (pre || '') + receiver + dot + e.to_s } + end + end + + def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc: + original_var, fixed_path = possible_var, '' + if /^(.+\/)([^\/]*)$/ === partial_path + fixed_path, partial_path = $~.captures + possible_var += "['#{fixed_path}']" + end + full_path = eval("#{possible_var}.full_path", @pure_binding) rescue nil + box = eval("#{possible_var}.box", @pure_binding) rescue nil + if full_path and box + Rush::Dir.new(full_path, box).entries. + select { |e| e.name.match(/^#{Regexp.escape(partial_path)}/) }. + map { |e| (pre || '') + original_var + accessor + quote + + fixed_path + e.name + (e.dir? ? "/" : "") } + end + end + + def complete_variable(partial_name, pre) + lvars = eval('local_variables', @pure_binding) + gvars = eval('global_variables', @pure_binding) + ivars = eval('instance_variables', @pure_binding) + constants = eval('Object.constants', @pure_binding) + (lvars + gvars + ivars + constants). + select { |e| e.match(/^#{Regexp.escape(partial_name)}/) }. + map { |e| (pre || '') + e.to_s } + end + + # Try to do tab completion on dir square brackets and slash accessors. + # + # Example: + # + # dir['subd # presing tab here will produce dir['subdir/ if subdir exists + # dir/'subd # presing tab here will produce dir/'subdir/ if subdir exists + # + # This isn't that cool yet, because it can't do multiple levels of subdirs. + # It does work remotely, though, which is pretty sweet. + def complete(input) + receiver, accessor, *rest = path_parts(input) + return unless receiver + case accessor + when /^[\[\/]$/ then complete_path(receiver, accessor, *rest) + when /^\.$/ then complete_method(receiver, accessor, *rest) + when nil then complete_variable(receiver, *rest) + end + end + + def syntax_highlight(input) + CodeRay.encode input, :ruby, :term + end + end +end diff --git a/rush.gemspec b/rush.gemspec index 3c93d63..16fc911 100644 --- a/rush.gemspec +++ b/rush.gemspec @@ -85,6 +85,8 @@ Gem::Specification.new do |s| s.add_development_dependency(%q, [">= 1.8.3"]) s.add_development_dependency(%q, ["~> 1.2.0"]) s.add_runtime_dependency(%q, [">= 0"]) + s.add_runtime_dependency(%q, [">= 0"]) + s.add_runtime_dependency(%q, [">= 0"]) else s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0.9.0"]) From cfde57b5a3858f94e13dcd2a5d0d89dee55fb1d5 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 6 Apr 2014 20:55:05 +0400 Subject: [PATCH 027/119] Remove swap file --- lib/rush/.shell.rb.swo | Bin 16384 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 lib/rush/.shell.rb.swo diff --git a/lib/rush/.shell.rb.swo b/lib/rush/.shell.rb.swo deleted file mode 100644 index 9f548d64294223ed9a8a6c2275dc9872757fe6db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHNON<;x87>khgohm-5#j{DXI7e?c-`wqqM%9EyNR=rbFe|#B$n9OOnQ21b~@YB z-E>v&%w%^B2M%0-1CUdYIDixh2`NNE!ndFZ5+o2pLPFw*cqk}b5C?<0@2?W}7jg$_{Z&(yibS z&Da&Olo#E}D9yJ;7K9v6Vz3lPU2`J_JdXXmdzh|V8^?fS;C2`&WdG!+jy`kt%;{4~ ztX?}#AN|NPx6^}b>lknhI0hU8jseGjW56-s7;p^yk24^%JJ{zjhj;&JuCblZd**(x z+4?7J`DCvCPWwE%N&P?mjl30eKaK&%fMdWh;23ZWI0hU8jseGjW56-s7;p^y7Z?b7 zjC~ya-ggK7;Q#;S`Tw1J8T$?JbKqxy1O~uofro%UA7kvtz-z$QfHvR(4d8CzF5ss} z8G8%(HZTC1z$bxE0Cxhv_#k6ffgb{20lp0MfyaOva2$C1184`l3H${3F7Og?8F&=< z7;x?VjQtLH3-~G^fED0D;2z+Q?_=x-z{|iFfJ?wp;4k+u_9x(N;41Jv;9EclJPfpe zW5D0u%h<1hH-M|a_kmY|mw*Vk3_J{+1P$1Fr$E0N(_@0el@e4>W-zz#r~n z?3ciIfG+`Ofe!=k9%1Z9z{|jkKm=?8Yrt{fKH#@_;qp503h+f>3=Dw_z&YRua1Cqv zci>mR8^Atr3D9eOb6X;FjRriSCC^)Ft?r&)JhQ*oUf654M<-Cw-q?7qy}5a!z1h6K z>9u`ebboVUiCIxvxZ=yS6Zj&ILhhYjqz9UQhL1fwRAf2t3Lk`+?-+v9!80y*epK`w=-E1J-+POT*ErD2QdW<2RxHPS*t z){mov3qMSYMAA8WkXERy;6hM~&_;NssGPvG53-GEnQF`DsJ@${uP6~259NN%4#Z=@ z?Jd!)Y$g53BUW6i(Gis>;ev$!LK-DrW04xA3aWyrR^QbU`!vSb+i@VS7q#6%JAEho zVUSTTr9Itr(Uk5n-535`r)Z^|nVQFknVgW|vRTfHDC*meX?dA;_op3IB&)8$NfDbx zQjcZ9MkM*r50%oJbYhuKGhM0w7`_R-=ti(Dp&n}OWIDK+66R(&e*u(x7XmTx6F%}z zHJf^(X6!}X&2*x+`(iCRO!ISf_PJl9eQx(v>A&e~?xd>9`~Zob-oj z1T};d2eb>M%sW9Cf~qv9R+gu546UO!Wo4#{PhC zgmrqlNVqBmt*TRPN(ac9aVMq zP`XbGRQg4nrYcZr{nR?yV)ZMB|&Yx{hh1@J?aWIWm2uTdHBl$BC#Z5;AU zMri`?=O(W0(@B*SxIcuo1Y6w3b$C%&aB8)tB1j%(j<~s^|KT_$XxwnDI;|l&Av%J? zw1|7uxWrgxQ{GdF@MPLQ zsWZEA8B65)B$2^*Eze=IGeqj(WFwaf?b?EFr3T*#Vz1VLe=Z;^>7Z9zq}8mz4yKC` z4ErjryJ}$akL?FbZl#I^+m{Ij`LO(L4_(#)>;?LY1(csxhj^qx=?athS1iRU5tI zH0X88@-n38{;zAnlbtz^%C@t_BTkiTI=e<{xNbUD`!E?KAvgNh>TON9SE^c8O*5csQ<7;@TSsk8{C*L~I(n6|wpGQ# zd6Qvhh{#6B0G1=Q)~NEY))DaMN@cB3)M5(1vp$gkKJm&~v(U{x7g~ZP#6`4*Oh2VJu zktW%R@-!J@w}QQ)4mK)z1PJ;Nww8hl>=H(U$nF8>GXKF1li2{Qt5na1X|I68D)VA0 zYw3NPs-Z{_wCJfFNQgCt`2^U7&8IV;=t<;hftsCk8_m0V!+=aE21>yopCHBM{BuPF zjT>3km_CGQ54mx2U5VKzHJCg9yYv6ts{k_Bp^xS6{6Dh?uTEX={9kT|=U;MQSLDwB z)ms9U<3+g%nR&?qmfZPYMF|K0gtzx2K3dj_-lbmxD4 dYH;WOo;&}mG~>?y Date: Sat, 12 Apr 2014 02:38:52 +0400 Subject: [PATCH 028/119] Fix gemspec --- Gemfile.lock | 6 +++++- Rakefile | 2 ++ rush.gemspec | 6 +++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b2f71a9..9fa057d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,6 +2,8 @@ PATH remote: . specs: rush (0.6.8) + coderay + coolline rush session @@ -11,6 +13,8 @@ GEM addressable (2.3.6) atomic (1.1.16) builder (3.2.2) + coderay (1.1.0) + coolline (0.4.3) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) faraday (0.9.0) @@ -55,7 +59,7 @@ GEM rdoc (4.1.1) json (~> 1.4) rspec (1.2.9) - session (3.1.2) + session (3.2.0) thread_safe (0.3.1) atomic (>= 1.1.7, < 2) diff --git a/Rakefile b/Rakefile index a108b69..3fa354c 100644 --- a/Rakefile +++ b/Rakefile @@ -13,6 +13,8 @@ Jeweler::Tasks.new do |s| s.has_rdoc = true s.add_dependency 'session' + s.add_dependency 'coolline' + s.add_dependency 'coderay' s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"] end diff --git a/rush.gemspec b/rush.gemspec index 16fc911..70b3b81 100644 --- a/rush.gemspec +++ b/rush.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] s.authors = ["Adam Wiggins"] - s.date = "2014-03-31" + s.date = "2014-04-11" s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." s.email = "adam@heroku.com" s.executables = ["rush", "rushd"] @@ -93,6 +93,8 @@ Gem::Specification.new do |s| s.add_dependency(%q, [">= 1.8.3"]) s.add_dependency(%q, ["~> 1.2.0"]) s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) end else s.add_dependency(%q, [">= 0"]) @@ -100,6 +102,8 @@ Gem::Specification.new do |s| s.add_dependency(%q, [">= 1.8.3"]) s.add_dependency(%q, ["~> 1.2.0"]) s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) end end From cc0bd96908eb71d100d492c251afa9a379f6941a Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 19 Apr 2014 08:29:34 +0400 Subject: [PATCH 029/119] Small improvements --- lib/rush/commands.rb | 2 +- lib/rush/string_ext.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index fe1de9d..ddca174 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -65,7 +65,7 @@ def open(*args) # Usage: # home.locate('timetable').open_witn :vim def open_with(app, *args) - names = entries.map(&:to_s).join(' ') + names = dir? ? '' : entries.map(&:to_s).join(' ') system "cd #{dirname}; #{app.to_s} #{names} #{args.join(' ')}" end diff --git a/lib/rush/string_ext.rb b/lib/rush/string_ext.rb index 0ecf8e5..5e69f6f 100644 --- a/lib/rush/string_ext.rb +++ b/lib/rush/string_ext.rb @@ -9,4 +9,8 @@ def less def dir? ::Dir.exists? self end + + def locate + Rush::Dir.new(ENV['HOME']).locate self + end end From 885e0257945974ead9d7a4dbe912e042ee997af8 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 2 May 2014 20:53:20 +0200 Subject: [PATCH 030/119] Add installation instructions to the README On copying the documentation from heroku I came accross installation instructions that seem to have gotten lost when the project came to github. I felt it would be useful to reintegrate them in the README. I don't know if there was a motivation for not bringing them in from heroku. --- README.rdoc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.rdoc b/README.rdoc index c04c472..7d0fda6 100644 --- a/README.rdoc +++ b/README.rdoc @@ -4,6 +4,27 @@ rush is a unix integration library and an interactive shell which uses pure Ruby {Build Status}[https://travis-ci.org/s-mage/rush] + +== Install + +Install v.0.6.8 from rubygems.org ~ no longer maintained + + sudo gem install rush + + +Or for the bleeding edge: + + git clone git://github.com/s-mage/rush.git + cd rush + gem build *.gemspec + sudo gem install *.gem + + +If you want the development version that last command would be: + + sudo gem install --dev *.gem + + == Usage Count the number of classes in your project using bash: From 74f67df5a9e75dc718218635b6f22e227f0faa89 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 2 May 2014 21:07:19 +0200 Subject: [PATCH 031/119] add uninstall instructions also I'm not a veteran ruby expert, I don't know if this suffices. --- README.rdoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rdoc b/README.rdoc index 7d0fda6..9f0781b 100644 --- a/README.rdoc +++ b/README.rdoc @@ -25,6 +25,11 @@ If you want the development version that last command would be: sudo gem install --dev *.gem +To uninstall + + sudo gem uninstall rush + + == Usage Count the number of classes in your project using bash: From 884bc5235ebac0fafdb28efc995f5bee387fb1c4 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 3 May 2014 12:15:54 +0200 Subject: [PATCH 032/119] Added ruby-dev to requirements in readme For the gem install --dev option --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 9f0781b..6e85887 100644 --- a/README.rdoc +++ b/README.rdoc @@ -20,7 +20,7 @@ Or for the bleeding edge: sudo gem install *.gem -If you want the development version that last command would be: +If you want the development version, you will need the development headers for ruby (eg. ruby-dev in most package managers) and change that last command to: sudo gem install --dev *.gem From e3d06171ed783d08928be2957ef6e3e2a45f0ec3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 3 May 2014 12:24:19 +0200 Subject: [PATCH 033/119] Add documentation and .gem file to .gitignore This adds *.gem doc/ .yardoc/ --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9fe97de..863b636 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ rdoc pkg .rbenv-version *.swp +*.gem +doc/ +.yardoc/ tags From 12d6106a8b671a309390c6edb2b988346e022a67 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 3 May 2014 12:30:07 +0200 Subject: [PATCH 034/119] Update website and meta in the readme There's not much point for outdated information --- README.rdoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rdoc b/README.rdoc index c04c472..3de6fa5 100644 --- a/README.rdoc +++ b/README.rdoc @@ -75,6 +75,10 @@ For more details on syntax and commands, see: == Meta +What follows is the meta from the original project, which is no longer maintained. +The mailinglist is still functional and the new website for this fork is: +https://github.com/s-mage/rush + Created by Adam Wiggins Patches contributed by Chihiro Ito, Gabriel Ware, Michael Schutte, Ricardo Chimal Jr., and Nicholas Schlueter, Pedro Belo, and Martin Kuehl From 7e9f07e2748e4c4de0aea2e42942c2a97d74b58f Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 4 May 2014 17:08:06 +0400 Subject: [PATCH 035/119] Improve work with requirements. Remove circular dependency and some "require rubygems" --- Gemfile | 4 +++- Gemfile.lock | 1 - Rakefile | 5 +---- bin/rushd | 4 ++-- lib/rush.rb | 42 +++++++++++++++++------------------ lib/rush/embeddable_shell.rb | 2 +- lib/rush/server.rb | 1 - rush.gemspec | 19 ++++------------ spec/access_spec.rb | 2 +- spec/array_ext_spec.rb | 2 +- spec/base.rb | 3 +-- spec/box_spec.rb | 2 +- spec/commands_spec.rb | 2 +- spec/config_spec.rb | 2 +- spec/dir_spec.rb | 2 +- spec/embeddable_shell_spec.rb | 2 +- spec/entry_spec.rb | 2 +- spec/file_spec.rb | 2 +- spec/find_by_spec.rb | 2 +- spec/fixnum_ext_spec.rb | 2 +- spec/local_spec.rb | 2 +- spec/process_set_spec.rb | 2 +- spec/process_spec.rb | 2 +- spec/remote_spec.rb | 2 +- spec/rush_spec.rb | 2 +- spec/search_results_spec.rb | 2 +- spec/shell_spec.rb | 4 ++-- spec/ssh_tunnel_spec.rb | 2 +- spec/string_ext_spec.rb | 2 +- 29 files changed, 53 insertions(+), 70 deletions(-) diff --git a/Gemfile b/Gemfile index c80ee36..2ff83e3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ source "http://rubygems.org" -gemspec +gem 'session' +gem 'coolline' +gem 'coderay' diff --git a/Gemfile.lock b/Gemfile.lock index 9fa057d..e79b0c0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -70,4 +70,3 @@ DEPENDENCIES jeweler (>= 1.8.3) rake (>= 0.9.0) rspec (~> 1.2.0) - rush! diff --git a/Rakefile b/Rakefile index 3fa354c..62949a7 100644 --- a/Rakefile +++ b/Rakefile @@ -8,14 +8,11 @@ Jeweler::Tasks.new do |s| s.author = "Adam Wiggins" s.email = "adam@heroku.com" s.homepage = "http://rush.heroku.com/" + s.licenses = ['MIT'] s.executables = [ "rush", "rushd" ] s.rubyforge_project = "ruby-shell" s.has_rdoc = true - s.add_dependency 'session' - s.add_dependency 'coolline' - s.add_dependency 'coderay' - s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"] end diff --git a/bin/rushd b/bin/rushd index cf17415..286c402 100755 --- a/bin/rushd +++ b/bin/rushd @@ -1,7 +1,7 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../lib/rush' -require File.dirname(__FILE__) + '/../lib/rush/server' +require_relative __dir__ + '/../lib/rush' +require_relative __dir__ + '/../lib/rush/server' RushServer.new.run diff --git a/lib/rush.rb b/lib/rush.rb index ff17f1c..864a89b 100644 --- a/lib/rush.rb +++ b/lib/rush.rb @@ -1,5 +1,3 @@ -require 'rubygems' - # The top-level Rush module has some convenience methods for accessing the # local box. module Rush @@ -65,23 +63,23 @@ module Rush::Connection; end $LOAD_PATH.unshift(File.dirname(__FILE__)) -require 'rush/exceptions' -require 'rush/config' -require 'rush/commands' -require 'rush/access' -require 'rush/entry' -require 'rush/file' -require 'rush/dir' -require 'rush/search_results' -require 'rush/head_tail' -require 'rush/find_by' -require 'rush/string_ext' -require 'rush/fixnum_ext' -require 'rush/array_ext' -require 'rush/process' -require 'rush/process_set' -require 'rush/local' -require 'rush/remote' -require 'rush/ssh_tunnel' -require 'rush/box' -require 'rush/embeddable_shell' +require_relative 'rush/exceptions' +require_relative 'rush/config' +require_relative 'rush/commands' +require_relative 'rush/access' +require_relative 'rush/entry' +require_relative 'rush/file' +require_relative 'rush/dir' +require_relative 'rush/search_results' +require_relative 'rush/head_tail' +require_relative 'rush/find_by' +require_relative 'rush/string_ext' +require_relative 'rush/fixnum_ext' +require_relative 'rush/array_ext' +require_relative 'rush/process' +require_relative 'rush/process_set' +require_relative 'rush/local' +require_relative 'rush/remote' +require_relative 'rush/ssh_tunnel' +require_relative 'rush/box' +require_relative 'rush/embeddable_shell' diff --git a/lib/rush/embeddable_shell.rb b/lib/rush/embeddable_shell.rb index 114c56f..0b125a2 100644 --- a/lib/rush/embeddable_shell.rb +++ b/lib/rush/embeddable_shell.rb @@ -1,4 +1,4 @@ -require 'rush/shell' +require_relative 'shell' module Rush # This is a class that can be embedded in other applications diff --git a/lib/rush/server.rb b/lib/rush/server.rb index 4b22f7d..168a4f3 100644 --- a/lib/rush/server.rb +++ b/lib/rush/server.rb @@ -1,4 +1,3 @@ -require 'rubygems' require 'mongrel' require 'base64' diff --git a/rush.gemspec b/rush.gemspec index 70b3b81..4b7f83f 100644 --- a/rush.gemspec +++ b/rush.gemspec @@ -9,9 +9,8 @@ Gem::Specification.new do |s| s.version = "0.6.8" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.require_paths = ["lib"] s.authors = ["Adam Wiggins"] - s.date = "2014-04-11" + s.date = "2014-05-04" s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." s.email = "adam@heroku.com" s.executables = ["rush", "rushd"] @@ -72,35 +71,25 @@ Gem::Specification.new do |s| "spec/string_ext_spec.rb" ] s.homepage = "http://rush.heroku.com/" + s.licenses = ["MIT"] + s.require_paths = ["lib"] s.rubyforge_project = "ruby-shell" - s.rubygems_version = "2.2.1" + s.rubygems_version = "2.1.9" s.summary = "A Ruby replacement for bash+ssh." if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0.9.0"]) - s.add_development_dependency(%q, [">= 1.8.3"]) - s.add_development_dependency(%q, ["~> 1.2.0"]) s.add_runtime_dependency(%q, [">= 0"]) s.add_runtime_dependency(%q, [">= 0"]) s.add_runtime_dependency(%q, [">= 0"]) else - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0.9.0"]) - s.add_dependency(%q, [">= 1.8.3"]) - s.add_dependency(%q, ["~> 1.2.0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) end else - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0.9.0"]) - s.add_dependency(%q, [">= 1.8.3"]) - s.add_dependency(%q, ["~> 1.2.0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) diff --git a/spec/access_spec.rb b/spec/access_spec.rb index e53ad1f..18ac4d2 100644 --- a/spec/access_spec.rb +++ b/spec/access_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::Access do before do diff --git a/spec/array_ext_spec.rb b/spec/array_ext_spec.rb index c186a52..d050d72 100644 --- a/spec/array_ext_spec.rb +++ b/spec/array_ext_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Array do it "mixes commands into array" do diff --git a/spec/base.rb b/spec/base.rb index 2a0789b..244db71 100644 --- a/spec/base.rb +++ b/spec/base.rb @@ -1,8 +1,7 @@ -require 'rubygems' require 'spec' $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') -require 'rush' +require_relative '../lib/rush' def mock_config(&block) mock_config_start diff --git a/spec/box_spec.rb b/spec/box_spec.rb index 0e2e94b..874f49e 100644 --- a/spec/box_spec.rb +++ b/spec/box_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::Box do before do diff --git a/spec/commands_spec.rb b/spec/commands_spec.rb index 0f79757..fd2ea5f 100644 --- a/spec/commands_spec.rb +++ b/spec/commands_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::Commands do before do diff --git a/spec/config_spec.rb b/spec/config_spec.rb index 75bb230..24f7ebb 100644 --- a/spec/config_spec.rb +++ b/spec/config_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::Config do before do diff --git a/spec/dir_spec.rb b/spec/dir_spec.rb index d6990bf..3fce5f3 100644 --- a/spec/dir_spec.rb +++ b/spec/dir_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::Dir do before do diff --git a/spec/embeddable_shell_spec.rb b/spec/embeddable_shell_spec.rb index 67734d1..95f7253 100644 --- a/spec/embeddable_shell_spec.rb +++ b/spec/embeddable_shell_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::EmbeddableShell do before do diff --git a/spec/entry_spec.rb b/spec/entry_spec.rb index 16f1882..6eb7094 100644 --- a/spec/entry_spec.rb +++ b/spec/entry_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::Entry do before do diff --git a/spec/file_spec.rb b/spec/file_spec.rb index f6a944d..0a795b0 100644 --- a/spec/file_spec.rb +++ b/spec/file_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::File do before do diff --git a/spec/find_by_spec.rb b/spec/find_by_spec.rb index 46c0475..29ffdf7 100644 --- a/spec/find_by_spec.rb +++ b/spec/find_by_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::FindBy do before do diff --git a/spec/fixnum_ext_spec.rb b/spec/fixnum_ext_spec.rb index 4f9dc61..04d8cb2 100644 --- a/spec/fixnum_ext_spec.rb +++ b/spec/fixnum_ext_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Fixnum do before do diff --git a/spec/local_spec.rb b/spec/local_spec.rb index 167559e..7511751 100644 --- a/spec/local_spec.rb +++ b/spec/local_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::Connection::Local do before do diff --git a/spec/process_set_spec.rb b/spec/process_set_spec.rb index 7b72667..e12a406 100644 --- a/spec/process_set_spec.rb +++ b/spec/process_set_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::ProcessSet do before do diff --git a/spec/process_spec.rb b/spec/process_spec.rb index af78b59..73e881c 100644 --- a/spec/process_spec.rb +++ b/spec/process_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::Process do before do diff --git a/spec/remote_spec.rb b/spec/remote_spec.rb index 11ce1a8..8e43123 100644 --- a/spec/remote_spec.rb +++ b/spec/remote_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::Connection::Local do before do diff --git a/spec/rush_spec.rb b/spec/rush_spec.rb index 7da4b1b..273f26e 100644 --- a/spec/rush_spec.rb +++ b/spec/rush_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush do it "fetches a local file path" do diff --git a/spec/search_results_spec.rb b/spec/search_results_spec.rb index 045396b..24ff947 100644 --- a/spec/search_results_spec.rb +++ b/spec/search_results_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::SearchResults do before do diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index efcba7c..07f8989 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -1,5 +1,5 @@ -require __dir__ + '/base' -require 'rush/shell' +require_relative 'base' +require_relative '../lib/rush/shell' describe Rush::Shell do before do diff --git a/spec/ssh_tunnel_spec.rb b/spec/ssh_tunnel_spec.rb index 0d9dbc3..35b421b 100644 --- a/spec/ssh_tunnel_spec.rb +++ b/spec/ssh_tunnel_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe Rush::SshTunnel do before do diff --git a/spec/string_ext_spec.rb b/spec/string_ext_spec.rb index 664b51f..738c77e 100644 --- a/spec/string_ext_spec.rb +++ b/spec/string_ext_spec.rb @@ -1,4 +1,4 @@ -require __dir__ + '/base' +require_relative 'base' describe String do before do From 57dab755b12717a53935712644aaaee998b30dc4 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 4 May 2014 17:15:49 +0400 Subject: [PATCH 036/119] Add development group to Gemfile --- Gemfile | 6 ++++++ Gemfile.lock | 18 ++++++------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Gemfile b/Gemfile index 2ff83e3..8a92777 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,9 @@ source "http://rubygems.org" gem 'session' gem 'coolline' gem 'coderay' + +group :development do + gem 'rake' + gem 'jeweler' + gem 'rspec' +end diff --git a/Gemfile.lock b/Gemfile.lock index e79b0c0..ad64494 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,12 +1,3 @@ -PATH - remote: . - specs: - rush (0.6.8) - coderay - coolline - rush - session - GEM remote: http://rubygems.org/ specs: @@ -67,6 +58,9 @@ PLATFORMS ruby DEPENDENCIES - jeweler (>= 1.8.3) - rake (>= 0.9.0) - rspec (~> 1.2.0) + coderay + coolline + jeweler + rake + rspec + session From b86f191e88b15251ed60ff944be5d8b825a9e915 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 4 May 2014 22:30:32 +0200 Subject: [PATCH 037/119] Use rspec2 instead of legacy rspec All tests pass, but with deprecated methods. --- Rakefile | 16 ++++++++-------- spec/base.rb | 2 +- spec/ssh_tunnel_spec.rb | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Rakefile b/Rakefile index 62949a7..bdcaacb 100644 --- a/Rakefile +++ b/Rakefile @@ -20,22 +20,22 @@ Jeweler::GemcutterTasks.new ###################################################### -require 'spec/rake/spectask' +require 'rspec/core/rake_task' desc "Run all specs" -Spec::Rake::SpecTask.new('spec') do |t| - t.spec_files = FileList['spec/*_spec.rb'] +RSpec::Core::RakeTask.new('spec') do |t| + t.pattern = 'spec/*_spec.rb' end desc "Print specdocs" -Spec::Rake::SpecTask.new(:doc) do |t| - t.spec_opts = ["--format", "specdoc", "--dry-run"] - t.spec_files = FileList['spec/*_spec.rb'] +RSpec::Core::RakeTask.new(:doc) do |t| + t.pattern = 'spec/*_spec.rb' + t.rspec_opts = ["--format", "specdoc", "--dry-run"] end desc "Run all examples with RCov" -Spec::Rake::SpecTask.new('rcov') do |t| - t.spec_files = FileList['spec/*_spec.rb'] +RSpec::Core::RakeTask.new('rcov') do |t| + t.pattern = 'spec/*_spec.rb' t.rcov = true t.rcov_opts = ['--exclude', 'examples'] end diff --git a/spec/base.rb b/spec/base.rb index 244db71..5a804d2 100644 --- a/spec/base.rb +++ b/spec/base.rb @@ -1,4 +1,4 @@ -require 'spec' +require 'rspec' $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') require_relative '../lib/rush' diff --git a/spec/ssh_tunnel_spec.rb b/spec/ssh_tunnel_spec.rb index 35b421b..8dd5c38 100644 --- a/spec/ssh_tunnel_spec.rb +++ b/spec/ssh_tunnel_spec.rb @@ -48,7 +48,7 @@ it "establishes a tunnel and saves it to ~/.rush/tunnels" do @tunnel.should_receive(:make_ssh_tunnel) - @tunnel.should_receive(:port).exactly(0).times.and_return('7771') # avoid infinite loop + @tunnel.should_receive(:port).exactly(0).times @tunnel.establish_tunnel @tunnel.config.tunnels_file.contents.should == "spec.example.com:7771\n" end From a5244447cc992a1b75c49aca84691e4b5f71c126 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 4 May 2014 22:27:58 +0200 Subject: [PATCH 038/119] Deprecated function in rspec2: stub! Replaced by stub --- spec/box_spec.rb | 4 ++-- spec/local_spec.rb | 4 ++-- spec/process_set_spec.rb | 4 ++-- spec/remote_spec.rb | 2 +- spec/rush_spec.rb | 2 +- spec/ssh_tunnel_spec.rb | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/box_spec.rb b/spec/box_spec.rb index 874f49e..df34086 100644 --- a/spec/box_spec.rb +++ b/spec/box_spec.rb @@ -33,7 +33,7 @@ it "executes bash commands in the background, returning a Rush::Process" do @box.connection.should_receive(:bash).with('cmd', nil, true, false).and_return(123) - @box.stub!(:processes).and_return([ mock('ps', :pid => 123) ]) + @box.stub(:processes).and_return([ double('ps', :pid => 123) ]) @box.bash('cmd', :background => true).pid.should == 123 end @@ -54,7 +54,7 @@ end it "sets the environment variables from the provided hash" do - @box.connection.stub!(:bash) + @box.connection.stub(:bash) @box.should_receive(:command_with_environment).with('cmd', { 1 => 2 }) @box.bash('cmd', :env => { 1 => 2 }) end diff --git a/spec/local_spec.rb b/spec/local_spec.rb index 7511751..b2914a7 100644 --- a/spec/local_spec.rb +++ b/spec/local_spec.rb @@ -294,13 +294,13 @@ end it "kills a process by pid sending a TERM" do - @con.stub!(:process_alive).and_return(false) + @con.stub(:process_alive).and_return(false) ::Process.should_receive(:kill).with('TERM', 123).once @con.kill_process(123) end it "kills a process by pid sending a KILL signal if TERM doesn't work" do - @con.stub!(:process_alive).and_return(true) + @con.stub(:process_alive).and_return(true) ::Process.should_receive(:kill).with('TERM', 123).at_least(:twice) ::Process.should_receive(:kill).with('KILL', 123) @con.kill_process(123) diff --git a/spec/process_set_spec.rb b/spec/process_set_spec.rb index e12a406..e46462d 100644 --- a/spec/process_set_spec.rb +++ b/spec/process_set_spec.rb @@ -37,13 +37,13 @@ end it "filters the set from a conditions hash and returns the filtered set" do - @process.stub!(:pid).and_return(123) + @process.stub(:pid).and_return(123) @set.filter(:pid => 123).first.should == @process @set.filter(:pid => 456).size.should == 0 end it "filters with regexps if provided in the conditions" do - @process.stub!(:command).and_return('foobaz') + @process.stub(:command).and_return('foobaz') @set.filter(:command => /baz/).first.should == @process @set.filter(:command => /blerg/).size.should == 0 end diff --git a/spec/remote_spec.rb b/spec/remote_spec.rb index 8e43123..3272c84 100644 --- a/spec/remote_spec.rb +++ b/spec/remote_spec.rb @@ -107,7 +107,7 @@ end it "an http result code of 400 raises the exception passed in the result body" do - @con.stub!(:parse_exception).and_return(Rush::DoesNotExist, "message") + @con.stub(:parse_exception).and_return(Rush::DoesNotExist, "message") lambda { @con.process_result("400", "") }.should raise_error(Rush::DoesNotExist) end diff --git a/spec/rush_spec.rb b/spec/rush_spec.rb index 273f26e..dfa45d5 100644 --- a/spec/rush_spec.rb +++ b/spec/rush_spec.rb @@ -10,7 +10,7 @@ end it "fetches the launch dir (aka current working directory or pwd)" do - Dir.stub!(:pwd).and_return('/tmp') + Dir.stub(:pwd).and_return('/tmp') Rush.launch_dir.should == Rush::Box.new['/tmp/'] end diff --git a/spec/ssh_tunnel_spec.rb b/spec/ssh_tunnel_spec.rb index 8dd5c38..96f2d49 100644 --- a/spec/ssh_tunnel_spec.rb +++ b/spec/ssh_tunnel_spec.rb @@ -3,8 +3,8 @@ describe Rush::SshTunnel do before do @tunnel = Rush::SshTunnel.new('spec.example.com') - @tunnel.stub!(:config).and_return(mock_config_start) - @tunnel.stub!(:display) + @tunnel.stub(:config).and_return(mock_config_start) + @tunnel.stub(:display) end after do From a67550f179ac4abd894707a9fa7ff00e9f8ced76 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 4 May 2014 22:31:20 +0200 Subject: [PATCH 039/119] Deprecated methods in rspec2: mock Replaced by double All tests run cleanly --- spec/local_spec.rb | 4 ++-- spec/process_set_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/local_spec.rb b/spec/local_spec.rb index b2914a7..e013658 100644 --- a/spec/local_spec.rb +++ b/spec/local_spec.rb @@ -73,7 +73,7 @@ end it "receive -> set_access(full_path, user, group, permissions)" do - access = mock("access") + access = double("access") Rush::Access.should_receive(:from_hash).with(:action => 'set_access', :full_path => 'full_path', :user => 'joe').and_return(access) @con.should_receive(:set_access).with('full_path', access) @@ -249,7 +249,7 @@ end it "set_access invokes the access object" do - access = mock("access") + access = double("access") access.should_receive(:apply).with('/some/path') @con.set_access('/some/path', access) end diff --git a/spec/process_set_spec.rb b/spec/process_set_spec.rb index e46462d..dea469f 100644 --- a/spec/process_set_spec.rb +++ b/spec/process_set_spec.rb @@ -2,7 +2,7 @@ describe Rush::ProcessSet do before do - @process = mock('process') + @process = double('process') @set = Rush::ProcessSet.new([ @process ]) end From ab810508c16881dc3ca47af0774291e44d6490b8 Mon Sep 17 00:00:00 2001 From: naja melan Date: Mon, 5 May 2014 12:07:43 +0200 Subject: [PATCH 040/119] Add .bundle to .gitignore As per http://bundler.io/v1.3/rationale.htm this is automated configuration files which should not be under version control. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 863b636..1cf520c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ pkg doc/ .yardoc/ tags +.bundle From 8ad9dc786662deeeeaec650861c4baf041677edd Mon Sep 17 00:00:00 2001 From: naja melan Date: Mon, 5 May 2014 12:09:14 +0200 Subject: [PATCH 041/119] Update the bundle dependencies to include rspec 2 This update was done running bundle update to update all dependencies. All tests pass. Gemfile.lock add's hard dependency versions to this gem. It was forcing rspec down to 1.2.6. This reference (http://bundler.io/v1.3/rationale.html) seems to say that you should check it into versioning, but then it would be good to explain somewhere in development docs that you need to regenerate Gemfile.lock with 'bundle update' before pushing and that you should test if things work with bundle install. I just completely bypassed bundle causing this version conflict. See also this, which seems to claim it's a bad idea to put Gemfile.lock in version control: http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ A good explanation can also be found in "man bundle install", especially the section on deployment mode. --- Gemfile.lock | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index ad64494..2276499 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,12 +2,12 @@ GEM remote: http://rubygems.org/ specs: addressable (2.3.6) - atomic (1.1.16) builder (3.2.2) coderay (1.1.0) coolline (0.4.3) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) + diff-lcs (1.2.5) faraday (0.9.0) multipart-post (>= 1.2, < 3) git (1.2.6) @@ -19,7 +19,7 @@ GEM multi_json (>= 1.7.5, < 2.0) nokogiri (~> 1.6.0) oauth2 - hashie (2.0.5) + hashie (2.1.1) highline (1.6.21) jeweler (2.0.1) builder @@ -34,7 +34,7 @@ GEM jwt (0.1.11) multi_json (>= 1.5) mini_portile (0.5.3) - multi_json (1.9.2) + multi_json (1.9.3) multi_xml (0.5.5) multipart-post (2.0.0) nokogiri (1.6.1) @@ -46,13 +46,19 @@ GEM multi_xml (~> 0.5) rack (~> 1.2) rack (1.5.2) - rake (10.2.2) + rake (10.3.1) rdoc (4.1.1) json (~> 1.4) - rspec (1.2.9) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.6) session (3.2.0) - thread_safe (0.3.1) - atomic (>= 1.1.7, < 2) + thread_safe (0.3.3) PLATFORMS ruby From c47741f079c67550b6e2abeec5764634c987fb8a Mon Sep 17 00:00:00 2001 From: naja melan Date: Mon, 5 May 2014 12:44:21 +0200 Subject: [PATCH 042/119] Try to work around travis bug travis-ci/travis-ci#2220 Not sure if it'll work but it's worth to try --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 73a900b..575d984 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ env: - CI=true rvm: - 2.0.0 - - 2.1.0 + - 2.1 - rbx-2 gemfile: - Gemfile From 6201b969fefd6a1e05c350cac636b46b6d281733 Mon Sep 17 00:00:00 2001 From: naja melan Date: Thu, 15 May 2014 01:48:41 +0200 Subject: [PATCH 043/119] Installation instruction for development REVIEW: I just wrote this as well as I could, but I'm no ruby expert. Needs verifying. --- README.rdoc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index d643988..b5fbce8 100644 --- a/README.rdoc +++ b/README.rdoc @@ -22,7 +22,14 @@ Or for the bleeding edge: If you want the development version, you will need the development headers for ruby (eg. ruby-dev in most package managers) and change that last command to: - sudo gem install --dev *.gem + git clone git://github.com/s-mage/rush.git + cd rush + bundle install # to get the dependencies + rake spec # to run the tests + rake rdoc # to generate a local copy of the docs from your sourcetree (optional) + rake gemspec + gem build *.gemspec + sudo gem install --development *.gem To uninstall From d925ef02b9c8fe8ef9f28a70fe05ca4c18470558 Mon Sep 17 00:00:00 2001 From: naja melan Date: Tue, 6 May 2014 14:59:49 +0200 Subject: [PATCH 044/119] remove a double definition of exceptions These exceptions are already defined in exceptions.rb, so I think they shouldn't be defined in entry.rb. I didn't try to manually figure out if this might break anything, but all tests pass. --- lib/rush/entry.rb | 6 ------ lib/rush/exceptions.rb | 5 +++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/rush/entry.rb b/lib/rush/entry.rb index 4916143..e6e6cc0 100644 --- a/lib/rush/entry.rb +++ b/lib/rush/entry.rb @@ -77,12 +77,6 @@ def last_accessed stat[:atime] end - # Attempts to rename, copy, or otherwise place an entry into a dir that already contains an entry by that name will fail with this exception. - class NameAlreadyExists < Exception; end - - # Do not use rename or duplicate with a slash; use copy_to or move_to instead. - class NameCannotContainSlash < Exception; end - # Rename an entry to another name within the same dir. The object's name # will be updated to match the change on the filesystem. def rename(new_name) diff --git a/lib/rush/exceptions.rb b/lib/rush/exceptions.rb index f0ed3cc..71cd4f3 100644 --- a/lib/rush/exceptions.rb +++ b/lib/rush/exceptions.rb @@ -17,10 +17,11 @@ class DoesNotExist < Exception; end # The bash command had a non-zero return value. Message is stderr. class BashFailed < Exception; end - # There's already an entry by the given name in the given dir. + # Attempts to rename, copy, or otherwise place an entry into a dir that + # already contains an entry by that name will fail with this exception. class NameAlreadyExists < Exception; end - # The name cannot contain a slash; use two operations, rename and then move, instead. + # Do not use rename or duplicate with a slash; use copy_to or move_to instead. class NameCannotContainSlash < Exception; end # You cannot move or copy entries to a path that is not a dir (should end with trailing slash). From 60e6d2f6b555e55ce039d550be93bf922adc590c Mon Sep 17 00:00:00 2001 From: naja melan Date: Tue, 6 May 2014 15:08:08 +0200 Subject: [PATCH 045/119] Do not unshift ourselves on the $LOADPATH since we now use require_relative no need to touch it and bad practice anyways --- lib/rush.rb | 2 -- spec/base.rb | 1 - 2 files changed, 3 deletions(-) diff --git a/lib/rush.rb b/lib/rush.rb index 864a89b..78476a6 100644 --- a/lib/rush.rb +++ b/lib/rush.rb @@ -61,8 +61,6 @@ def self.quote(path) module Rush::Connection; end -$LOAD_PATH.unshift(File.dirname(__FILE__)) - require_relative 'rush/exceptions' require_relative 'rush/config' require_relative 'rush/commands' diff --git a/spec/base.rb b/spec/base.rb index 5a804d2..386bc99 100644 --- a/spec/base.rb +++ b/spec/base.rb @@ -1,6 +1,5 @@ require 'rspec' -$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') require_relative '../lib/rush' def mock_config(&block) From 1e8c6acdda4b0711866628dbe550c2d73ab00617 Mon Sep 17 00:00:00 2001 From: naja melan Date: Mon, 19 May 2014 03:32:11 +0200 Subject: [PATCH 046/119] Add vendor/bundle to gitignore Bundler installs dependencies to this subfolder when calling "bundle install". They should not be tracked by git. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1cf520c..c3de711 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ doc/ .yardoc/ tags .bundle +vendor/bundle/ From fb382eb73417738906b4b9f3013966f0038e8d9d Mon Sep 17 00:00:00 2001 From: naja melan Date: Mon, 19 May 2014 03:33:56 +0200 Subject: [PATCH 047/119] Simplify dev install instructions by using rake install. I'm still not sure about the many different ways of doing stuff in ruby gems. Note that sudo gem install had --development option. I'm not sure what rake install exactly does, but it is briefer. --- README.rdoc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.rdoc b/README.rdoc index b5fbce8..29cb477 100644 --- a/README.rdoc +++ b/README.rdoc @@ -24,12 +24,11 @@ If you want the development version, you will need the development headers for r git clone git://github.com/s-mage/rush.git cd rush - bundle install # to get the dependencies - rake spec # to run the tests - rake rdoc # to generate a local copy of the docs from your sourcetree (optional) - rake gemspec - gem build *.gemspec - sudo gem install --development *.gem + bundle install # to get the dependencies + rake spec # to run the tests + rake rdoc # to generate a local copy of the docs from your sourcetree (optional) + + sudo rake install # install your default gem location To uninstall From 05a92aebb4f97145cf765c7a1199f3b5986ede2c Mon Sep 17 00:00:00 2001 From: naja melan Date: Fri, 23 May 2014 01:40:28 +0200 Subject: [PATCH 048/119] Create Entry::chown and Entry::chown_R Rush didn't have a method for changing ownership of files and directories. This adds Entry::chown which takes 3 parameters: user, group and options It is a simple frontend for FileUtils.chown and chown_R, to integrate their functionality with the Rush syntax. Currently not very tested. The problem is that tests for this will have to run as sudo usually, because unprivileged you can't do much with chmod. Also, I currently didn't have a remote setup which would be practical for testing, so remote testing has not been done at all. Towards the future I would like to add more sys admin functionality to Rush, so it's not only an interface to filesystem and bash, but also to user management. Obviously a growing number of features would require root access to be used, and thus also to be tested. I will think about a way to create an optional set of tests that will be run only if the specs are being run as root. That way, we can have our tests, but peopl who can't be bothered to run it as root can still run all the other tests. --- lib/rush/entry.rb | 29 +++++++++++++++++++++++++++++ lib/rush/local.rb | 19 +++++++++++++++++++ lib/rush/remote.rb | 4 ++++ spec/local_spec.rb | 8 ++++++++ 4 files changed, 60 insertions(+) diff --git a/lib/rush/entry.rb b/lib/rush/entry.rb index e6e6cc0..4819556 100644 --- a/lib/rush/entry.rb +++ b/lib/rush/entry.rb @@ -163,6 +163,35 @@ def access Rush::Access.new.from_octal(stat[:mode]).display_hash end + + # Change entry ownership + # + # Changes owner and group on the named files (in list) to the user user and the group group. user and group may be an ID (Integer/String) or a name (String). If user or group is nil, this method does not change the attribute. + # + # @param user [string/integer] The user to own the file + # @param group [string/integer] The group to own the file + # @param options [hash] the options to pass to FileUtils.chown (eg. 'noop', 'verbose' or 'recursive' ) + # + def chown( user = nil, group = nil, options = {} ) + + connection.chown( full_path, user, group, options ) + self + + end + + + # Shortcut to Entry::chown to pass the 'recursive' option by default + # + def chown_R( user = nil, group = nil, options = {} ) + + options[ :recursive ] = true + chown( user, group, options ) + + end + + + + # Destroy the entry. If it is a dir, everything inside it will also be destroyed. def destroy connection.destroy(full_path) diff --git a/lib/rush/local.rb b/lib/rush/local.rb index d72afa0..d902759 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -128,6 +128,24 @@ def set_access(full_path, access) access.apply(full_path) end + + # A frontend for FileUtils::chown + # + def chown( full_path, user=nil, group=nil, options={} ) + + if options[ :recursive ] + + FileUtils.chown_R( user, group, full_path, options ) + + else + + FileUtils.chown( user, group, full_path, options ) + + end + + end + + # Fetch the size of a dir, since a standard file stat does not include the # size of the contents. def size(full_path) @@ -345,6 +363,7 @@ def receive(params) when 'index' then index(params[:base_path], params[:glob]).join("\n") + "\n" when 'stat' then YAML.dump(stat(params[:full_path])) when 'set_access' then set_access(params[:full_path], Rush::Access.from_hash(params)) + when 'chown' then chown( params[:full_path], params[:user], params[:group], params[:options]) when 'size' then size(params[:full_path]) when 'processes' then YAML.dump(processes) when 'process_alive' then process_alive(params[:pid]) ? '1' : '0' diff --git a/lib/rush/remote.rb b/lib/rush/remote.rb index 83a2a31..2dd2e94 100644 --- a/lib/rush/remote.rb +++ b/lib/rush/remote.rb @@ -66,6 +66,10 @@ def set_access(full_path, access) transmit access.to_hash.merge(:action => 'set_access', :full_path => full_path) end + def chown( full_path, user = nil, group = nil, options = {} ) + transmit( :action => 'chown', :full_path => full_path, :user => user, :group => group, :options => options ) + end + def size(full_path) transmit(:action => 'size', :full_path => full_path).to_i end diff --git a/spec/local_spec.rb b/spec/local_spec.rb index e013658..2b499d0 100644 --- a/spec/local_spec.rb +++ b/spec/local_spec.rb @@ -85,6 +85,12 @@ @con.receive(:action => 'size', :full_path => 'full_path').should == "1024" end + it "receive -> chown( full_path, user, group, options )" do + options = { noop: true } + @con.should_receive(:chown).with('full_path', 'username', 'groupname', options ) + @con.receive(:action => 'chown', :full_path => 'full_path', user: 'username', group: 'groupname', options: options) + end + it "receive -> processes" do @con.should_receive(:processes).with().and_return([ { :pid => 1 } ]) @con.receive(:action => 'processes').should == YAML.dump([ { :pid => 1 } ]) @@ -254,6 +260,8 @@ @con.set_access('/some/path', access) end + it "chown should change the ownership of a file" + if !RUBY_PLATFORM.match(/darwin/) # doesn't work on OS X 'cause du switches are different it "size gives size of a directory and all its contents recursively" do system "mkdir -p #{@sandbox_dir}/a/b/; echo 1234 > #{@sandbox_dir}/a/b/c" From 43738b5990168ae84595b3938e46ed96efaeba7f Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 25 May 2014 15:10:31 +0400 Subject: [PATCH 049/119] Refactor pull request #7 --- lib/rush/entry.rb | 19 +- lib/rush/local.rb | 16 +- lib/rush/remote.rb | 286 +++++++++--------- spec/local_spec.rb | 714 ++++++++++++++++++++++----------------------- 4 files changed, 509 insertions(+), 526 deletions(-) diff --git a/lib/rush/entry.rb b/lib/rush/entry.rb index 4819556..7f74f29 100644 --- a/lib/rush/entry.rb +++ b/lib/rush/entry.rb @@ -163,7 +163,6 @@ def access Rush::Access.new.from_octal(stat[:mode]).display_hash end - # Change entry ownership # # Changes owner and group on the named files (in list) to the user user and the group group. user and group may be an ID (Integer/String) or a name (String). If user or group is nil, this method does not change the attribute. @@ -172,26 +171,18 @@ def access # @param group [string/integer] The group to own the file # @param options [hash] the options to pass to FileUtils.chown (eg. 'noop', 'verbose' or 'recursive' ) # - def chown( user = nil, group = nil, options = {} ) - - connection.chown( full_path, user, group, options ) + def chown(user = nil, group = nil, options = {}) + connection.chown(full_path, user, group, options) self - end - # Shortcut to Entry::chown to pass the 'recursive' option by default # - def chown_R( user = nil, group = nil, options = {} ) - - options[ :recursive ] = true - chown( user, group, options ) - + def chown_R(user = nil, group = nil, options = {}) + options[:recursive] = true + chown(user, group, options) end - - - # Destroy the entry. If it is a dir, everything inside it will also be destroyed. def destroy connection.destroy(full_path) diff --git a/lib/rush/local.rb b/lib/rush/local.rb index d902759..8a6f321 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -128,24 +128,16 @@ def set_access(full_path, access) access.apply(full_path) end - # A frontend for FileUtils::chown # def chown( full_path, user=nil, group=nil, options={} ) - - if options[ :recursive ] - - FileUtils.chown_R( user, group, full_path, options ) - + if options[:recursive] + FileUtils.chown_R(user, group, full_path, options) else - - FileUtils.chown( user, group, full_path, options ) - + FileUtils.chown(user, group, full_path, options) end - end - # Fetch the size of a dir, since a standard file stat does not include the # size of the contents. def size(full_path) @@ -363,7 +355,7 @@ def receive(params) when 'index' then index(params[:base_path], params[:glob]).join("\n") + "\n" when 'stat' then YAML.dump(stat(params[:full_path])) when 'set_access' then set_access(params[:full_path], Rush::Access.from_hash(params)) - when 'chown' then chown( params[:full_path], params[:user], params[:group], params[:options]) + when 'chown' then chown(params[:full_path], params[:user], params[:group], params[:options]) when 'size' then size(params[:full_path]) when 'processes' then YAML.dump(processes) when 'process_alive' then process_alive(params[:pid]) ? '1' : '0' diff --git a/lib/rush/remote.rb b/lib/rush/remote.rb index 2dd2e94..90ea3d6 100644 --- a/lib/rush/remote.rb +++ b/lib/rush/remote.rb @@ -7,154 +7,154 @@ # This is an internal class that does not need to be accessed in normal use of # the rush shell or library. class Rush::Connection::Remote - attr_reader :host, :tunnel + attr_reader :host, :tunnel - def initialize(host) - @host = host - @tunnel = Rush::SshTunnel.new(host) - end + def initialize(host) + @host = host + @tunnel = Rush::SshTunnel.new(host) + end - def write_file(full_path, contents) - transmit(:action => 'write_file', :full_path => full_path, :payload => contents) - end + def write_file(full_path, contents) + transmit(:action => 'write_file', :full_path => full_path, :payload => contents) + end - def append_to_file(full_path, contents) - transmit(:action => 'append_to_file', :full_path => full_path, :payload => contents) - end + def append_to_file(full_path, contents) + transmit(:action => 'append_to_file', :full_path => full_path, :payload => contents) + end - def file_contents(full_path) - transmit(:action => 'file_contents', :full_path => full_path) - end + def file_contents(full_path) + transmit(:action => 'file_contents', :full_path => full_path) + end - def destroy(full_path) - transmit(:action => 'destroy', :full_path => full_path) - end + def destroy(full_path) + transmit(:action => 'destroy', :full_path => full_path) + end - def purge(full_path) - transmit(:action => 'purge', :full_path => full_path) - end + def purge(full_path) + transmit(:action => 'purge', :full_path => full_path) + end - def create_dir(full_path) - transmit(:action => 'create_dir', :full_path => full_path) - end - - def rename(path, name, new_name) - transmit(:action => 'rename', :path => path, :name => name, :new_name => 'new_name') - end - - def copy(src, dst) - transmit(:action => 'copy', :src => src, :dst => dst) - end - - def read_archive(full_path) - transmit(:action => 'read_archive', :full_path => full_path) - end - - def write_archive(archive, dir) - transmit(:action => 'write_archive', :dir => dir, :payload => archive) - end - - def index(base_path, glob) - transmit(:action => 'index', :base_path => base_path, :glob => glob).split("\n") - end - - def stat(full_path) - YAML.load(transmit(:action => 'stat', :full_path => full_path)) - end - - def set_access(full_path, access) - transmit access.to_hash.merge(:action => 'set_access', :full_path => full_path) - end - - def chown( full_path, user = nil, group = nil, options = {} ) - transmit( :action => 'chown', :full_path => full_path, :user => user, :group => group, :options => options ) - end - - def size(full_path) - transmit(:action => 'size', :full_path => full_path).to_i - end - - def processes - YAML.load(transmit(:action => 'processes')) - end - - def process_alive(pid) - transmit(:action => 'process_alive', :pid => pid) - end - - def kill_process(pid, options={}) - transmit(:action => 'kill_process', :pid => pid, :payload => YAML.dump(options)) - end - - def bash(command, user, background, reset_environment) - transmit(:action => 'bash', :payload => command, :user => user, :background => background, :reset_environment => reset_environment) - end - - # Given a hash of parameters (converted by the method call on the connection - # object), send it across the wire to the RushServer listening on the other - # side. Uses http basic auth, with credentials fetched from the Rush::Config. - def transmit(params) - ensure_tunnel - - require 'net/http' - - payload = params.delete(:payload) - - uri = "/?" - params.each do |key, value| - uri += "#{key}=#{value}&" - end - - req = Net::HTTP::Post.new(uri) - req.basic_auth config.credentials_user, config.credentials_password - - Net::HTTP.start(tunnel.host, tunnel.port) do |http| - res = http.request(req, payload) - process_result(res.code, res.body) - end - rescue EOFError - raise Rush::RushdNotRunning - end - - # Take the http result of a transmit and raise an error, or return the body - # of the result when valid. - def process_result(code, body) - raise Rush::NotAuthorized if code == "401" - - if code == "400" - klass, message = parse_exception(body) - raise klass, "#{host}:#{message}" - end - - raise Rush::FailedTransmit if code != "200" - - body - end - - # Parse an exception returned from the server, with the class name on the - # first line and the message on the second. - def parse_exception(body) - klass, message = body.split("\n", 2) - raise "invalid exception class: #{klass}" unless klass.match(/^Rush::[A-Za-z]+$/) - klass = Object.module_eval(klass) - [ klass, message.strip ] - end - - # Set up the tunnel if it is not already running. - def ensure_tunnel(options={}) - tunnel.ensure_tunnel(options) - end - - # Remote connections are alive when the box on the other end is responding - # to commands. - def alive? - index('/', 'alive_check') - true - rescue - false - end - - def config - @config ||= Rush::Config.new - end + def create_dir(full_path) + transmit(:action => 'create_dir', :full_path => full_path) + end + + def rename(path, name, new_name) + transmit(:action => 'rename', :path => path, :name => name, :new_name => 'new_name') + end + + def copy(src, dst) + transmit(:action => 'copy', :src => src, :dst => dst) + end + + def read_archive(full_path) + transmit(:action => 'read_archive', :full_path => full_path) + end + + def write_archive(archive, dir) + transmit(:action => 'write_archive', :dir => dir, :payload => archive) + end + + def index(base_path, glob) + transmit(:action => 'index', :base_path => base_path, :glob => glob).split("\n") + end + + def stat(full_path) + YAML.load(transmit(:action => 'stat', :full_path => full_path)) + end + + def set_access(full_path, access) + transmit access.to_hash.merge(:action => 'set_access', :full_path => full_path) + end + + def chown( full_path, user = nil, group = nil, options = {} ) + transmit(:action => 'chown', :full_path => full_path, :user => user, :group => group, :options => options) + end + + def size(full_path) + transmit(:action => 'size', :full_path => full_path).to_i + end + + def processes + YAML.load(transmit(:action => 'processes')) + end + + def process_alive(pid) + transmit(:action => 'process_alive', :pid => pid) + end + + def kill_process(pid, options={}) + transmit(:action => 'kill_process', :pid => pid, :payload => YAML.dump(options)) + end + + def bash(command, user, background, reset_environment) + transmit(:action => 'bash', :payload => command, :user => user, :background => background, :reset_environment => reset_environment) + end + + # Given a hash of parameters (converted by the method call on the connection + # object), send it across the wire to the RushServer listening on the other + # side. Uses http basic auth, with credentials fetched from the Rush::Config. + def transmit(params) + ensure_tunnel + + require 'net/http' + + payload = params.delete(:payload) + + uri = "/?" + params.each do |key, value| + uri += "#{key}=#{value}&" + end + + req = Net::HTTP::Post.new(uri) + req.basic_auth config.credentials_user, config.credentials_password + + Net::HTTP.start(tunnel.host, tunnel.port) do |http| + res = http.request(req, payload) + process_result(res.code, res.body) + end + rescue EOFError + raise Rush::RushdNotRunning + end + + # Take the http result of a transmit and raise an error, or return the body + # of the result when valid. + def process_result(code, body) + raise Rush::NotAuthorized if code == "401" + + if code == "400" + klass, message = parse_exception(body) + raise klass, "#{host}:#{message}" + end + + raise Rush::FailedTransmit if code != "200" + + body + end + + # Parse an exception returned from the server, with the class name on the + # first line and the message on the second. + def parse_exception(body) + klass, message = body.split("\n", 2) + raise "invalid exception class: #{klass}" unless klass.match(/^Rush::[A-Za-z]+$/) + klass = Object.module_eval(klass) + [ klass, message.strip ] + end + + # Set up the tunnel if it is not already running. + def ensure_tunnel(options={}) + tunnel.ensure_tunnel(options) + end + + # Remote connections are alive when the box on the other end is responding + # to commands. + def alive? + index('/', 'alive_check') + true + rescue + false + end + + def config + @config ||= Rush::Config.new + end end diff --git a/spec/local_spec.rb b/spec/local_spec.rb index 2b499d0..6525ffb 100644 --- a/spec/local_spec.rb +++ b/spec/local_spec.rb @@ -1,365 +1,365 @@ require_relative 'base' describe Rush::Connection::Local do - before do - @sandbox_dir = "/tmp/rush_spec.#{Process.pid}" - system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}" - - @con = Rush::Connection::Local.new - end - - after do - system "rm -rf #{@sandbox_dir}" - end - - it "receive -> write_file(file, contents)" do - @con.should_receive(:write_file).with('file', 'contents') - @con.receive(:action => 'write_file', :full_path => 'file', :payload => 'contents') - end - - it "receive -> append_to_file(file, contents)" do - @con.should_receive(:append_to_file).with('file', 'contents') - @con.receive(:action => 'append_to_file', :full_path => 'file', :payload => 'contents') - end - - it "receive -> file_contents(file)" do - @con.should_receive(:file_contents).with('file').and_return('the contents') - @con.receive(:action => 'file_contents', :full_path => 'file').should == 'the contents' - end - - it "receive -> destroy(file or dir)" do - @con.should_receive(:destroy).with('file') - @con.receive(:action => 'destroy', :full_path => 'file') - end - - it "receive -> purge(dir)" do - @con.should_receive(:purge).with('dir') - @con.receive(:action => 'purge', :full_path => 'dir') - end - - it "receive -> create_dir(path)" do - @con.should_receive(:create_dir).with('dir') - @con.receive(:action => 'create_dir', :full_path => 'dir') - end - - it "receive -> rename(path, name, new_name)" do - @con.should_receive(:rename).with('path', 'name', 'new_name') - @con.receive(:action => 'rename', :path => 'path', :name => 'name', :new_name => 'new_name') - end - - it "receive -> copy(src, dst)" do - @con.should_receive(:copy).with('src', 'dst') - @con.receive(:action => 'copy', :src => 'src', :dst => 'dst') - end - - it "receive -> read_archive(full_path)" do - @con.should_receive(:read_archive).with('full_path').and_return('archive data') - @con.receive(:action => 'read_archive', :full_path => 'full_path').should == 'archive data' - end - - it "receive -> write_archive(archive, dir)" do - @con.should_receive(:write_archive).with('archive', 'dir') - @con.receive(:action => 'write_archive', :dir => 'dir', :payload => 'archive') - end - - it "receive -> index(base_path, glob)" do - @con.should_receive(:index).with('base_path', '*').and_return(%w(1 2)) - @con.receive(:action => 'index', :base_path => 'base_path', :glob => '*').should == "1\n2\n" - end - - it "receive -> stat(full_path)" do - @con.should_receive(:stat).with('full_path').and_return(1 => 2) - @con.receive(:action => 'stat', :full_path => 'full_path').should == YAML.dump(1 => 2) - end - - it "receive -> set_access(full_path, user, group, permissions)" do - access = double("access") - Rush::Access.should_receive(:from_hash).with(:action => 'set_access', :full_path => 'full_path', :user => 'joe').and_return(access) - - @con.should_receive(:set_access).with('full_path', access) - @con.receive(:action => 'set_access', :full_path => 'full_path', :user => 'joe') - end - - it "receive -> size(full_path)" do - @con.should_receive(:size).with('full_path').and_return("1024") - @con.receive(:action => 'size', :full_path => 'full_path').should == "1024" - end - - it "receive -> chown( full_path, user, group, options )" do - options = { noop: true } - @con.should_receive(:chown).with('full_path', 'username', 'groupname', options ) - @con.receive(:action => 'chown', :full_path => 'full_path', user: 'username', group: 'groupname', options: options) - end - - it "receive -> processes" do - @con.should_receive(:processes).with().and_return([ { :pid => 1 } ]) - @con.receive(:action => 'processes').should == YAML.dump([ { :pid => 1 } ]) - end - - it "receive -> process_alive" do - @con.should_receive(:process_alive).with(123).and_return(true) - @con.receive(:action => 'process_alive', :pid => 123).should == '1' - end - - it "receive -> kill_process" do - @con.should_receive(:kill_process).with(123, :wait => 10).and_return(true) - @con.receive(:action => 'kill_process', :pid => '123', :payload => YAML.dump(:wait => 10)) - end - - it "receive -> bash (reset environment)" do - @con.should_receive(:bash).with('cmd', 'user', false, true).and_return('output') - @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'false', :reset_environment => 'true').should == 'output' - end - - it "receive -> bash (foreground)" do - @con.should_receive(:bash).with('cmd', 'user', false, false).and_return('output') - @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'false').should == 'output' - end - - it "receive -> bash (background)" do - @con.should_receive(:bash).with('cmd', 'user', true, false).and_return('output') - @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'true').should == 'output' - end - - it "receive -> unknown action exception" do - lambda { @con.receive(:action => 'does_not_exist') }.should raise_error(Rush::Connection::Local::UnknownAction) - end - - it "write_file writes contents to a file" do - fname = "#{@sandbox_dir}/a_file" - data = "some data" - @con.write_file(fname, data) - File.read(fname).should == data - end - - it "append_to_file appends contents to a file" do - fname = "#{@sandbox_dir}/a_file" - system "echo line1 > #{fname}" - @con.append_to_file(fname, 'line2') - File.read(fname).should == "line1\nline2" - end - - it "file_contents reads a file's contents" do - fname = "#{@sandbox_dir}/a_file" - system "echo stuff > #{fname}" - @con.file_contents(fname).should == "stuff\n" - end - - it "file_contents raises DoesNotExist if the file does not exist" do - fname = "#{@sandbox_dir}/does_not_exist" - lambda { @con.file_contents(fname) }.should raise_error(Rush::DoesNotExist, fname) - end - - it "destroy to destroy a file or dir" do - fname = "#{@sandbox_dir}/delete_me" - system "touch #{fname}" - @con.destroy(fname) - File.exists?(fname).should be_false - end - - it "purge to purge a dir" do - system "cd #{@sandbox_dir}; touch {1,2}; mkdir 3; touch 3/4" - @con.purge(@sandbox_dir) - File.exists?(@sandbox_dir).should be_true - Dir.glob("#{@sandbox_dir}/*").should == [] - end - - it "purge kills hidden (dotfile) entries too" do - system "cd #{@sandbox_dir}; touch .killme" - @con.purge(@sandbox_dir) - File.exists?(@sandbox_dir).should be_true - `cd #{@sandbox_dir}; ls -lA | grep -v total | wc -l`.to_i.should == 0 - end - - it "create_dir creates a directory" do - fname = "#{@sandbox_dir}/a/b/c/" - @con.create_dir(fname) - File.directory?(fname).should be_true - end - - it "rename to rename entries within a dir" do - system "touch #{@sandbox_dir}/a" - @con.rename(@sandbox_dir, 'a', 'b') - File.exists?("#{@sandbox_dir}/a").should be_false - File.exists?("#{@sandbox_dir}/b").should be_true - end - - it "copy to copy an entry to another dir on the same box" do - system "mkdir #{@sandbox_dir}/subdir" - system "touch #{@sandbox_dir}/a" - @con.copy("#{@sandbox_dir}/a", "#{@sandbox_dir}/subdir") - File.exists?("#{@sandbox_dir}/a").should be_true - File.exists?("#{@sandbox_dir}/subdir/a").should be_true - end - - it "copy raises DoesNotExist with source path if it doesn't exist or otherwise can't be accessed" do - lambda { @con.copy('/does/not/exist', '/tmp') }.should raise_error(Rush::DoesNotExist, '/does/not/exist') - end - - it "copy raises DoesNotExist with destination path if it can't access the destination" do - lambda { @con.copy('/tmp', '/does/not/exist') }.should raise_error(Rush::DoesNotExist, '/does/not') - end - - it "read_archive to pull an archive of a dir into a byte stream" do - system "touch #{@sandbox_dir}/a" - @con.read_archive(@sandbox_dir).size.should > 50 - end - - it "read_archive works for paths with spaces" do - system "mkdir -p #{@sandbox_dir}/with\\ space; touch #{@sandbox_dir}/with\\ space/a" - @con.read_archive("#{@sandbox_dir}/with space").size.should > 50 - end - - it "write_archive to turn a byte stream into a dir" do - system "cd #{@sandbox_dir}; mkdir -p a; touch a/b; tar cf xfer.tar a; mkdir dst" - archive = File.read("#{@sandbox_dir}/xfer.tar") - @con.write_archive(archive, "#{@sandbox_dir}/dst") - File.directory?("#{@sandbox_dir}/dst/a").should be_true - File.exists?("#{@sandbox_dir}/dst/a/b").should be_true - end - - it "write_archive works for paths with spaces" do - system "cd #{@sandbox_dir}; mkdir -p a; touch a/b; tar cf xfer.tar a; mkdir with\\ space" - archive = File.read("#{@sandbox_dir}/xfer.tar") - @con.write_archive(archive, "#{@sandbox_dir}/with space") - File.directory?("#{@sandbox_dir}/with space/a").should be_true - File.exists?("#{@sandbox_dir}/with space/a/b").should be_true - end - - it "index fetches list of all files and dirs in a dir when pattern is empty" do - system "cd #{@sandbox_dir}; mkdir dir; touch file" - @con.index(@sandbox_dir, '').should == [ 'dir/', 'file' ] - end - - it "index fetches only files with a certain extension with a flat pattern, *.rb" do - system "cd #{@sandbox_dir}; touch a.rb; touch b.txt" - @con.index(@sandbox_dir, '*.rb').should == [ 'a.rb' ] - end - - it "index raises DoesNotExist when the base path is invalid" do - lambda { @con.index('/does/not/exist', '*') }.should raise_error(Rush::DoesNotExist, '/does/not/exist') - end - - it "stat gives file stats like size and timestamps" do - @con.stat(@sandbox_dir).should have_key(:ctime) - @con.stat(@sandbox_dir).should have_key(:size) - end - - it "stat fetches the octal permissions" do - @con.stat(@sandbox_dir)[:mode].should be_kind_of(Fixnum) - end - - it "stat raises DoesNotExist if the entry does not exist" do - fname = "#{@sandbox_dir}/does_not_exist" - lambda { @con.stat(fname) }.should raise_error(Rush::DoesNotExist, fname) - end - - it "set_access invokes the access object" do - access = double("access") - access.should_receive(:apply).with('/some/path') - @con.set_access('/some/path', access) - end - - it "chown should change the ownership of a file" - - if !RUBY_PLATFORM.match(/darwin/) # doesn't work on OS X 'cause du switches are different - it "size gives size of a directory and all its contents recursively" do - system "mkdir -p #{@sandbox_dir}/a/b/; echo 1234 > #{@sandbox_dir}/a/b/c" - @con.size(@sandbox_dir).should == (4096*3 + 5) - end - end - - it "parses ps output on os x" do - @con.parse_ps("21712 501 21711 1236 0 /usr/bin/vi somefile.rb").should == { - :pid => "21712", - :uid => "501", - :parent_pid => 21711, - :mem => 1236, - :cpu => 0, - :command => '/usr/bin/vi', - :cmdline => '/usr/bin/vi somefile.rb', - } - end - - it "gets the list of processes on os x via the ps command" do - @con.should_receive(:os_x_raw_ps).and_return < write_file(file, contents)" do + @con.should_receive(:write_file).with('file', 'contents') + @con.receive(:action => 'write_file', :full_path => 'file', :payload => 'contents') + end + + it "receive -> append_to_file(file, contents)" do + @con.should_receive(:append_to_file).with('file', 'contents') + @con.receive(:action => 'append_to_file', :full_path => 'file', :payload => 'contents') + end + + it "receive -> file_contents(file)" do + @con.should_receive(:file_contents).with('file').and_return('the contents') + @con.receive(:action => 'file_contents', :full_path => 'file').should == 'the contents' + end + + it "receive -> destroy(file or dir)" do + @con.should_receive(:destroy).with('file') + @con.receive(:action => 'destroy', :full_path => 'file') + end + + it "receive -> purge(dir)" do + @con.should_receive(:purge).with('dir') + @con.receive(:action => 'purge', :full_path => 'dir') + end + + it "receive -> create_dir(path)" do + @con.should_receive(:create_dir).with('dir') + @con.receive(:action => 'create_dir', :full_path => 'dir') + end + + it "receive -> rename(path, name, new_name)" do + @con.should_receive(:rename).with('path', 'name', 'new_name') + @con.receive(:action => 'rename', :path => 'path', :name => 'name', :new_name => 'new_name') + end + + it "receive -> copy(src, dst)" do + @con.should_receive(:copy).with('src', 'dst') + @con.receive(:action => 'copy', :src => 'src', :dst => 'dst') + end + + it "receive -> read_archive(full_path)" do + @con.should_receive(:read_archive).with('full_path').and_return('archive data') + @con.receive(:action => 'read_archive', :full_path => 'full_path').should == 'archive data' + end + + it "receive -> write_archive(archive, dir)" do + @con.should_receive(:write_archive).with('archive', 'dir') + @con.receive(:action => 'write_archive', :dir => 'dir', :payload => 'archive') + end + + it "receive -> index(base_path, glob)" do + @con.should_receive(:index).with('base_path', '*').and_return(%w(1 2)) + @con.receive(:action => 'index', :base_path => 'base_path', :glob => '*').should == "1\n2\n" + end + + it "receive -> stat(full_path)" do + @con.should_receive(:stat).with('full_path').and_return(1 => 2) + @con.receive(:action => 'stat', :full_path => 'full_path').should == YAML.dump(1 => 2) + end + + it "receive -> set_access(full_path, user, group, permissions)" do + access = double("access") + Rush::Access.should_receive(:from_hash).with(:action => 'set_access', :full_path => 'full_path', :user => 'joe').and_return(access) + + @con.should_receive(:set_access).with('full_path', access) + @con.receive(:action => 'set_access', :full_path => 'full_path', :user => 'joe') + end + + it "receive -> size(full_path)" do + @con.should_receive(:size).with('full_path').and_return("1024") + @con.receive(:action => 'size', :full_path => 'full_path').should == "1024" + end + + it "receive -> chown(full_path, user, group, options)" do + options = { noop: true } + @con.should_receive(:chown).with('full_path', 'username', 'groupname', options ) + @con.receive(:action => 'chown', :full_path => 'full_path', user: 'username', group: 'groupname', options: options) + end + + it "receive -> processes" do + @con.should_receive(:processes).with().and_return([ { :pid => 1 } ]) + @con.receive(:action => 'processes').should == YAML.dump([ { :pid => 1 } ]) + end + + it "receive -> process_alive" do + @con.should_receive(:process_alive).with(123).and_return(true) + @con.receive(:action => 'process_alive', :pid => 123).should == '1' + end + + it "receive -> kill_process" do + @con.should_receive(:kill_process).with(123, :wait => 10).and_return(true) + @con.receive(:action => 'kill_process', :pid => '123', :payload => YAML.dump(:wait => 10)) + end + + it "receive -> bash (reset environment)" do + @con.should_receive(:bash).with('cmd', 'user', false, true).and_return('output') + @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'false', :reset_environment => 'true').should == 'output' + end + + it "receive -> bash (foreground)" do + @con.should_receive(:bash).with('cmd', 'user', false, false).and_return('output') + @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'false').should == 'output' + end + + it "receive -> bash (background)" do + @con.should_receive(:bash).with('cmd', 'user', true, false).and_return('output') + @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'true').should == 'output' + end + + it "receive -> unknown action exception" do + lambda { @con.receive(:action => 'does_not_exist') }.should raise_error(Rush::Connection::Local::UnknownAction) + end + + it "write_file writes contents to a file" do + fname = "#{@sandbox_dir}/a_file" + data = "some data" + @con.write_file(fname, data) + File.read(fname).should == data + end + + it "append_to_file appends contents to a file" do + fname = "#{@sandbox_dir}/a_file" + system "echo line1 > #{fname}" + @con.append_to_file(fname, 'line2') + File.read(fname).should == "line1\nline2" + end + + it "file_contents reads a file's contents" do + fname = "#{@sandbox_dir}/a_file" + system "echo stuff > #{fname}" + @con.file_contents(fname).should == "stuff\n" + end + + it "file_contents raises DoesNotExist if the file does not exist" do + fname = "#{@sandbox_dir}/does_not_exist" + lambda { @con.file_contents(fname) }.should raise_error(Rush::DoesNotExist, fname) + end + + it "destroy to destroy a file or dir" do + fname = "#{@sandbox_dir}/delete_me" + system "touch #{fname}" + @con.destroy(fname) + File.exists?(fname).should be_false + end + + it "purge to purge a dir" do + system "cd #{@sandbox_dir}; touch {1,2}; mkdir 3; touch 3/4" + @con.purge(@sandbox_dir) + File.exists?(@sandbox_dir).should be_true + Dir.glob("#{@sandbox_dir}/*").should == [] + end + + it "purge kills hidden (dotfile) entries too" do + system "cd #{@sandbox_dir}; touch .killme" + @con.purge(@sandbox_dir) + File.exists?(@sandbox_dir).should be_true + `cd #{@sandbox_dir}; ls -lA | grep -v total | wc -l`.to_i.should == 0 + end + + it "create_dir creates a directory" do + fname = "#{@sandbox_dir}/a/b/c/" + @con.create_dir(fname) + File.directory?(fname).should be_true + end + + it "rename to rename entries within a dir" do + system "touch #{@sandbox_dir}/a" + @con.rename(@sandbox_dir, 'a', 'b') + File.exists?("#{@sandbox_dir}/a").should be_false + File.exists?("#{@sandbox_dir}/b").should be_true + end + + it "copy to copy an entry to another dir on the same box" do + system "mkdir #{@sandbox_dir}/subdir" + system "touch #{@sandbox_dir}/a" + @con.copy("#{@sandbox_dir}/a", "#{@sandbox_dir}/subdir") + File.exists?("#{@sandbox_dir}/a").should be_true + File.exists?("#{@sandbox_dir}/subdir/a").should be_true + end + + it "copy raises DoesNotExist with source path if it doesn't exist or otherwise can't be accessed" do + lambda { @con.copy('/does/not/exist', '/tmp') }.should raise_error(Rush::DoesNotExist, '/does/not/exist') + end + + it "copy raises DoesNotExist with destination path if it can't access the destination" do + lambda { @con.copy('/tmp', '/does/not/exist') }.should raise_error(Rush::DoesNotExist, '/does/not') + end + + it "read_archive to pull an archive of a dir into a byte stream" do + system "touch #{@sandbox_dir}/a" + @con.read_archive(@sandbox_dir).size.should > 50 + end + + it "read_archive works for paths with spaces" do + system "mkdir -p #{@sandbox_dir}/with\\ space; touch #{@sandbox_dir}/with\\ space/a" + @con.read_archive("#{@sandbox_dir}/with space").size.should > 50 + end + + it "write_archive to turn a byte stream into a dir" do + system "cd #{@sandbox_dir}; mkdir -p a; touch a/b; tar cf xfer.tar a; mkdir dst" + archive = File.read("#{@sandbox_dir}/xfer.tar") + @con.write_archive(archive, "#{@sandbox_dir}/dst") + File.directory?("#{@sandbox_dir}/dst/a").should be_true + File.exists?("#{@sandbox_dir}/dst/a/b").should be_true + end + + it "write_archive works for paths with spaces" do + system "cd #{@sandbox_dir}; mkdir -p a; touch a/b; tar cf xfer.tar a; mkdir with\\ space" + archive = File.read("#{@sandbox_dir}/xfer.tar") + @con.write_archive(archive, "#{@sandbox_dir}/with space") + File.directory?("#{@sandbox_dir}/with space/a").should be_true + File.exists?("#{@sandbox_dir}/with space/a/b").should be_true + end + + it "index fetches list of all files and dirs in a dir when pattern is empty" do + system "cd #{@sandbox_dir}; mkdir dir; touch file" + @con.index(@sandbox_dir, '').should == [ 'dir/', 'file' ] + end + + it "index fetches only files with a certain extension with a flat pattern, *.rb" do + system "cd #{@sandbox_dir}; touch a.rb; touch b.txt" + @con.index(@sandbox_dir, '*.rb').should == [ 'a.rb' ] + end + + it "index raises DoesNotExist when the base path is invalid" do + lambda { @con.index('/does/not/exist', '*') }.should raise_error(Rush::DoesNotExist, '/does/not/exist') + end + + it "stat gives file stats like size and timestamps" do + @con.stat(@sandbox_dir).should have_key(:ctime) + @con.stat(@sandbox_dir).should have_key(:size) + end + + it "stat fetches the octal permissions" do + @con.stat(@sandbox_dir)[:mode].should be_kind_of(Fixnum) + end + + it "stat raises DoesNotExist if the entry does not exist" do + fname = "#{@sandbox_dir}/does_not_exist" + lambda { @con.stat(fname) }.should raise_error(Rush::DoesNotExist, fname) + end + + it "set_access invokes the access object" do + access = double("access") + access.should_receive(:apply).with('/some/path') + @con.set_access('/some/path', access) + end + + it "chown should change the ownership of a file" + + if !RUBY_PLATFORM.match(/darwin/) # doesn't work on OS X 'cause du switches are different + it "size gives size of a directory and all its contents recursively" do + system "mkdir -p #{@sandbox_dir}/a/b/; echo 1234 > #{@sandbox_dir}/a/b/c" + @con.size(@sandbox_dir).should == (4096*3 + 5) + end + end + + it "parses ps output on os x" do + @con.parse_ps("21712 501 21711 1236 0 /usr/bin/vi somefile.rb").should == { + :pid => "21712", + :uid => "501", + :parent_pid => 21711, + :mem => 1236, + :cpu => 0, + :command => '/usr/bin/vi', + :cmdline => '/usr/bin/vi somefile.rb', + } + end + + it "gets the list of processes on os x via the ps command" do + @con.should_receive(:os_x_raw_ps).and_return < "1", :uid => "0", :parent_pid => 1, :mem => 1111, :cpu => 0, :command => "cmd1", :cmdline => "cmd1 args" }, - { :pid => "2", :uid => "501", :parent_pid => 1, :mem => 222, :cpu => 1, :command => "cmd2", :cmdline => "cmd2" }, - ] - end - - it "the current process should be alive" do - @con.process_alive(Process.pid).should be_true - end - - it "a made-up process should not be alive" do - @con.process_alive(99999).should be_false - end - - it "kills a process by pid sending a TERM" do - @con.stub(:process_alive).and_return(false) - ::Process.should_receive(:kill).with('TERM', 123).once - @con.kill_process(123) - end - - it "kills a process by pid sending a KILL signal if TERM doesn't work" do - @con.stub(:process_alive).and_return(true) - ::Process.should_receive(:kill).with('TERM', 123).at_least(:twice) - ::Process.should_receive(:kill).with('KILL', 123) - @con.kill_process(123) - end - - it "kills a process by pid without sending TERM if :wait is zero" do - ::Process.should_not_receive(:kill).with('TERM', 123) - ::Process.should_receive(:kill).with('KILL', 123) - @con.kill_process(123, :wait => 0) - end - - it "does not raise an error if the process is already dead" do - ::Process.should_receive(:kill).and_raise(Errno::ESRCH) - lambda { @con.kill_process(123) }.should_not raise_error - end - - it "executes a bash command, returning stdout when successful" do - @con.bash("echo test").should == "test\n" - end - - it "executes a bash command, raising and error (with stderr as the message) when return value is nonzero" do - lambda { @con.bash("no_such_bin") }.should raise_error(Rush::BashFailed, /command not found/) - end - - it "executes a bash command as another user using sudo" do - @con.bash("echo test2", ENV['USER']).should == "test2\n" - end - - it "executes a bash command in the background, returning the pid" do - @con.bash("true", nil, true).should > 0 - end - - it "ensure_tunnel to match with remote connection" do - @con.ensure_tunnel - end - - it "always returns true on alive?" do - @con.should be_alive - end - - it "resolves a unix uid to a user" do - @con.resolve_unix_uid_to_user(0).should == "root" - @con.resolve_unix_uid_to_user('0').should == "root" - end - - it "returns nil if the unix uid does not exist" do - @con.resolve_unix_uid_to_user(9999).should be_nil - end - - it "iterates through a process list and resolves the unix uid for each" do - list = [ { :uid => 0, :command => 'pureftpd' }, { :uid => 9999, :command => 'defunk' } ] - @con.resolve_unix_uids(list).should == [ { :uid => 0, :user => 'root', :command => 'pureftpd' }, { :uid => 9999, :command => 'defunk', :user => nil } ] - end + @con.os_x_processes.should == [ + { :pid => "1", :uid => "0", :parent_pid => 1, :mem => 1111, :cpu => 0, :command => "cmd1", :cmdline => "cmd1 args" }, + { :pid => "2", :uid => "501", :parent_pid => 1, :mem => 222, :cpu => 1, :command => "cmd2", :cmdline => "cmd2" }, + ] + end + + it "the current process should be alive" do + @con.process_alive(Process.pid).should be_true + end + + it "a made-up process should not be alive" do + @con.process_alive(99999).should be_false + end + + it "kills a process by pid sending a TERM" do + @con.stub(:process_alive).and_return(false) + ::Process.should_receive(:kill).with('TERM', 123).once + @con.kill_process(123) + end + + it "kills a process by pid sending a KILL signal if TERM doesn't work" do + @con.stub(:process_alive).and_return(true) + ::Process.should_receive(:kill).with('TERM', 123).at_least(:twice) + ::Process.should_receive(:kill).with('KILL', 123) + @con.kill_process(123) + end + + it "kills a process by pid without sending TERM if :wait is zero" do + ::Process.should_not_receive(:kill).with('TERM', 123) + ::Process.should_receive(:kill).with('KILL', 123) + @con.kill_process(123, :wait => 0) + end + + it "does not raise an error if the process is already dead" do + ::Process.should_receive(:kill).and_raise(Errno::ESRCH) + lambda { @con.kill_process(123) }.should_not raise_error + end + + it "executes a bash command, returning stdout when successful" do + @con.bash("echo test").should == "test\n" + end + + it "executes a bash command, raising and error (with stderr as the message) when return value is nonzero" do + lambda { @con.bash("no_such_bin") }.should raise_error(Rush::BashFailed, /command not found/) + end + + it "executes a bash command as another user using sudo" do + @con.bash("echo test2", ENV['USER']).should == "test2\n" + end + + it "executes a bash command in the background, returning the pid" do + @con.bash("true", nil, true).should > 0 + end + + it "ensure_tunnel to match with remote connection" do + @con.ensure_tunnel + end + + it "always returns true on alive?" do + @con.should be_alive + end + + it "resolves a unix uid to a user" do + @con.resolve_unix_uid_to_user(0).should == "root" + @con.resolve_unix_uid_to_user('0').should == "root" + end + + it "returns nil if the unix uid does not exist" do + @con.resolve_unix_uid_to_user(9999).should be_nil + end + + it "iterates through a process list and resolves the unix uid for each" do + list = [ { :uid => 0, :command => 'pureftpd' }, { :uid => 9999, :command => 'defunk' } ] + @con.resolve_unix_uids(list).should == [ { :uid => 0, :user => 'root', :command => 'pureftpd' }, { :uid => 9999, :command => 'defunk', :user => nil } ] + end end From c0ea256a40ca2bf4a97ca475c01284a71d35193f Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 1 Jun 2014 23:48:03 +0400 Subject: [PATCH 050/119] Add owner accessor to file --- lib/rush.rb | 2 ++ lib/rush/entry.rb | 15 +++++++++++++++ lib/rush/shell.rb | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/rush.rb b/lib/rush.rb index 78476a6..442f472 100644 --- a/lib/rush.rb +++ b/lib/rush.rb @@ -1,3 +1,5 @@ +require 'etc' # get info from /etc + # The top-level Rush module has some convenience methods for accessing the # local box. module Rush diff --git a/lib/rush/entry.rb b/lib/rush/entry.rb index 7f74f29..eac4394 100644 --- a/lib/rush/entry.rb +++ b/lib/rush/entry.rb @@ -183,6 +183,21 @@ def chown_R(user = nil, group = nil, options = {}) chown(user, group, options) end + # Chown in ruby way. Ruby way is creating accessors. + def owner + stat = ::File.stat(full_path) + { user: Etc.getpwuid(stat.uid).name, group: Etc.getgrgid(stat.gid).name } + end + + def owner=(params) + case params + when Hash then chown(params.delete(:user), params.delete(:group), params) + when String then chown(params) + when Numeric then chown(Etc.getpwuid(params).name) + else raise 'Something wrong with params for chown' + end + end + # Destroy the entry. If it is a dir, everything inside it will also be destroyed. def destroy connection.destroy(full_path) diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 6f16668..b7f2eab 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -172,7 +172,7 @@ def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc: end def complete_variable(partial_name, pre) - pre = eval(pre, @pure_binding) + pre = eval(pre, @pure_binding) rescue nil the_binding = pre ? pre.instance_eval('binding') : @pure_binding lvars = eval('local_variables', the_binding) gvars = eval('global_variables', the_binding) From 9a570a1c40b5c6209671f4daf17d8cad518235a1 Mon Sep 17 00:00:00 2001 From: naja melan Date: Thu, 22 May 2014 23:24:29 +0200 Subject: [PATCH 051/119] remove a backup file --- lib/rush/shell.rb~ | 188 --------------------------------------------- 1 file changed, 188 deletions(-) delete mode 100644 lib/rush/shell.rb~ diff --git a/lib/rush/shell.rb~ b/lib/rush/shell.rb~ deleted file mode 100644 index 7406391..0000000 --- a/lib/rush/shell.rb~ +++ /dev/null @@ -1,188 +0,0 @@ -require 'coolline' -require 'coderay' -require 'pp' - -# Rush::Shell is used to create an interactive shell. It is invoked by the rush binary. -module Rush - class Shell - attr_accessor :suppress_output - # Set up the user's environment, including a pure binding into which - # env.rb and commands.rb are mixed. - def initialize - root = Rush::Dir.new('/') - home = Rush::Dir.new(ENV['HOME']) if ENV['HOME'] - pwd = Rush::Dir.new(ENV['PWD']) if ENV['PWD'] - - @config = Rush::Config.new - - @history = Coolline::History.new @config.history_file.full_path - - @readline = Coolline.new do |c| - c.transform_proc = proc { syntax_highlight c.line } - c.completion_proc = proc { complete c.completed_word } - end - - @box = Rush::Box.new - @pure_binding = @box.instance_eval "binding" - $last_res = nil - - eval @config.load_env, @pure_binding - - commands = @config.load_commands - Rush::Dir.class_eval commands - Array.class_eval commands - - # Multiline commands should be stored somewhere' - @multiline_cmd = '' - end - - # Run a single command. - def execute(cmd) - res = eval(@multiline_cmd << "\n" << cmd, @pure_binding) - $last_res = res - eval("_ = $last_res", @pure_binding) - @multiline_cmd = '' - print_result res - rescue SyntaxError => e - unless e.message.include? 'unexpected end-of-input' - @multiline_cmd = '' - puts "Exception #{e.class} -> #{e.message}" - end - # Else it should be multiline command. - rescue Rush::Exception => e - puts "Exception #{e.class} -> #{e.message}" - @multiline_cmd = '' - rescue ::Exception => e - puts "Exception #{e.class} -> #{e.message}" - e.backtrace.each { |t| puts "\t#{::File.expand_path(t)}" } - @multiline_cmd = '' - end - - # Run the interactive shell using coolline. - def run - loop do - prompt = self.class.prompt || "#{`whoami`.chomp} $ " - cmd = @readline.readline prompt - - finish if cmd.nil? or cmd == 'exit' - next if cmd.empty? - @history << cmd - execute cmd - end - end - - # Tune the prompt with - # Rush::Shell.prompt = 'hey there! > ' - class << self - attr_accessor :prompt - end - - # Save history to ~/.rush/history when the shell exists. - def finish - @config.save_history(Coolline::History.to_a) - puts - exit - end - - # Nice printing of different return types, particularly Rush::SearchResults. - def print_result(res) - return if self.suppress_output - if res.kind_of? String - output = res - elsif res.kind_of? Rush::SearchResults - output = res.to_s << - "#{res.entries.size} matching files with #{res.lines.size} matching lines" - elsif res.respond_to? :each - output = '' - counts = res.inject(Hash.new(0)) do |result, item| - output << item.to_s << "\n" - result[item.class] += 1 - result - end - if counts == {} - output = "=> (empty set)" - else - count_s = counts.map do |klass, count| - "#{count} x #{klass}" - end.join(', ') - output << "=> #{count_s}" - end - else - output = "=> #{res.inspect}" - end - output.lines.count > 5 ? output.less : puts(output) - end - - def path_parts(input) # :nodoc: - case input - when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)([\[\/])(['"])([^\3]*)$/ - $~.to_a.slice(1, 4).push($~.pre_match) - when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)(\.)(\w*)$/ - $~.to_a.slice(1, 3).push($~.pre_match) - when /((?:@{1,2}|\$|)\w+)$/ - $~.to_a.slice(1, 1).push(nil).push($~.pre_match) - else - [ nil, nil, nil ] - end - end - - def complete_method(receiver, dot, partial_name, pre) - path = eval("#{receiver}.full_path", @pure_binding) rescue nil - box = eval("#{receiver}.box", @pure_binding) rescue nil - if path and box - (box[path].methods - Object.methods). - select { |e| e.match /^#{Regexp.escape(partial_name)}/ }. - map { |e| (pre || '') + receiver + dot + e.to_s } - end - end - - def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc: - original_var, fixed_path = possible_var, '' - if /^(.+\/)([^\/]*)$/ === partial_path - fixed_path, partial_path = $~.captures - possible_var += "['#{fixed_path}']" - end - full_path = eval("#{possible_var}.full_path", @pure_binding) rescue nil - box = eval("#{possible_var}.box", @pure_binding) rescue nil - if full_path and box - Rush::Dir.new(full_path, box).entries. - select { |e| e.name.match(/^#{Regexp.escape(partial_path)}/) }. - map { |e| (pre || '') + original_var + accessor + quote + - fixed_path + e.name + (e.dir? ? "/" : "") } - end - end - - def complete_variable(partial_name, pre) - lvars = eval('local_variables', @pure_binding) - gvars = eval('global_variables', @pure_binding) - ivars = eval('instance_variables', @pure_binding) - constants = eval('Object.constants', @pure_binding) - (lvars + gvars + ivars + constants). - select { |e| e.match(/^#{Regexp.escape(partial_name)}/) }. - map { |e| (pre || '') + e.to_s } - end - - # Try to do tab completion on dir square brackets and slash accessors. - # - # Example: - # - # dir['subd # presing tab here will produce dir['subdir/ if subdir exists - # dir/'subd # presing tab here will produce dir/'subdir/ if subdir exists - # - # This isn't that cool yet, because it can't do multiple levels of subdirs. - # It does work remotely, though, which is pretty sweet. - def complete(input) - receiver, accessor, *rest = path_parts(input) - return unless receiver - case accessor - when /^[\[\/]$/ then complete_path(receiver, accessor, *rest) - when /^\.$/ then complete_method(receiver, accessor, *rest) - when nil then complete_variable(receiver, *rest) - end - end - - def syntax_highlight(input) - CodeRay.encode input, :ruby, :term - end - end -end From 8f7d21ceb92b29bee96ee70933498c65e240b074 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Mon, 11 Aug 2014 07:54:49 +0400 Subject: [PATCH 052/119] Fix crashing on some cases of autocomplete --- lib/rush/local.rb | 2 +- lib/rush/shell.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rush/local.rb b/lib/rush/local.rb index 8a6f321..e6e388d 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -1,6 +1,7 @@ require 'fileutils' require 'yaml' require 'timeout' +require 'session' # Rush::Box uses a connection object to execute all rush commands. If the box # is local, Rush::Connection::Local is created. The local connection is the @@ -295,7 +296,6 @@ def kill_process(pid, options={}) def bash(command, user=nil, background=false, reset_environment=false) return bash_background(command, user, reset_environment) if background - require 'session' sh = Session::Bash.new shell = reset_environment ? "env -i bash" : "bash" out, err = sh.execute sudo(user, shell), :stdin => command diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index b7f2eab..7061aed 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -172,6 +172,7 @@ def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc: end def complete_variable(partial_name, pre) + return [] if pre.gsub(/\W/, '').empty? pre = eval(pre, @pure_binding) rescue nil the_binding = pre ? pre.instance_eval('binding') : @pure_binding lvars = eval('local_variables', the_binding) From c1f4dc9b9277be122910cc76f3ea1768d9f18711 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 14 Sep 2014 12:41:36 +0400 Subject: [PATCH 053/119] [refs #10] Remove mongrel, bring connection via net/ssh. Still no rush magic, but I am working on it. --- .idea/.name | 1 + .idea/.rakeTasks | 7 + .idea/codeStyleSettings.xml | 14 + .idea/dataSources.ids | 15 + .idea/dataSources.xml | 44 ++ .idea/dictionaries/s.xml | 9 + .idea/encodings.xml | 5 + .idea/misc.xml | 5 + .idea/modules.xml | 14 + .idea/rush.iml | 21 + .idea/scopes/scope_settings.xml | 5 + .idea/vcs.xml | 15 + .idea/workspace.xml | 750 ++++++++++++++++++++++++++++++++ Gemfile | 1 + Gemfile.lock | 55 +-- lib/rush.rb | 25 +- lib/rush/access.rb | 1 + lib/rush/remote.rb | 4 +- lib/rush/server.rb | 116 ----- lib/rush/shell.rb | 1 - lib/rush/ssh_tunnel.rb | 128 ++---- 21 files changed, 980 insertions(+), 256 deletions(-) create mode 100644 .idea/.name create mode 100644 .idea/.rakeTasks create mode 100644 .idea/codeStyleSettings.xml create mode 100644 .idea/dataSources.ids create mode 100644 .idea/dataSources.xml create mode 100644 .idea/dictionaries/s.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/rush.iml create mode 100644 .idea/scopes/scope_settings.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml delete mode 100644 lib/rush/server.rb diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..6e21420 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +rush \ No newline at end of file diff --git a/.idea/.rakeTasks b/.idea/.rakeTasks new file mode 100644 index 0000000..c6656b1 --- /dev/null +++ b/.idea/.rakeTasks @@ -0,0 +1,7 @@ + + diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 0000000..cab819f --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/.idea/dataSources.ids b/.idea/dataSources.ids new file mode 100644 index 0000000..a9d0a0e --- /dev/null +++ b/.idea/dataSources.ids @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..366e380 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,44 @@ + + + + + sqlite.xerial + org.sqlite.JDBC + jdbc:sqlite:$USER_HOME$/work/savage/db/development.sqlite3 + + + + sqlite.xerial + org.sqlite.JDBC + jdbc:sqlite:$USER_HOME$/work/timesheet/db/development.sqlite3 + + + + sqlite.xerial + org.sqlite.JDBC + jdbc:sqlite:$USER_HOME$/work/timesheet/db/test.sqlite3 + + + + mysql + com.mysql.jdbc.Driver + jdbc:mysql://devdb1.prg-dev.veribox.com/timesheet + develop + dfcedfcfdfdcdfcfdfc6dfc5dfdadfeadfc8dfc5dfd2 + + + + sqlite.xerial + org.sqlite.JDBC + jdbc:sqlite:$USER_HOME$/work/fundsex/db/development.sqlite3 + + + + sqlite.xerial + org.sqlite.JDBC + jdbc:sqlite:$USER_HOME$/work/fundsex/db/development.sqlite3 + + + + + diff --git a/.idea/dictionaries/s.xml b/.idea/dictionaries/s.xml new file mode 100644 index 0000000..7a46d27 --- /dev/null +++ b/.idea/dictionaries/s.xml @@ -0,0 +1,9 @@ + + + + idcert + omniauth + timeframe + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6d07aa6 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a4360a3 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/.idea/rush.iml b/.idea/rush.iml new file mode 100644 index 0000000..4f48cb5 --- /dev/null +++ b/.idea/rush.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..be4e529 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..4cf1b9d --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,750 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ruby + + + XML + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1407844233027 + 1407844233027 + + + 1408346666472 + 1408346666472 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gemfile b/Gemfile index 8a92777..b3e6a1e 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,7 @@ source "http://rubygems.org" gem 'session' gem 'coolline' gem 'coderay' +gem 'net-ssh' group :development do gem 'rake' diff --git a/Gemfile.lock b/Gemfile.lock index 2276499..8a02f1c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,22 +4,22 @@ GEM addressable (2.3.6) builder (3.2.2) coderay (1.1.0) - coolline (0.4.3) + coolline (0.4.4) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) diff-lcs (1.2.5) faraday (0.9.0) multipart-post (>= 1.2, < 3) - git (1.2.6) - github_api (0.11.3) + git (1.2.8) + github_api (0.12.1) addressable (~> 2.3) - descendants_tracker (~> 0.0.1) + descendants_tracker (~> 0.0.4) faraday (~> 0.8, < 0.10) - hashie (>= 1.2) + hashie (>= 3.2) multi_json (>= 1.7.5, < 2.0) - nokogiri (~> 1.6.0) + nokogiri (~> 1.6.3) oauth2 - hashie (2.1.1) + hashie (3.3.1) highline (1.6.21) jeweler (2.0.1) builder @@ -31,34 +31,37 @@ GEM rake rdoc json (1.8.1) - jwt (0.1.11) - multi_json (>= 1.5) - mini_portile (0.5.3) - multi_json (1.9.3) + jwt (1.0.0) + mini_portile (0.6.0) + multi_json (1.10.1) multi_xml (0.5.5) multipart-post (2.0.0) - nokogiri (1.6.1) - mini_portile (~> 0.5.0) - oauth2 (0.9.3) + nokogiri (1.6.3.1) + mini_portile (= 0.6.0) + oauth2 (1.0.0) faraday (>= 0.8, < 0.10) - jwt (~> 0.1.8) + jwt (~> 1.0) multi_json (~> 1.3) multi_xml (~> 0.5) rack (~> 1.2) rack (1.5.2) - rake (10.3.1) - rdoc (4.1.1) + rake (10.3.2) + rdoc (4.1.2) json (~> 1.4) - rspec (2.14.1) - rspec-core (~> 2.14.0) - rspec-expectations (~> 2.14.0) - rspec-mocks (~> 2.14.0) - rspec-core (2.14.8) - rspec-expectations (2.14.5) - diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.14.6) + rspec (3.1.0) + rspec-core (~> 3.1.0) + rspec-expectations (~> 3.1.0) + rspec-mocks (~> 3.1.0) + rspec-core (3.1.2) + rspec-support (~> 3.1.0) + rspec-expectations (3.1.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.1.0) + rspec-mocks (3.1.0) + rspec-support (~> 3.1.0) + rspec-support (3.1.0) session (3.2.0) - thread_safe (0.3.3) + thread_safe (0.3.4) PLATFORMS ruby diff --git a/lib/rush.rb b/lib/rush.rb index 442f472..7d87a01 100644 --- a/lib/rush.rb +++ b/lib/rush.rb @@ -3,16 +3,20 @@ # The top-level Rush module has some convenience methods for accessing the # local box. module Rush - # Access the root filesystem of the local box. Example: + # Access the root filesystem of the local box. # + # @param key [String] relative path. + # @example # Rush['/etc/hosts'].contents # def self.[](key) box[key] end - # Create a dir object from the path of a provided file. Example: + # Create a dir object from the path of a provided file. # + # @param filename [String] path that should be created. + # @example # Rush.dir(__FILE__).files # def self.dir(filename) @@ -20,8 +24,9 @@ def self.dir(filename) end # Create a dir object based on the shell's current working directory at the - # time the program was run. Example: + # time the program was run. # + # @example # Rush.launch_dir.files # def self.launch_dir @@ -34,17 +39,19 @@ def self.bash(command, options={}) box.bash(command, options) end - # Pull the process list for the local machine. Example: - # - # Rush.processes.filter(:cmdline => /ruby/) + # Pull the process list for the local machine. + # + # @example + # Rush.processes.filter(:cmdline => /ruby/) # def self.processes box.processes end - # Get the process object for this program's PID. Example: - # - # puts "I'm using #{Rush.my_process.mem} blocks of memory" + # Get the process object for this program's PID. + # + # @example + # puts "I'm using #{Rush.my_process.mem} blocks of memory" # def self.my_process box.processes.filter(:pid => ::Process.pid).first diff --git a/lib/rush/access.rb b/lib/rush/access.rb index effcff1..d3a22e4 100644 --- a/lib/rush/access.rb +++ b/lib/rush/access.rb @@ -1,5 +1,6 @@ # A class to hold permissions (read, write, execute) for files and dirs. # See Rush::Entry#access= for information on the public-facing interface. +# class Rush::Access ROLES = %w(user group other) PERMISSIONS = %w(read write execute) diff --git a/lib/rush/remote.rb b/lib/rush/remote.rb index 90ea3d6..f823c4a 100644 --- a/lib/rush/remote.rb +++ b/lib/rush/remote.rb @@ -9,9 +9,9 @@ class Rush::Connection::Remote attr_reader :host, :tunnel - def initialize(host) + def initialize(host, user) @host = host - @tunnel = Rush::SshTunnel.new(host) + @tunnel = Rush::SshTunnel.new(host, user) end def write_file(full_path, contents) diff --git a/lib/rush/server.rb b/lib/rush/server.rb deleted file mode 100644 index 168a4f3..0000000 --- a/lib/rush/server.rb +++ /dev/null @@ -1,116 +0,0 @@ -require 'mongrel' -require 'base64' - -# Mongrel handler that translates the incoming HTTP request into a -# Rush::Connection::Local call. The results are sent back across the wire to -# be decoded by Rush::Connection::Remote on the other side. -class RushHandler < Mongrel::HttpHandler - def process(request, response) - request.params['QUERY_STRING'].split("?").last. - split("&").inject({}) do |params, tuple| - key, value = tuple.split("=") - params[key.to_sym] = value - end - - unless authorize(request.params['HTTP_AUTHORIZATION']) - response.start(401) do |head, out| - end - else - payload = request.body.read - - without_action = params - without_action.delete(params[:action]) - - msg = sprintf "%-20s", params[:action] - msg += without_action.inspect - msg += " + #{payload.size} bytes of payload" if payload.size > 0 - log msg - - params[:payload] = payload - - begin - result = box.connection.receive(params) - - response.start(200) do |head, out| - out.write result - end - rescue Rush::Exception => e - response.start(400) do |head, out| - out.write "#{e.class}\n#{e.message}\n" - end - end - end - rescue Exception => e - log e.full_display - end - - def authorize(auth) - unless m = auth.match(/^Basic (.+)$/) - log "Request with no authorization data" - return false - end - - decoded = Base64.decode64(m[1]) - user, password = decoded.split(':', 2) - - if user.nil? or user.length == 0 or password.nil? or password.length == 0 - log "Malformed user or password" - return false - end - - if password == config.passwords[user] - return true - else - log "Access denied to #{user}" - return false - end - end - - def box - @box ||= Rush::Box.new('localhost') - end - - def config - @config ||= Rush::Config.new - end - - def log(msg) - File.open('rushd.log', 'a') do |f| - f.puts "#{Time.now.strftime('%Y-%m-%d %H:%I:%S')} :: #{msg}" - end - end -end - -# A container class to run the Mongrel server for rushd. -class RushServer - def run - host = "127.0.0.1" - port = Rush::Config::DefaultPort - - rushd = RushHandler.new - rushd.log "rushd listening on #{host}:#{port}" - - h = Mongrel::HttpServer.new(host, port) - h.register("/", rushd) - h.run.join - end -end - -class Exception - def full_display - out = [] - out << "Exception #{self.class} => #{self}" - out << "Backtrace:" - out << self.filtered_backtrace.collect do |t| - " #{t}" - end - out << "" - out.join("\n") - end - - def filtered_backtrace - backtrace.reject do |bt| - bt.match(/^\/usr\//) - end - end -end diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 7061aed..b7f2eab 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -172,7 +172,6 @@ def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc: end def complete_variable(partial_name, pre) - return [] if pre.gsub(/\W/, '').empty? pre = eval(pre, @pure_binding) rescue nil the_binding = pre ? pre.instance_eval('binding') : @pure_binding lvars = eval('local_variables', the_binding) diff --git a/lib/rush/ssh_tunnel.rb b/lib/rush/ssh_tunnel.rb index be50729..a463c3e 100644 --- a/lib/rush/ssh_tunnel.rb +++ b/lib/rush/ssh_tunnel.rb @@ -1,122 +1,46 @@ +require 'net/ssh' + # Internal class for managing an ssh tunnel, across which relatively insecure -# HTTP commands can be sent by Rush::Connection::Remote. +# class Rush::SshTunnel - def initialize(real_host) - @real_host = real_host - end - - def host - 'localhost' - end - - def port - @port - end - - def ensure_tunnel(options={}) - return if @port and tunnel_alive? - - @port = config.tunnels[@real_host] - - if !@port or !tunnel_alive? - setup_everything(options) - end - end - - def setup_everything(options={}) - display "Connecting to #{@real_host}..." - push_credentials - launch_rushd - establish_tunnel(options) - end - - def push_credentials - display "Pushing credentials" - config.ensure_credentials_exist - ssh_append_to_credentials(config.credentials_file.contents.strip) - end - - def ssh_append_to_credentials(string) - # the following horror is exactly why rush is needed - passwords_file = "~/.rush/passwords" - string = "'#{string}'" - ssh "M=`grep #{string} #{passwords_file} 2>/dev/null | wc -l`; if [ $M = 0 ]; then mkdir -p .rush; chmod 700 .rush; echo #{string} >> #{passwords_file}; chmod 600 #{passwords_file}; fi" - end + attr_reader :connection, :transport, :host, :user, :password + attr_accessor :config - def launch_rushd - display "Launching rushd" - ssh("if [ `ps aux | grep rushd | grep -v grep | wc -l` -ge 1 ]; then exit; fi; rushd > /dev/null 2>&1 &") + def initialize(host, user, password = nil) + @host = host + @user = user + @password = password end - def establish_tunnel(options={}) - display "Establishing ssh tunnel" - @port = next_available_port - - make_ssh_tunnel(options) - - tunnels = config.tunnels - tunnels[@real_host] = @port - config.save_tunnels tunnels - - sleep 0.5 - end - - def tunnel_options - { - :local_port => @port, - :remote_port => Rush::Config::DefaultPort, - :ssh_host => @real_host, - } + def connect + @connection = establish_tunnel(host, user, password: password) end - def tunnel_alive? - `#{tunnel_count_command}`.to_i > 0 + def disconnect + connect.close + transport.close end - def tunnel_count_command - "ps x | grep '#{ssh_tunnel_command_without_stall}' | grep -v grep | wc -l" - end - - class SshFailed < Exception; end - class NoPortSelectedYet < Exception; end - - def ssh(command) - raise SshFailed unless system("ssh #{@real_host} '#{command}'") - end - - def make_ssh_tunnel(options={}) - raise SshFailed unless system(ssh_tunnel_command(options)) - end - - def ssh_tunnel_command_without_stall - options = tunnel_options - raise NoPortSelectedYet unless options[:local_port] - "ssh -f -L #{options[:local_port]}:127.0.0.1:#{options[:remote_port]} #{options[:ssh_host]}" - end - - def ssh_stall_command(options={}) - if options[:timeout] == :infinite - "while [ 1 ]; do sleep 1000; done" - elsif options[:timeout].to_i > 10 - "sleep #{options[:timeout].to_i}" + def establish_tunnel(host, user, options = {}) + options = { user: user, host_name: host, logger: Logger.new(STDERR) } + options[:logger].level = Logger::INFO + @transport = Net::SSH::Transport::Session.new(host, options) + auth = Net::SSH::Authentication::Session.new(transport, options) + args = ['ssh-connection', user, options.delete(:password)].reject(&:nil?) + if auth.authenticate(*args) + Net::SSH::Connection::Session.new(transport, options) else - "sleep 9000" + fail SshFailed, 'Authentication failed' end end - def ssh_tunnel_command(options={}) - ssh_tunnel_command_without_stall + ' "' + ssh_stall_command(options) + '"' - end - - def next_available_port - (config.tunnels.values.max || Rush::Config::DefaultPort) + 1 + def send(command) + connection.exec! command end def config @config ||= Rush::Config.new end - def display(msg) - puts msg - end + class SshFailed < StandardError; end end From 50f2dafb2e8885ae525e88d64c5a4f93b1c56067 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 14 Sep 2014 12:43:10 +0400 Subject: [PATCH 054/119] Remove .idea --- .idea/.name | 1 - .idea/.rakeTasks | 7 - .idea/codeStyleSettings.xml | 14 - .idea/dataSources.ids | 15 - .idea/dataSources.xml | 44 -- .idea/dictionaries/s.xml | 9 - .idea/encodings.xml | 5 - .idea/misc.xml | 5 - .idea/modules.xml | 14 - .idea/rush.iml | 21 - .idea/scopes/scope_settings.xml | 5 - .idea/vcs.xml | 15 - .idea/workspace.xml | 750 -------------------------------- 13 files changed, 905 deletions(-) delete mode 100644 .idea/.name delete mode 100644 .idea/.rakeTasks delete mode 100644 .idea/codeStyleSettings.xml delete mode 100644 .idea/dataSources.ids delete mode 100644 .idea/dataSources.xml delete mode 100644 .idea/dictionaries/s.xml delete mode 100644 .idea/encodings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/rush.iml delete mode 100644 .idea/scopes/scope_settings.xml delete mode 100644 .idea/vcs.xml delete mode 100644 .idea/workspace.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 6e21420..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -rush \ No newline at end of file diff --git a/.idea/.rakeTasks b/.idea/.rakeTasks deleted file mode 100644 index c6656b1..0000000 --- a/.idea/.rakeTasks +++ /dev/null @@ -1,7 +0,0 @@ - - diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml deleted file mode 100644 index cab819f..0000000 --- a/.idea/codeStyleSettings.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - diff --git a/.idea/dataSources.ids b/.idea/dataSources.ids deleted file mode 100644 index a9d0a0e..0000000 --- a/.idea/dataSources.ids +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml deleted file mode 100644 index 366e380..0000000 --- a/.idea/dataSources.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - sqlite.xerial - org.sqlite.JDBC - jdbc:sqlite:$USER_HOME$/work/savage/db/development.sqlite3 - - - - sqlite.xerial - org.sqlite.JDBC - jdbc:sqlite:$USER_HOME$/work/timesheet/db/development.sqlite3 - - - - sqlite.xerial - org.sqlite.JDBC - jdbc:sqlite:$USER_HOME$/work/timesheet/db/test.sqlite3 - - - - mysql - com.mysql.jdbc.Driver - jdbc:mysql://devdb1.prg-dev.veribox.com/timesheet - develop - dfcedfcfdfdcdfcfdfc6dfc5dfdadfeadfc8dfc5dfd2 - - - - sqlite.xerial - org.sqlite.JDBC - jdbc:sqlite:$USER_HOME$/work/fundsex/db/development.sqlite3 - - - - sqlite.xerial - org.sqlite.JDBC - jdbc:sqlite:$USER_HOME$/work/fundsex/db/development.sqlite3 - - - - - diff --git a/.idea/dictionaries/s.xml b/.idea/dictionaries/s.xml deleted file mode 100644 index 7a46d27..0000000 --- a/.idea/dictionaries/s.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - idcert - omniauth - timeframe - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index e206d70..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 6d07aa6..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index a4360a3..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/.idea/rush.iml b/.idea/rush.iml deleted file mode 100644 index 4f48cb5..0000000 --- a/.idea/rush.iml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml deleted file mode 100644 index 922003b..0000000 --- a/.idea/scopes/scope_settings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index be4e529..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 4cf1b9d..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,750 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Ruby - - - XML - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1407844233027 - 1407844233027 - - - 1408346666472 - 1408346666472 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 83c02cb28f296d19044e331a4f079eccfb579ea3 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 14 Sep 2014 15:06:02 +0400 Subject: [PATCH 055/119] Dir and config specs moved to expect syntax --- rush.gemspec | 19 +++++++++++---- spec/config_spec.rb | 44 +++++++++++++++++----------------- spec/dir_spec.rb | 57 ++++++++++++++++++++++----------------------- 3 files changed, 65 insertions(+), 55 deletions(-) diff --git a/rush.gemspec b/rush.gemspec index 4b7f83f..5b3dac1 100644 --- a/rush.gemspec +++ b/rush.gemspec @@ -9,8 +9,9 @@ Gem::Specification.new do |s| s.version = "0.6.8" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib"] s.authors = ["Adam Wiggins"] - s.date = "2014-05-04" + s.date = "2014-09-14" s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." s.email = "adam@heroku.com" s.executables = ["rush", "rushd"] @@ -44,7 +45,6 @@ Gem::Specification.new do |s| "lib/rush/process_set.rb", "lib/rush/remote.rb", "lib/rush/search_results.rb", - "lib/rush/server.rb", "lib/rush/shell.rb", "lib/rush/ssh_tunnel.rb", "lib/rush/string_ext.rb", @@ -72,9 +72,8 @@ Gem::Specification.new do |s| ] s.homepage = "http://rush.heroku.com/" s.licenses = ["MIT"] - s.require_paths = ["lib"] s.rubyforge_project = "ruby-shell" - s.rubygems_version = "2.1.9" + s.rubygems_version = "2.2.2" s.summary = "A Ruby replacement for bash+ssh." if s.respond_to? :specification_version then @@ -84,15 +83,27 @@ Gem::Specification.new do |s| s.add_runtime_dependency(%q, [">= 0"]) s.add_runtime_dependency(%q, [">= 0"]) s.add_runtime_dependency(%q, [">= 0"]) + s.add_runtime_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) else s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) end else s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) end end diff --git a/spec/config_spec.rb b/spec/config_spec.rb index 24f7ebb..6ff897b 100644 --- a/spec/config_spec.rb +++ b/spec/config_spec.rb @@ -12,97 +12,97 @@ end it "creates the dir" do - File.directory?(@sandbox_dir).should be_true + expect(File.directory?(@sandbox_dir)).to eq(true) end it "can access the history file" do - @config.history_file.class.should == Rush::File + expect(@config.history_file).to be_kind_of(Rush::File) end it "saves the shell command history" do @config.save_history(%w(1 2 3)) - @config.history_file.contents.should == "1\n2\n3\n" + expect(@config.history_file.contents).to eq("1\n2\n3\n") end it "loads the shell command history" do @config.save_history(%w(1 2 3)) - @config.load_history.should == %w(1 2 3) + expect(@config.load_history).to eq(%w(1 2 3)) end it "loads a blank history if no history file" do - @config.load_history.should == [] + expect(@config.load_history).to eq([]) end it "loads the env file" do @config.env_file.write('abc') - @config.load_env.should == 'abc' + expect(@config.load_env).to eq('abc') end it "loads nothing if env file does not exist" do - @config.load_env.should == "" + expect(@config.load_env).to eq('') end it "loads the commands file" do @config.commands_file.write('abc') - @config.load_commands.should == 'abc' + expect(@config.load_commands).to eq('abc') end it "loads nothing if commands file does not exist" do - @config.load_commands.should == "" + expect(@config.load_commands).to eq('') end it "loads usernames and password for rushd" do system "echo 1:2 > #{@sandbox_dir}/passwords" system "echo a:b >> #{@sandbox_dir}/passwords" - @config.passwords.should == { '1' => '2', 'a' => 'b' } + expect(@config.passwords).to eq({ '1' => '2', 'a' => 'b' }) end it "loads blank hash if no passwords file" do - @config.passwords.should == { } + expect(@config.passwords).to eq({}) end it "loads credentials for client connecting to server" do system "echo user:pass > #{@sandbox_dir}/credentials" - @config.credentials_user.should == 'user' - @config.credentials_password.should == 'pass' + expect(@config.credentials_user).to eq 'user' + expect(@config.credentials_password).to eq 'pass' end it "loads list of ssh tunnels" do system "echo host.example.com:123 > #{@sandbox_dir}/tunnels" - @config.tunnels.should == { 'host.example.com' => 123 } + expect(@config.tunnels).to eq({ 'host.example.com' => 123 }) end it "returns an empty hash if tunnels file is blank" do - @config.tunnels.should == { } + expect(@config.tunnels).to eq({}) end it "saves a list of ssh tunnels" do @config.save_tunnels({ 'my.example.com' => 4000 }) - @config.tunnels_file.contents.should == "my.example.com:4000\n" + expect(@config.tunnels_file.contents).to eq "my.example.com:4000\n" end it "ensure_credentials_exist doesn't do anything if credentials already exist" do @config.credentials_file.write "dummy" - @config.should_receive(:generate_credentials).exactly(0).times + expect(@config).to receive(:generate_credentials).exactly(0).times @config.ensure_credentials_exist end it "ensure_credentials_exist generates credentials file if they don't exist" do - @config.should_receive(:generate_credentials) + expect(@config).to receive(:generate_credentials) @config.ensure_credentials_exist end it "secret_characters returns valid characters for username or password" do - @config.secret_characters.should be_kind_of(Array) + expect(@config.secret_characters).to be_kind_of(Array) end it "generate_secret products a random string for use in username and password" do - @config.should_receive(:secret_characters).and_return(%w(a)) - @config.generate_secret(2, 2).should == "aa" + expect(@config).to receive(:secret_characters).and_return(%w(a)) + expect(@config.generate_secret(2, 2)).to eq "aa" end it "generate_credentials saves credentials" do @config.generate_credentials - @config.credentials_file.contents.should match(/^.+:.+$/) + expect(@config.credentials_file.contents).to match(/^.+:.+$/) end end diff --git a/spec/dir_spec.rb b/spec/dir_spec.rb index 3fce5f3..f712e2a 100644 --- a/spec/dir_spec.rb +++ b/spec/dir_spec.rb @@ -16,98 +16,98 @@ end it "is a child of Rush::Entry" do - @dir.should be_kind_of(Rush::Entry) + expect(@dir).to be_kind_of(Rush::Entry) end it "can create itself, returning itself" do system "rm -rf #{@sandbox_dir}" - @dir.create.should == @dir - File.directory?(@dir.full_path).should be_true + expect(@dir.create).to eq @dir + expect(File.directory?(@dir.full_path)).to eq(true) end it "can create a new file" do newfile = @dir.create_file('one.txt') - newfile.name.should == 'one.txt' - newfile.parent.should == @dir + expect(newfile.name).to eq 'one.txt' + expect(newfile.parent).to eq @dir end it "can create a new subdir" do newfile = @dir['two/'].create - newfile.name.should == 'two' - newfile.parent.should == @dir + expect(newfile.name).to eq 'two' + expect(newfile.parent).to eq @dir end it "find_by_name finds a single entry in the contents" do file = @dir.create_file('one.rb') - @dir.find_by_name('one.rb').should == file + expect(@dir.find_by_name('one.rb')).to eq file end it "find_by_glob finds a list of entries by wildcard" do file1 = @dir.create_file('one.rb') file2 = @dir.create_file('two.txt') - @dir.find_by_glob('*.rb').should == [ file1 ] + expect(@dir.find_by_glob('*.rb')).to eq([file1]) end it "lists files" do @dir.create_file('a') - @dir.files.should == [ Rush::File.new("#{@dirname}/a") ] + expect(@dir.files).to eq([Rush::File.new("#{@dirname}/a")]) end it "lists dirs" do system "mkdir -p #{@dir.full_path}/b" - @dir.dirs.should == [ Rush::Dir.new("#{@dirname}/b") ] + expect(@dir.dirs).to eq([Rush::Dir.new("#{@dirname}/b")]) end it "lists combined files and dirs" do @dir['c'].create @dir['d/'].create - @dir.contents.size.should == 2 + expect(@dir.contents.size).to eq 2 end it "fetches the entry_tree of all contents recursively" do @dir['a/'].create['b/'].create['c'].create - @dir.entries_tree.should == @dir.make_entries(%w(a/ a/b/ a/b/c)) + expect(@dir.entries_tree).to eq @dir.make_entries(%w(a/ a/b/ a/b/c)) end it "maps [] to find_by_name" do - @dir.should_receive(:find_by_name).once + expect(@dir).to receive(:find_by_name).once @dir['one'] end it "maps [] with a wildcard character to find_by_glob" do - @dir.should_receive(:find_by_glob).once + expect(@dir).to receive(:find_by_glob).once @dir['*'] end it "can use symbols or strings for [] access" do - @dir.should_receive(:find_by_name).once.with('subdir') + expect(@dir).to receive(:find_by_name).once.with('subdir') @dir[:subdir] end it "[] can return a file that has yet to be created" do - @dir['not_yet'].class.should == Rush::File + expect(@dir['not_yet']).to be_kind_of Rush::File end it "makes a list of entries from an array of filenames" do @dir['a'].create @dir['b/c/'].create - @dir.make_entries(%w(a b/c)).should == [ @dir['a'], @dir['b/c'] ] + expect(@dir.make_entries(%w(a b/c))).to eq([ @dir['a'], @dir['b/c'] ]) end it "lists flattened files from all nested subdirectories" do @dir['1'].create @dir['2/3/'].create['4'].create @dir['a/b/c/'].create['d'].create - @dir.files_flattened.should == @dir.make_entries(%w(1 2/3/4 a/b/c/d)) + expect(@dir.files_flattened).to eq @dir.make_entries(%w(1 2/3/4 a/b/c/d)) end it "lists flattened dirs from all nested subdirectories" do @dir.create_dir('1/2') - @dir.dirs_flattened.should == @dir.make_entries(%w(1/ 1/2/)) + expect(@dir.dirs_flattened).to eq @dir.make_entries(%w(1/ 1/2/)) end it "** as a shortcut to flattened_files" do - @dir['**'].should == @dir.files_flattened + expect(@dir['**']).to eq @dir.files_flattened end it "**/ as a shortcut to flattened_files + regular globbing" do @@ -115,25 +115,25 @@ @dir.create_file('ignore.txt') @dir.create_dir('2').create_file('3.rb') @dir.create_dir('a/b').create_file('c.rb') - @dir['**/*.rb'].should == @dir.make_entries(%w(1.rb 2/3.rb a/b/c.rb)) + expect(@dir['**/*.rb']).to eq @dir.make_entries(%w(1.rb 2/3.rb a/b/c.rb)) end it "lists nonhidden files" do @dir.create_file('show') @dir.create_file('.dont_show') - @dir.nonhidden_files.should == @dir.make_entries(%(show)) + expect(@dir.nonhidden_files).to eq @dir.make_entries(%(show)) end it "lists nonhidden dirs" do @dir.create_dir('show') @dir.create_dir('.dont_show') - @dir.nonhidden_dirs.should == @dir.make_entries(%(show/)) + expect(@dir.nonhidden_dirs).to eq @dir.make_entries(%(show/)) end if !RUBY_PLATFORM.match(/darwin/) # doesn't work on OS X 'cause du switches are different it "knows its size in bytes, which includes its contents recursively" do @dir.create_file('a').write('1234') - @dir.size.should be(4096 + 4) + expect(@dir.size).to eq(4096 + 4) end end @@ -148,17 +148,16 @@ it "can run a bash command within itself" do system "echo test > #{@dir.full_path}/file" - @dir.bash("cat file").should == "test\n" + expect(@dir.bash("cat file")).to eq "test\n" end it "can run bash within directories with spaces" do @dir.create_dir('with space').create_file('file').write('test') - @dir["with space/"].bash("cat file").should == "test" + expect(@dir["with space/"].bash("cat file")).to eq "test" end it "passes bash options (e.g., :user) through to the box bash command" do - @dir.should_receive(:bash).with('cmd', :opt1 => 1, :opt2 => 2) + expect(@dir).to receive(:bash).with('cmd', :opt1 => 1, :opt2 => 2) @dir.bash('cmd', :opt1 => 1, :opt2 => 2) end - end From f797d01b60ef593d1381b3e08e26f70bcdc427f4 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 14 Sep 2014 15:13:33 +0400 Subject: [PATCH 056/119] Search results and string ext specs moved to expect syntax --- spec/search_results_spec.rb | 14 +++++++------- spec/string_ext_spec.rb | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/spec/search_results_spec.rb b/spec/search_results_spec.rb index 24ff947..3ab9a6e 100644 --- a/spec/search_results_spec.rb +++ b/spec/search_results_spec.rb @@ -8,37 +8,37 @@ it "returns its list of entries" do @results.add(@file, %w(a)) - @results.entries.should == [ @file ] + expect(@results.entries).to eq [ @file ] end it "only returns each entry once no matter how many line matches it has" do @results.add(@file, %w(a b)) - @results.entries.should == [ @file ] + expect(@results.entries).to eq [ @file ] end it "returns its list of matched lines" do @results.add(@file, %w(a b)) - @results.lines.should == %w(a b) + expect(@results.lines).to eq %w(a b) end it "returns all lines for each entry in a flattened form" do file2 = Rush::File.new("another file") @results.add(@file, %w(a b)) @results.add(file2, %w(c d)) - @results.lines.should == %w(a b c d) + expect(@results.lines).to eq %w(a b c d) end it "returns a hash of entries_with_lines" do @results.add(@file, %w(a)) - @results.entries_with_lines.should == { @file => %w(a) } + expect(@results.entries_with_lines).to eq({ @file => %w(a) }) end it "mixes in Commands to operate like a dir or entry array" do - @results.methods.include?(:search).should be_true + expect(@results.methods.include?(:search)).to eq(true) end it "mixes in Enumerable to behave like an array" do @results.add(@file, %w(a)) - @results.map { |e| e }.should == [ @file ] + expect(@results.map { |e| e }).to eq [ @file ] end end diff --git a/spec/string_ext_spec.rb b/spec/string_ext_spec.rb index 738c77e..3c938b3 100644 --- a/spec/string_ext_spec.rb +++ b/spec/string_ext_spec.rb @@ -6,18 +6,18 @@ end it "heads from the front of the string" do - @string.head(1).should == 'a' + expect(@string.head(1)).to eq 'a' end it "tails from the back of the string" do - @string.tail(1).should == 'c' + expect(@string.tail(1)).to eq 'c' end it "gives the whole string when head exceeds length" do - @string.head(999).should == @string + expect(@string.head(999)).to eq @string end it "gives the whole string when tail exceeds length" do - @string.tail(999).should == @string + expect(@string.tail(999)).to eq @string end end From e6015e9e456d6fbc4ee3866f8aa14e9e818d82b7 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Wed, 8 Oct 2014 20:04:45 +0400 Subject: [PATCH 057/119] Add completion for executables --- Gemfile | 1 + Gemfile.lock | 9 +++++++++ lib/rush/remote.rb | 2 +- lib/rush/shell.rb | 30 ++++++++++++++---------------- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Gemfile b/Gemfile index b3e6a1e..052c586 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,7 @@ gem 'coderay' gem 'net-ssh' group :development do + gem 'pry' gem 'rake' gem 'jeweler' gem 'rspec' diff --git a/Gemfile.lock b/Gemfile.lock index 8a02f1c..3622e12 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -32,10 +32,12 @@ GEM rdoc json (1.8.1) jwt (1.0.0) + method_source (0.8.2) mini_portile (0.6.0) multi_json (1.10.1) multi_xml (0.5.5) multipart-post (2.0.0) + net-ssh (2.9.1) nokogiri (1.6.3.1) mini_portile (= 0.6.0) oauth2 (1.0.0) @@ -44,6 +46,10 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (~> 1.2) + pry (0.10.1) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) rack (1.5.2) rake (10.3.2) rdoc (4.1.2) @@ -61,6 +67,7 @@ GEM rspec-support (~> 3.1.0) rspec-support (3.1.0) session (3.2.0) + slop (3.6.0) thread_safe (0.3.4) PLATFORMS @@ -70,6 +77,8 @@ DEPENDENCIES coderay coolline jeweler + net-ssh + pry rake rspec session diff --git a/lib/rush/remote.rb b/lib/rush/remote.rb index f823c4a..65a745a 100644 --- a/lib/rush/remote.rb +++ b/lib/rush/remote.rb @@ -9,7 +9,7 @@ class Rush::Connection::Remote attr_reader :host, :tunnel - def initialize(host, user) + def initialize(host, user = nil) @host = host @tunnel = Rush::SshTunnel.new(host, user) end diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index b7f2eab..1bb52dc 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -1,5 +1,6 @@ require 'coolline' require 'coderay' +require 'pp' # Rush::Shell is used to create an interactive shell. It is invoked by the rush binary. module Rush @@ -78,7 +79,6 @@ class << self # Save history to ~/.rush/history when the shell exists. def finish - @config.save_history(@history.to_a) puts exit end @@ -92,20 +92,7 @@ def print_result(res) output = res.to_s << "#{res.entries.size} matching files with #{res.lines.size} matching lines" elsif res.respond_to? :each - output = '' - counts = res.inject(Hash.new(0)) do |result, item| - output << item.to_s << "\n" - result[item.class] += 1 - result - end - if counts == {} - output = "=> (empty set)" - else - count_s = counts.map do |klass, count| - "#{count} x #{klass}" - end.join(', ') - output << "=> #{count_s}" - end + pp res else output = "=> #{res.inspect}" end @@ -121,6 +108,11 @@ def print_result(res) # # This isn't that cool yet, because it can't do multiple levels of subdirs. # It does work remotely, though, which is pretty sweet. + # + # TODO: + # 1. move to separate module + # 2. agressive refactor it + # def complete(input) receiver, accessor, *rest = path_parts(input) return [] unless receiver @@ -179,11 +171,17 @@ def complete_variable(partial_name, pre) ivars = eval('instance_variables', the_binding) mets = eval('methods', the_binding) || eval('Kernel.methods') consts = eval('Object.constants', the_binding) - (lvars + gvars + ivars + mets + consts). + (executables + lvars + gvars + ivars + mets + consts). select { |e| e.match(/^#{Regexp.escape(partial_name)}/) }. map { |e| (pre || '') + e.to_s } end + def executables + ENV['PATH'].split(':'). + map { |x| Rush::Dir.new(x).entries.map(&:name) }. + flatten + end + def syntax_highlight(input) CodeRay.encode input, :ruby, :term end From ea222cd4e57c08c673255dcd94b6deb5422bfe16 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 11 Oct 2014 23:35:37 +0400 Subject: [PATCH 058/119] [refs #9] Rewrite autocomplete system --- lib/rush/shell.rb | 124 +++++++---------------------------- lib/rush/shell/completion.rb | 110 +++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 101 deletions(-) create mode 100644 lib/rush/shell/completion.rb diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 1bb52dc..421192a 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -1,11 +1,16 @@ +require_relative 'shell/completion' require 'coolline' require 'coderay' require 'pp' -# Rush::Shell is used to create an interactive shell. It is invoked by the rush binary. module Rush + # Rush::Shell is used to create an interactive shell. + # It is invoked by the rush binary. + # class Shell - attr_accessor :suppress_output, :config, :history + include Rush::Completion + + attr_accessor :suppress_output, :config, :history, :pure_binding # Set up the user's environment, including a pure binding into which # env.rb and commands.rb are mixed. def initialize @@ -14,23 +19,20 @@ def initialize pwd = Rush::Dir.new(ENV['PWD']) if ENV['PWD'] @config = Rush::Config.new - @history = Coolline::History.new config.history_file.full_path - @readline = Coolline.new do |c| - c.transform_proc = proc { syntax_highlight c.line } + c.transform_proc = proc { syntax_highlight c.line } c.completion_proc = proc { complete c.completed_word } end @box = Rush::Box.new - @pure_binding = @box.instance_eval "binding" + @pure_binding = @box.instance_eval 'binding' $last_res = nil eval config.load_env, @pure_binding - commands = config.load_commands Rush::Dir.class_eval commands - Array.class_eval commands + Array.class_eval commands # Multiline commands should be stored somewhere @multiline_cmd = '' @@ -40,7 +42,7 @@ def initialize def execute(cmd) res = eval(@multiline_cmd << "\n" << cmd, @pure_binding) $last_res = res - eval("_ = $last_res", @pure_binding) + eval('_ = $last_res', @pure_binding) @multiline_cmd = '' print_result res rescue SyntaxError => e @@ -63,9 +65,8 @@ def run loop do prompt = self.class.prompt || "#{`whoami`.chomp} $ " cmd = @readline.readline prompt - - finish if cmd.nil? or cmd == 'exit' - next if cmd.empty? + finish if cmd.nil? || cmd == 'exit' + next if cmd.empty? @history << cmd execute cmd end @@ -84,104 +85,25 @@ def finish end # Nice printing of different return types, particularly Rush::SearchResults. + # def print_result(res) - return if self.suppress_output - if res.kind_of? String + return if suppress_output + if res.is_a? String output = res - elsif res.kind_of? Rush::SearchResults + elsif res.is_a? Rush::SearchResults output = res.to_s << - "#{res.entries.size} matching files with #{res.lines.size} matching lines" + "#{res.entries.size} matching files with #{res.lines.size} lines" elsif res.respond_to? :each - pp res + print ' = ' + output = pp(res) else - output = "=> #{res.inspect}" + output = " = #{res.inspect}" end - output.lines.count > 5 ? output.less : puts(output) + output.to_s.lines.count > 5 ? output.less : puts(output) end - # Try to do tab completion on dir square brackets and slash accessors. - # - # Example: + # Syntax highlighting with coderay. # - # dir['subd # presing tab here will produce dir['subdir/ if subdir exists - # dir/'subd # presing tab here will produce dir/'subdir/ if subdir exists - # - # This isn't that cool yet, because it can't do multiple levels of subdirs. - # It does work remotely, though, which is pretty sweet. - # - # TODO: - # 1. move to separate module - # 2. agressive refactor it - # - def complete(input) - receiver, accessor, *rest = path_parts(input) - return [] unless receiver - case accessor - when /^[\[\/]$/ then complete_path(receiver, accessor, *rest) - when /^\.$/ then complete_method(receiver, accessor, *rest) - when nil then complete_variable(receiver, *rest) - else [] - end - end - - # TODO: get string from space to last dot and do something sane with it. - # I just don't want to figure out what's going on there. - def path_parts(input) # :nodoc: - case input - when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)([\[\/])(['"])([^\3]*)$/ - $~.to_a.slice(1, 4).push($~.pre_match) - when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)(\.)(\w*)$/ - $~.to_a.slice(1, 3).push($~.pre_match) - when /((?:@{1,2}|\$|)\w+)$/ - $~.to_a.slice(1, 1).push(nil).push($~.pre_match) - else - [ nil, nil, nil ] - end - end - - def complete_method(receiver, dot, partial_name, pre) - path = eval("#{receiver}.full_path", @pure_binding) rescue nil - box = eval("#{receiver}.box", @pure_binding) rescue nil - return [] unless (path and box) - box[path].methods. # why box[path] ? - select { |e| e.match /^#{Regexp.escape(partial_name)}/ }. - map { |e| (pre || '') + receiver + dot + e.to_s } - end - - def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc: - original_var, fixed_path = possible_var, '' - if /^(.+\/)([^\/]*)$/ === partial_path - fixed_path, partial_path = $~.captures - possible_var += "['#{fixed_path}']" - end - full_path = eval("#{possible_var}.full_path", @pure_binding) rescue nil - box = eval("#{possible_var}.box", @pure_binding) rescue nil - return [] unless (full_path and box) - Rush::Dir.new(full_path, box).entries. - select { |e| e.name.match(/^#{Regexp.escape(partial_path)}/) }. - map { |e| (pre || '') + original_var + accessor + quote + - fixed_path + e.name + (e.dir? ? "/" : "") } - end - - def complete_variable(partial_name, pre) - pre = eval(pre, @pure_binding) rescue nil - the_binding = pre ? pre.instance_eval('binding') : @pure_binding - lvars = eval('local_variables', the_binding) - gvars = eval('global_variables', the_binding) - ivars = eval('instance_variables', the_binding) - mets = eval('methods', the_binding) || eval('Kernel.methods') - consts = eval('Object.constants', the_binding) - (executables + lvars + gvars + ivars + mets + consts). - select { |e| e.match(/^#{Regexp.escape(partial_name)}/) }. - map { |e| (pre || '') + e.to_s } - end - - def executables - ENV['PATH'].split(':'). - map { |x| Rush::Dir.new(x).entries.map(&:name) }. - flatten - end - def syntax_highlight(input) CodeRay.encode input, :ruby, :term end diff --git a/lib/rush/shell/completion.rb b/lib/rush/shell/completion.rb new file mode 100644 index 0000000..d6dbf65 --- /dev/null +++ b/lib/rush/shell/completion.rb @@ -0,0 +1,110 @@ +module Rush + # Paths, executables and methods copmletion module. + # + module Completion + # The complete method itself. + # @param input [String] part of line from last space/beginning of line. + # @returns [String] completed string. + # + # Types of completed lines: + # kernel constants, global variables, executables: + # CON -- constant; + # met -- method/variable name; + # execu -- executable file from PATH environment variable; + # given module/class constants: + # Object::CON -- constant of given module/class; + # methods: + # Object.met -- method of object; + # Object.method1.meth; + # variable.met; + # variable.method1.method2.met -- method number N in chain; + # paths: + # box['pa -- relative path with multiple lvls; + # box/'pa -- another syntax to relative path; + # box/'path/to/fi -- the same; + # + def complete(input) + TEMPLATES.values + .select { |x| x[:regexp].match input } + .map { |x| send x[:method], input } + .flatten + .compact + end + + TEMPLATES = { + constant: { + regexp: /[A-Z](\w|_)+/, + method: :complete_constant + }, + object_constant: { + regexp: /^([A-Z](\w|_)+::)+[A-Z](\w|_)+$/, + method: :complete_object_constant + }, + global_method: { + regexp: /^[a-z](\w|_)+$/, + method: :complete_global_method + }, + method: { + regexp: /^(((\w|_)+(\.|::))+)((\w|_)+)$/, + method: :complete_method + }, + path: { + regexp: /^(\w|_|.|:)+[\[\/][\'\"].+$/, + method: :complete_path + } + } + + def complete_constant(input) + Object.constants.map(&:to_s).select { |x| x.start_with? input } + end + + def complete_object_constant(input) + receiver, delimiter, const_part = *input.rpartition('::') + eval(receiver, pure_binding).constants + .map(&:to_s) + .select { |x| x.start_with? const_part } + .map { |x| receiver + delimiter + x } + end + + def complete_global_method(input) + complete_for(pure_binding, input) + end + + def complete_method(input) + receiver, delimiter, method_part = *input.rpartition('.') + the_binding = eval(receiver, pure_binding).instance_eval('binding') + complete_for(the_binding, method_part) + .map { |x| receiver + delimiter + x } + end + + def complete_for(the_binding, method_part) + lvars = eval('local_variables', the_binding) + gvars = eval('global_variables', the_binding) + ivars = eval('instance_variables', the_binding) + mets = eval('methods', the_binding) + (executables + lvars + gvars + ivars + mets) + .map(&:to_s) + .select { |e| e.start_with? method_part } + end + + def executables + ENV['PATH'].split(':') + .map { |x| Rush::Dir.new(x).entries.map(&:name) } + .flatten + end + + def complete_path(input) + delimiters = %w([' [" /' /") + delimiter = delimiters.find { |x| input.include? x } + object, _, path = *input.rpartition(delimiter) + path_done, _, path_part = path.rpartition('/') + return [] unless eval(object, pure_binding).class == Rush::Dir + box = eval(object + "/'" + path_done + "'", pure_binding) + box.entries + .map(&:name) + .select { |x| x.start_with? path_part } + .map { |x| object + delimiter + path_done + '/' + x } + end + end +end From 4935d530ebf0d2ba7c07e0fc0ccd2c1039583535 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 12 Oct 2014 07:37:23 +0400 Subject: [PATCH 059/119] Output should be pretty again --- lib/rush/shell.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 421192a..6dc704a 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -32,6 +32,7 @@ def initialize eval config.load_env, @pure_binding commands = config.load_commands Rush::Dir.class_eval commands + Rush::File.class_eval commands Array.class_eval commands # Multiline commands should be stored somewhere @@ -94,12 +95,11 @@ def print_result(res) output = res.to_s << "#{res.entries.size} matching files with #{res.lines.size} lines" elsif res.respond_to? :each - print ' = ' - output = pp(res) + output = res.pretty_inspect else output = " = #{res.inspect}" end - output.to_s.lines.count > 5 ? output.less : puts(output) + output.lines.count > 5 ? output.less : puts(output) end # Syntax highlighting with coderay. From 08cf1c1968b678c8b909a022557808e9b3c0f4f2 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 12 Oct 2014 07:38:59 +0400 Subject: [PATCH 060/119] Run executables without pain: my_file.firefox will open firefox --- lib/rush/entry.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/rush/entry.rb b/lib/rush/entry.rb index eac4394..21d4381 100644 --- a/lib/rush/entry.rb +++ b/lib/rush/entry.rb @@ -13,6 +13,20 @@ def initialize(full_path, box=nil) @box = box || Rush::Box.new('localhost') end + def method_missing(meth, *args, &block) + if executables.include? meth.to_s + open_with meth, *args + else + super + end + end + + def executables + ENV['PATH'].split(':') + .map { |x| Rush::Dir.new(x).entries.map(&:name) } + .flatten + end + # The factory checks to see if the full path has a trailing slash for # creating a Rush::Dir rather than the default Rush::File. def self.factory(full_path, box=nil) From b1da22e718197a421b4ffaf30be5ead616c49adb Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 12 Oct 2014 07:43:59 +0400 Subject: [PATCH 061/119] Update dependencies --- Gemfile.lock | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3622e12..61b62c2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,7 +4,8 @@ GEM addressable (2.3.6) builder (3.2.2) coderay (1.1.0) - coolline (0.4.4) + coolline (0.5.0) + unicode_utils (~> 1.4) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) diff-lcs (1.2.5) @@ -58,17 +59,18 @@ GEM rspec-core (~> 3.1.0) rspec-expectations (~> 3.1.0) rspec-mocks (~> 3.1.0) - rspec-core (3.1.2) + rspec-core (3.1.6) rspec-support (~> 3.1.0) - rspec-expectations (3.1.0) + rspec-expectations (3.1.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.1.0) - rspec-mocks (3.1.0) + rspec-mocks (3.1.3) rspec-support (~> 3.1.0) - rspec-support (3.1.0) + rspec-support (3.1.2) session (3.2.0) slop (3.6.0) thread_safe (0.3.4) + unicode_utils (1.4.0) PLATFORMS ruby From 3539dbae2ca68375d1b1ddc48d85cbe15fb76eb5 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 12 Oct 2014 08:13:02 +0400 Subject: [PATCH 062/119] [refs #9] Tests for completion --- spec/shell_spec.rb | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 07f8989..0bcec6b 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -6,18 +6,30 @@ @shell = Rush::Shell.new end - it "matches open path commands for readline tab completion" do - @shell.path_parts("dir['app").should == [ "dir", "[", "'", "app", "" ] - @shell.path_parts('dir/"app').should == [ "dir", "/", '"', "app", "" ] - end + it 'Complete constants' do + expect(@shell.complete('Obj')).to eq(["Object", "ObjectSpace"]) + expect(@shell.complete('Rush::Sh')).to eq(["Rush::Shell"]) + end - it "matches open path commands on globals for readline tab completion" do - @shell.path_parts("$dir['app").should == [ "$dir", "[", "'", "app", "" ] - @shell.path_parts('$dir/"app').should == [ "$dir", "/", '"', "app", "" ] - end + it 'Complete executables' do + expect(@shell.complete('rub')).to include 'ruby' + end - it "matches open path commands on instance vars for readline tab completion" do - @shell.path_parts("@dir['app").should == [ "@dir", "[", "'", "app", "" ] - @shell.path_parts('@dir/"app').should == [ "@dir", "/", '"', "app", "" ] - end + it 'Complete global method names' do + eval('def wakawaka; p "hi"; end', @shell.pure_binding) + expect(@shell.complete('waka')).to eq ["wakawaka"] + end + + it 'Complete method names' do + expect(@shell.complete('Rush.meth')). + to eq(["Rush.method_part", "Rush.method_defined?", "Rush.methods", "Rush.method"]) + expect(@shell.complete('Rush.methods.inc')).to eq ["Rush.methods.include?"] + end + + it 'Complete paths' do + expect(@shell.complete('root["bin/ba')).to eq ["root[\"bin/bash"] + expect(@shell.complete('root[\'bin/ba')).to eq ["root['bin/bash"] + expect(@shell.complete('root/"bin/ba')).to eq ["root/\"bin/bash"] + expect(@shell.complete('root/\'bin/ba')).to eq ["root/'bin/bash"] + end end From c53eee209491f9d07d70128acd7f9669a87d3b0b Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 12 Oct 2014 10:13:06 +0400 Subject: [PATCH 063/119] [refs #10] Use sshfs for remote connections --- lib/rush.rb | 1 - lib/rush/box.rb | 31 +++----- lib/rush/commands.rb | 2 +- lib/rush/remote.rb | 177 ++++++----------------------------------- lib/rush/ssh_tunnel.rb | 46 ----------- 5 files changed, 38 insertions(+), 219 deletions(-) delete mode 100644 lib/rush/ssh_tunnel.rb diff --git a/lib/rush.rb b/lib/rush.rb index 7d87a01..4983393 100644 --- a/lib/rush.rb +++ b/lib/rush.rb @@ -87,6 +87,5 @@ module Rush::Connection; end require_relative 'rush/process_set' require_relative 'rush/local' require_relative 'rush/remote' -require_relative 'rush/ssh_tunnel' require_relative 'rush/box' require_relative 'rush/embeddable_shell' diff --git a/lib/rush/box.rb b/lib/rush/box.rb index eece3a9..b786ab9 100644 --- a/lib/rush/box.rb +++ b/lib/rush/box.rb @@ -11,14 +11,15 @@ # local.processes # class Rush::Box - attr_reader :host + attr_reader :host, :local_path # Instantiate a box. No action is taken to make a connection until you try # to perform an action. If the box is remote, an ssh tunnel will be opened. # Specify a username with the host if the remote ssh user is different from # the local one (e.g. Rush::Box.new('user@host')). - def initialize(host='localhost') + def initialize(host='localhost', local_path = nil) @host = host + @local_path = local_path end def to_s # :nodoc: @@ -31,7 +32,11 @@ def inspect # :nodoc: # Access / on the box. def filesystem - Rush::Entry.factory('/', self) + if host == 'localhost' + Rush::Entry.factory('/', self) + else + connection.local_path + end end # Look up an entry on the filesystem, e.g. box['/path/to/some/file']. @@ -49,22 +54,10 @@ def processes ) end - # FAST HACK! - # Guess if method missing then it's bash command. - # The logic is the following: - # Ruby has Symbol type for similar things that bash has '-'-keys. - # Ruby also use Symbol for keys like '--key', but bash supports - # sequences like 'ls -lah' and I don't know how to distinguish them. - # - # Usage: - # ls :lah - # ls '--help' + # Guess if method missing then it's command for folder binded to that box. # - def method_missing(meth, *args) - command = args.map do |c| - Symbol === c ? c.to_s.chars.map { |x| '-' << x } : c - end.flatten.join(' ') - system meth.to_s << ' ' << command + def method_missing(meth, *args, &block) + filesystem.send(meth, *args, &block) end # Execute a command in the standard unix shell. Returns the contents of @@ -122,7 +115,7 @@ def connection # :nodoc: def make_connection # :nodoc: host == 'localhost' ? Rush::Connection::Local.new : - Rush::Connection::Remote.new(host) + Rush::Connection::Remote.new(host, local_path) end def ==(other) # :nodoc: diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index ddca174..82b6c5c 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -63,7 +63,7 @@ def open(*args) # Open file with any application you like. # Usage: - # home.locate('timetable').open_witn :vim + # home.locate('timetable').open_with :vim def open_with(app, *args) names = dir? ? '' : entries.map(&:to_s).join(' ') system "cd #{dirname}; #{app.to_s} #{names} #{args.join(' ')}" diff --git a/lib/rush/remote.rb b/lib/rush/remote.rb index 65a745a..997da27 100644 --- a/lib/rush/remote.rb +++ b/lib/rush/remote.rb @@ -1,160 +1,33 @@ -require 'yaml' - -# This class it the mirror of Rush::Connection::Local. A Rush::Box which is -# not localhost has a remote connection, which it can use to convert method -# calls to text suitable for transmission across the wire. -# -# This is an internal class that does not need to be accessed in normal use of -# the rush shell or library. -class Rush::Connection::Remote - attr_reader :host, :tunnel - - def initialize(host, user = nil) - @host = host - @tunnel = Rush::SshTunnel.new(host, user) - end - - def write_file(full_path, contents) - transmit(:action => 'write_file', :full_path => full_path, :payload => contents) - end - - def append_to_file(full_path, contents) - transmit(:action => 'append_to_file', :full_path => full_path, :payload => contents) - end - - def file_contents(full_path) - transmit(:action => 'file_contents', :full_path => full_path) - end - - def destroy(full_path) - transmit(:action => 'destroy', :full_path => full_path) - end - - def purge(full_path) - transmit(:action => 'purge', :full_path => full_path) - end - - def create_dir(full_path) - transmit(:action => 'create_dir', :full_path => full_path) - end - - def rename(path, name, new_name) - transmit(:action => 'rename', :path => path, :name => name, :new_name => 'new_name') - end - - def copy(src, dst) - transmit(:action => 'copy', :src => src, :dst => dst) - end - - def read_archive(full_path) - transmit(:action => 'read_archive', :full_path => full_path) - end - - def write_archive(archive, dir) - transmit(:action => 'write_archive', :dir => dir, :payload => archive) - end - - def index(base_path, glob) - transmit(:action => 'index', :base_path => base_path, :glob => glob).split("\n") - end - - def stat(full_path) - YAML.load(transmit(:action => 'stat', :full_path => full_path)) - end - - def set_access(full_path, access) - transmit access.to_hash.merge(:action => 'set_access', :full_path => full_path) - end - - def chown( full_path, user = nil, group = nil, options = {} ) - transmit(:action => 'chown', :full_path => full_path, :user => user, :group => group, :options => options) - end - - def size(full_path) - transmit(:action => 'size', :full_path => full_path).to_i - end - - def processes - YAML.load(transmit(:action => 'processes')) - end - - def process_alive(pid) - transmit(:action => 'process_alive', :pid => pid) - end - - def kill_process(pid, options={}) - transmit(:action => 'kill_process', :pid => pid, :payload => YAML.dump(options)) - end - - def bash(command, user, background, reset_environment) - transmit(:action => 'bash', :payload => command, :user => user, :background => background, :reset_environment => reset_environment) - end - - # Given a hash of parameters (converted by the method call on the connection - # object), send it across the wire to the RushServer listening on the other - # side. Uses http basic auth, with credentials fetched from the Rush::Config. - def transmit(params) - ensure_tunnel - - require 'net/http' - - payload = params.delete(:payload) - - uri = "/?" - params.each do |key, value| - uri += "#{key}=#{value}&" +require 'net/ssh' + +module Rush + # wrapper of command + # sshfs '-o idmap=user @: ' + # + class Connection::Remote + attr_reader :local_path, :full_remote_path, :remote_path, :remote_server, :remote_user + + def initialize(full_remote_path, local_path) + local_path = local_path.full_path if local_path.respond_to?(:full_path) + @full_remote_path = full_remote_path + @local_path = Rush::Dir.new(local_path) + @local_path.create unless @local_path.exists? + @remote_user, server_and_path = *full_remote_path.split('@', 2) + @remote_server, @remote_address = *server_and_path.split(':', 2) end - req = Net::HTTP::Post.new(uri) - req.basic_auth config.credentials_user, config.credentials_password - - Net::HTTP.start(tunnel.host, tunnel.port) do |http| - res = http.request(req, payload) - process_result(res.code, res.body) + def connect + system "sshfs -o idmap=user #{full_remote_path} #{local_path}" end - rescue EOFError - raise Rush::RushdNotRunning - end - - # Take the http result of a transmit and raise an error, or return the body - # of the result when valid. - def process_result(code, body) - raise Rush::NotAuthorized if code == "401" + alias_method :mount, :connect - if code == "400" - klass, message = parse_exception(body) - raise klass, "#{host}:#{message}" + def disconnect + system "fusermount -u #{local_path.full_path}" end + alias_method :umount, :disconnect - raise Rush::FailedTransmit if code != "200" - - body - end - - # Parse an exception returned from the server, with the class name on the - # first line and the message on the second. - def parse_exception(body) - klass, message = body.split("\n", 2) - raise "invalid exception class: #{klass}" unless klass.match(/^Rush::[A-Za-z]+$/) - klass = Object.module_eval(klass) - [ klass, message.strip ] - end - - # Set up the tunnel if it is not already running. - def ensure_tunnel(options={}) - tunnel.ensure_tunnel(options) - end - - # Remote connections are alive when the box on the other end is responding - # to commands. - def alive? - index('/', 'alive_check') - true - rescue - false - end - - def config - @config ||= Rush::Config.new + def method_missing(meth, *args, &block) + @local_path.send(meth, *args, &block) + end end end diff --git a/lib/rush/ssh_tunnel.rb b/lib/rush/ssh_tunnel.rb deleted file mode 100644 index a463c3e..0000000 --- a/lib/rush/ssh_tunnel.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'net/ssh' - -# Internal class for managing an ssh tunnel, across which relatively insecure -# -class Rush::SshTunnel - attr_reader :connection, :transport, :host, :user, :password - attr_accessor :config - - def initialize(host, user, password = nil) - @host = host - @user = user - @password = password - end - - def connect - @connection = establish_tunnel(host, user, password: password) - end - - def disconnect - connect.close - transport.close - end - - def establish_tunnel(host, user, options = {}) - options = { user: user, host_name: host, logger: Logger.new(STDERR) } - options[:logger].level = Logger::INFO - @transport = Net::SSH::Transport::Session.new(host, options) - auth = Net::SSH::Authentication::Session.new(transport, options) - args = ['ssh-connection', user, options.delete(:password)].reject(&:nil?) - if auth.authenticate(*args) - Net::SSH::Connection::Session.new(transport, options) - else - fail SshFailed, 'Authentication failed' - end - end - - def send(command) - connection.exec! command - end - - def config - @config ||= Rush::Config.new - end - - class SshFailed < StandardError; end -end From 91cc3de4199f51bb0a730851ff96eb9254165eb9 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 12 Oct 2014 10:14:42 +0400 Subject: [PATCH 064/119] Version bump to 0.7.0 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index fae59ca..bcaffe1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.8 +0.7.0 \ No newline at end of file From a68c76ee61028a15695f6c8d160183d8207584b2 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 12 Oct 2014 10:28:46 +0400 Subject: [PATCH 065/119] Update readme; release gem rush2 --- README.rdoc | 8 +++- Rakefile | 9 ++-- rush2.gemspec | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 rush2.gemspec diff --git a/README.rdoc b/README.rdoc index 29cb477..0ca3d84 100644 --- a/README.rdoc +++ b/README.rdoc @@ -11,6 +11,10 @@ Install v.0.6.8 from rubygems.org ~ no longer maintained sudo gem install rush +Install last stable version (yes, it named rush2 and I know that it's ugly, but still): + + sudo gem install rush2 + Or for the bleeding edge: @@ -117,9 +121,9 @@ Patches contributed by Chihiro Ito, Gabriel Ware, Michael Schutte, Ricardo Chima Logo by James Lindenbaum -Released under the MIT License: http://www.opensource.org/licenses/mit-license.php +Now maintained by Sergey Smagin. Ask me if something is unclear: smaginsergey1310@gmail.com -http://rush.heroku.com +Released under the MIT License: http://www.opensource.org/licenses/mit-license.php http://groups.google.com/group/ruby-shell diff --git a/Rakefile b/Rakefile index bdcaacb..623a14d 100644 --- a/Rakefile +++ b/Rakefile @@ -2,15 +2,14 @@ require 'rake' require 'jeweler' Jeweler::Tasks.new do |s| - s.name = "rush" + s.name = "rush2" s.summary = "A Ruby replacement for bash+ssh." s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." - s.author = "Adam Wiggins" - s.email = "adam@heroku.com" - s.homepage = "http://rush.heroku.com/" + s.author = "Sergey Smagin" + s.email = "smaginsergey1310@gmail.com" + s.homepage = "https://github.com/s-mage/rush" s.licenses = ['MIT'] s.executables = [ "rush", "rushd" ] - s.rubyforge_project = "ruby-shell" s.has_rdoc = true s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"] diff --git a/rush2.gemspec b/rush2.gemspec new file mode 100644 index 0000000..3c56bbb --- /dev/null +++ b/rush2.gemspec @@ -0,0 +1,111 @@ +# Generated by jeweler +# DO NOT EDIT THIS FILE DIRECTLY +# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' +# -*- encoding: utf-8 -*- +# stub: rush2 0.7.0 ruby lib + +Gem::Specification.new do |s| + s.name = "rush2" + s.version = "0.7.0" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib"] + s.authors = ["Sergey Smagin"] + s.date = "2014-10-12" + s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." + s.email = "smaginsergey1310@gmail.com" + s.executables = ["rush", "rushd"] + s.extra_rdoc_files = [ + "README.rdoc" + ] + s.files = [ + "Gemfile", + "Gemfile.lock", + "README.rdoc", + "Rakefile", + "VERSION", + "bin/rush", + "bin/rushd", + "lib/rush.rb", + "lib/rush/access.rb", + "lib/rush/array_ext.rb", + "lib/rush/box.rb", + "lib/rush/commands.rb", + "lib/rush/config.rb", + "lib/rush/dir.rb", + "lib/rush/embeddable_shell.rb", + "lib/rush/entry.rb", + "lib/rush/exceptions.rb", + "lib/rush/file.rb", + "lib/rush/find_by.rb", + "lib/rush/fixnum_ext.rb", + "lib/rush/head_tail.rb", + "lib/rush/local.rb", + "lib/rush/process.rb", + "lib/rush/process_set.rb", + "lib/rush/remote.rb", + "lib/rush/search_results.rb", + "lib/rush/shell.rb", + "lib/rush/shell/completion.rb", + "lib/rush/string_ext.rb", + "spec/access_spec.rb", + "spec/array_ext_spec.rb", + "spec/base.rb", + "spec/box_spec.rb", + "spec/commands_spec.rb", + "spec/config_spec.rb", + "spec/dir_spec.rb", + "spec/embeddable_shell_spec.rb", + "spec/entry_spec.rb", + "spec/file_spec.rb", + "spec/find_by_spec.rb", + "spec/fixnum_ext_spec.rb", + "spec/local_spec.rb", + "spec/process_set_spec.rb", + "spec/process_spec.rb", + "spec/remote_spec.rb", + "spec/rush_spec.rb", + "spec/search_results_spec.rb", + "spec/shell_spec.rb", + "spec/ssh_tunnel_spec.rb", + "spec/string_ext_spec.rb" + ] + s.homepage = "https://github.com/s-mage/rush" + s.licenses = ["MIT"] + s.rubygems_version = "2.2.2" + s.summary = "A Ruby replacement for bash+ssh." + + if s.respond_to? :specification_version then + s.specification_version = 4 + + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_runtime_dependency(%q, [">= 0"]) + s.add_runtime_dependency(%q, [">= 0"]) + s.add_runtime_dependency(%q, [">= 0"]) + s.add_runtime_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + else + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + end + else + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + end +end + From bda86ccbf638378efa67b47f1d3a636aedb7d86d Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Mon, 13 Oct 2014 19:48:04 +0400 Subject: [PATCH 066/119] Improve autocompletion with coolline settings --- lib/rush/shell.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 6dc704a..e0e7350 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -20,6 +20,8 @@ def initialize @config = Rush::Config.new @history = Coolline::History.new config.history_file.full_path + Coolline::Settings[:word_boundaries] = [' ', "\t"] + Coolline::Settings[:completion_word_boundaries] = [' ', "\t"] @readline = Coolline.new do |c| c.transform_proc = proc { syntax_highlight c.line } c.completion_proc = proc { complete c.completed_word } From b3536fe79a5ca4008611af58f38fe561d9d770d7 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Mon, 13 Oct 2014 19:50:30 +0400 Subject: [PATCH 067/119] Version bump to 0.7.1 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index bcaffe1..7deb86f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.0 \ No newline at end of file +0.7.1 \ No newline at end of file From e14cfd8e8c95eaaa6afebb7f2271d516d9a1359b Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Wed, 24 Dec 2014 23:52:16 +0300 Subject: [PATCH 068/119] Split shell initialization into several commands --- Gemfile.lock | 2 +- lib/rush/shell.rb | 18 +++++++++++------- rush2.gemspec | 6 +++--- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 61b62c2..7c4deee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -59,7 +59,7 @@ GEM rspec-core (~> 3.1.0) rspec-expectations (~> 3.1.0) rspec-mocks (~> 3.1.0) - rspec-core (3.1.6) + rspec-core (3.1.7) rspec-support (~> 3.1.0) rspec-expectations (3.1.2) diff-lcs (>= 1.2.0, < 2.0) diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index e0e7350..25a734d 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -19,6 +19,15 @@ def initialize pwd = Rush::Dir.new(ENV['PWD']) if ENV['PWD'] @config = Rush::Config.new + @box = Rush::Box.new + @pure_binding = @box.instance_eval 'binding' + $last_res = nil + load_custom_commands + set_readline + @multiline_cmd = '' # Multiline commands should be stored somewhere + end + + def set_readline @history = Coolline::History.new config.history_file.full_path Coolline::Settings[:word_boundaries] = [' ', "\t"] Coolline::Settings[:completion_word_boundaries] = [' ', "\t"] @@ -26,19 +35,14 @@ def initialize c.transform_proc = proc { syntax_highlight c.line } c.completion_proc = proc { complete c.completed_word } end + end - @box = Rush::Box.new - @pure_binding = @box.instance_eval 'binding' - $last_res = nil - + def load_custom_commands eval config.load_env, @pure_binding commands = config.load_commands Rush::Dir.class_eval commands Rush::File.class_eval commands Array.class_eval commands - - # Multiline commands should be stored somewhere - @multiline_cmd = '' end # Run a single command. diff --git a/rush2.gemspec b/rush2.gemspec index 3c56bbb..3fbd428 100644 --- a/rush2.gemspec +++ b/rush2.gemspec @@ -2,16 +2,16 @@ # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- -# stub: rush2 0.7.0 ruby lib +# stub: rush2 0.7.1 ruby lib Gem::Specification.new do |s| s.name = "rush2" - s.version = "0.7.0" + s.version = "0.7.1" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] s.authors = ["Sergey Smagin"] - s.date = "2014-10-12" + s.date = "2014-10-13" s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." s.email = "smaginsergey1310@gmail.com" s.executables = ["rush", "rushd"] From 85e66164fa82939e26cfd32254bdcb5b69db10ed Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Thu, 25 Dec 2014 16:30:04 +0300 Subject: [PATCH 069/119] Update dependencies --- Gemfile.lock | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7c4deee..d766fb7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,15 +12,15 @@ GEM faraday (0.9.0) multipart-post (>= 1.2, < 3) git (1.2.8) - github_api (0.12.1) + github_api (0.12.2) addressable (~> 2.3) descendants_tracker (~> 0.0.4) faraday (~> 0.8, < 0.10) - hashie (>= 3.2) + hashie (>= 3.3) multi_json (>= 1.7.5, < 2.0) nokogiri (~> 1.6.3) oauth2 - hashie (3.3.1) + hashie (3.3.2) highline (1.6.21) jeweler (2.0.1) builder @@ -31,16 +31,15 @@ GEM nokogiri (>= 1.5.10) rake rdoc - json (1.8.1) - jwt (1.0.0) + jwt (1.2.0) method_source (0.8.2) - mini_portile (0.6.0) + mini_portile (0.6.1) multi_json (1.10.1) multi_xml (0.5.5) multipart-post (2.0.0) net-ssh (2.9.1) - nokogiri (1.6.3.1) - mini_portile (= 0.6.0) + nokogiri (1.6.5) + mini_portile (~> 0.6.0) oauth2 (1.0.0) faraday (>= 0.8, < 0.10) jwt (~> 1.0) @@ -51,10 +50,9 @@ GEM coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - rack (1.5.2) - rake (10.3.2) - rdoc (4.1.2) - json (~> 1.4) + rack (1.6.0) + rake (10.4.2) + rdoc (4.2.0) rspec (3.1.0) rspec-core (~> 3.1.0) rspec-expectations (~> 3.1.0) From 99fd8b841ad64b38211bd3592fdea1d24d542d93 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Thu, 1 Jan 2015 19:40:28 +0300 Subject: [PATCH 070/119] Move more specs to new format --- lib/rush/shell.rb | 24 ++-- spec/access_spec.rb | 68 +++++----- spec/array_ext_spec.rb | 18 +-- spec/commands_spec.rb | 86 ++++++------ spec/embeddable_shell_spec.rb | 8 +- spec/find_by_spec.rb | 18 +-- spec/fixnum_ext_spec.rb | 6 +- spec/rush_spec.rb | 38 +++--- spec/ssh_tunnel_spec.rb | 244 +++++++++++++++++----------------- 9 files changed, 255 insertions(+), 255 deletions(-) diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 25a734d..822f120 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -45,6 +45,18 @@ def load_custom_commands Array.class_eval commands end + # Run the interactive shell using coolline. + def run + loop do + prompt = self.class.prompt || "#{`whoami`.chomp} $ " + cmd = @readline.readline prompt + finish if cmd.nil? || cmd == 'exit' + next if cmd.empty? + @history << cmd + execute cmd + end + end + # Run a single command. def execute(cmd) res = eval(@multiline_cmd << "\n" << cmd, @pure_binding) @@ -67,18 +79,6 @@ def execute(cmd) @multiline_cmd = '' end - # Run the interactive shell using coolline. - def run - loop do - prompt = self.class.prompt || "#{`whoami`.chomp} $ " - cmd = @readline.readline prompt - finish if cmd.nil? || cmd == 'exit' - next if cmd.empty? - @history << cmd - execute cmd - end - end - # Tune the prompt with # Rush::Shell.prompt = 'hey there! > ' class << self diff --git a/spec/access_spec.rb b/spec/access_spec.rb index 18ac4d2..9277288 100644 --- a/spec/access_spec.rb +++ b/spec/access_spec.rb @@ -14,55 +14,55 @@ end it "gets parts from a one-part symbol like :user" do - @access.parts_from(:user).should == %w(user) + expect(@access.parts_from(:user)).to eq(%w(user)) end it "gets parts from a two-part symbol like :read_write" do - @access.parts_from(:read_write).should == %w(read write) + expect(@access.parts_from(:read_write)).to eq(%w(read write)) end it "allows use of 'and' in multipart symbols, like :user_and_group" do - @access.parts_from(:user_and_group).should == %w(user group) + expect(@access.parts_from(:user_and_group)).to eq( %w(user group)) end it "extract_list verifies that all the parts among the valid choices" do - @access.should_receive(:parts_from).with(:red_green).and_return(%w(red green)) - @access.extract_list('type', :red_green, %w(red blue green)).should == %w(red green) + expect(@access).to receive(:parts_from).with(:red_green).and_return(%w(red green)) + expect(@access.extract_list('type', :red_green, %w(red blue green))).to eq(%w(red green)) end it "extract_list raises a BadAccessSpecifier when there is part not in the list of choices" do - lambda do + expect do @access.extract_list('role', :user_bork, %w(user group)) - end.should raise_error(Rush::BadAccessSpecifier, "Unrecognized role: bork") + end.to raise_error(Rush::BadAccessSpecifier, "Unrecognized role: bork") end it "sets one value in the matrix of permissions and roles" do @access.set_matrix(%w(read), %w(user)) - @access.user_can_read.should == true + expect(@access.user_can_read).to eq(true) end it "sets two values in the matrix of permissions and roles" do @access.set_matrix(%w(read), %w(user group)) - @access.user_can_read.should == true - @access.group_can_read.should == true + expect(@access.user_can_read).to eq(true) + expect(@access.group_can_read).to eq(true) end it "sets four values in the matrix of permissions and roles" do @access.set_matrix(%w(read write), %w(user group)) - @access.user_can_read.should == true - @access.group_can_read.should == true - @access.user_can_write.should == true - @access.group_can_write.should == true + expect(@access.user_can_read).to eq(true) + expect(@access.group_can_read).to eq(true) + expect(@access.user_can_write).to eq(true) + expect(@access.group_can_write).to eq(true) end it "parse options hash" do @access.parse(:user_can => :read) - @access.user_can_read.should == true + expect(@access.user_can_read).to eq(true) end it "generates octal permissions from its member vars" do @access.user_can_read = true - @access.octal_permissions.should == 0400 + expect(@access.octal_permissions).to eq(0400) end it "generates octal permissions from its member vars" do @@ -71,7 +71,7 @@ @access.user_can_execute = true @access.group_can_read = true @access.group_can_execute = true - @access.octal_permissions.should == 0750 + expect(@access.octal_permissions).to eq(0750) end it "applies its settings to a file" do @@ -80,7 +80,7 @@ system "rm -rf #{file}; touch #{file}; chmod 770 #{file}" @access.user_can_read = true @access.apply(file) - `ls -l #{file}`.should match(/^-r--------/) + expect(`ls -l #{file}`).to match(/^-r--------/) ensure system "rm -rf #{file}; touch #{file}" end @@ -88,47 +88,47 @@ it "serializes itself to a hash" do @access.user_can_read = true - @access.to_hash.should == { + expect(@access.to_hash).to eq({ :user_can_read => 1, :user_can_write => 0, :user_can_execute => 0, :group_can_read => 0, :group_can_write => 0, :group_can_execute => 0, :other_can_read => 0, :other_can_write => 0, :other_can_execute => 0, - } + }) end it "unserializes from a hash" do @access.from_hash(:user_can_read => '1') - @access.user_can_read.should == true + expect(@access.user_can_read).to eq(true) end it "initializes from a serialized hash" do - @access.class.should_receive(:new).and_return(@access) - @access.class.from_hash(:user_can_read => '1').should == @access - @access.user_can_read.should == true + expect(@access.class).to receive(:new).and_return(@access) + expect(@access.class.from_hash(:user_can_read => '1')).to eq(@access) + expect(@access.user_can_read).to eq(true) end it "initializes from a parsed options hash" do - @access.class.should_receive(:new).and_return(@access) - @access.class.parse(:user_and_group_can => :read).should == @access - @access.user_can_read.should == true + expect(@access.class).to receive(:new).and_return(@access) + expect(@access.class.parse(:user_and_group_can => :read)).to eq(@access) + expect(@access.user_can_read).to eq(true) end it "converts and octal integer into an array of integers" do - @access.octal_integer_array(0740).should == [ 7, 4, 0 ] + expect(@access.octal_integer_array(0740)).to eq([ 7, 4, 0 ]) end it "filters out anything above the top three digits (File.stat returns some extra data there)" do - @access.octal_integer_array(0100644).should == [ 6, 4, 4 ] + expect(@access.octal_integer_array(0100644)).to eq([ 6, 4, 4 ]) end it "taskes permissions from an octal representation" do @access.from_octal(0644) - @access.user_can_read.should == true - @access.user_can_write.should == true - @access.user_can_execute.should == false + expect(@access.user_can_read).to eq(true) + expect(@access.user_can_write).to eq(true) + expect(@access.user_can_execute).to eq(false) end it "computes a display hash by dropping false keys and converting the 1s to trues" do - @access.should_receive(:to_hash).and_return(:red => 1, :green => 0, :blue => 1) - @access.display_hash.should == { :red => true, :blue => true } + expect(@access).to receive(:to_hash).and_return(:red => 1, :green => 0, :blue => 1) + expect(@access.display_hash).to eq({ :red => true, :blue => true }) end end diff --git a/spec/array_ext_spec.rb b/spec/array_ext_spec.rb index d050d72..c00c01a 100644 --- a/spec/array_ext_spec.rb +++ b/spec/array_ext_spec.rb @@ -1,15 +1,15 @@ require_relative 'base' describe Array do - it "mixes commands into array" do - [ 1,2,3 ].entries.should == [ 1, 2, 3 ] - end + it 'mixes commands into array' do + expect([1, 2, 3].entries).to eq([1, 2, 3]) + end - it "can call head" do - [ 1,2,3 ].head(1).should == [ 1 ] - end + it 'can call head' do + expect([1, 2, 3].head(1)).to eq([1]) + end - it "can call tail" do - [ 1,2,3 ].tail(1).should == [ 3 ] - end + it 'can call tail' do + expect([1, 2, 3].tail(1)).to eq([3]) + end end diff --git a/spec/commands_spec.rb b/spec/commands_spec.rb index fd2ea5f..2a7d8bc 100644 --- a/spec/commands_spec.rb +++ b/spec/commands_spec.rb @@ -1,47 +1,47 @@ require_relative 'base' describe Rush::Commands do - before do - @sandbox_dir = "/tmp/rush_spec.#{Process.pid}" - system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}" - - @filename = "test_file" - system "echo thing_to_find > #{@sandbox_dir}/#{@filename}" - system "echo dont_find_me > #{@sandbox_dir}/some_other_file" - - @dir = Rush::Dir.new(@sandbox_dir) - @array = @dir.files - end - - after do - system "rm -rf #{@sandbox_dir}" - end - - it "searches a list of files" do - results = @dir.files.search(/thing_to_find/) - results.should be_kind_of(Rush::SearchResults) - results.entries.should == [ @dir[@filename] ] - results.lines.should == [ "thing_to_find" ] - end - - it "searches a dir" do - @dir.search(/thing_to_find/).entries.should == [ @dir[@filename] ] - end - - it "searchs a dir's nested files" do - @dir.create_dir('sub').create_file('file').write('nested') - @dir['**'].search(/nested/).entries.should == [ @dir['sub/file'] ] - end - - it "search and replace contents on all files in the glob" do - @dir['1'].create.write('xax') - @dir['2'].create.write('-a-') - @dir.replace_contents!(/a/, 'b') - @dir['1'].contents.should == "xbx" - @dir['2'].contents.should == "-b-" - end - - it "counts lines of the contained files" do - @dir.files.line_count.should == 2 - end + before do + @sandbox_dir = "/tmp/rush_spec.#{Process.pid}" + system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}" + + @filename = 'test_file' + system "echo thing_to_find > #{@sandbox_dir}/#{@filename}" + system "echo dont_find_me > #{@sandbox_dir}/some_other_file" + + @dir = Rush::Dir.new(@sandbox_dir) + @array = @dir.files + end + + after do + system "rm -rf #{@sandbox_dir}" + end + + it 'searches a list of files' do + results = @dir.files.search(/thing_to_find/) + expect(results).to be_kind_of(Rush::SearchResults) + expect(results.entries).to eq [@dir[@filename]] + expect(results.lines).to eq ['thing_to_find'] + end + + it 'searches a dir' do + expect(@dir.search(/thing_to_find/).entries).to eq [@dir[@filename]] + end + + it 'searchs a dir\'s nested files' do + @dir.create_dir('sub').create_file('file').write('nested') + expect(@dir['**'].search(/nested/).entries).to eq [@dir['sub/file']] + end + + it 'search and replace contents on all files in the glob' do + @dir['1'].create.write('xax') + @dir['2'].create.write('-a-') + @dir.replace_contents!(/a/, 'b') + expect(@dir['1'].contents).to eq 'xbx' + expect(@dir['2'].contents).to eq '-b-' + end + + it 'counts lines of the contained files' do + expect(@dir.files.line_count).to eq 2 + end end diff --git a/spec/embeddable_shell_spec.rb b/spec/embeddable_shell_spec.rb index 95f7253..97ccf81 100644 --- a/spec/embeddable_shell_spec.rb +++ b/spec/embeddable_shell_spec.rb @@ -6,12 +6,12 @@ end it "should execute unknown methods against a Rush::Shell instance" do - @shell.root.class.should == Rush::Dir + expect(@shell.root.class).to eq(Rush::Dir) end it "should executes a block as if it were inside the shell" do - @shell.execute_in_shell { - root.class.should == Rush::Dir - } + expect(@shell.execute_in_shell { + root.class + }).to eq Rush::Dir end end diff --git a/spec/find_by_spec.rb b/spec/find_by_spec.rb index 29ffdf7..2a8c7eb 100644 --- a/spec/find_by_spec.rb +++ b/spec/find_by_spec.rb @@ -17,35 +17,35 @@ def initialize(bar) end it "compare_or_match exact match success" do - @list.compare_or_match('1', '1').should == true + expect(@list.compare_or_match('1', '1')).to eq true end it "compare_or_match exact match failure" do - @list.compare_or_match('1', '2').should == false + expect(@list.compare_or_match('1', '2')).to eq false end it "compare_or_match regexp match success" do - @list.compare_or_match('123', /2/).should == true + expect(@list.compare_or_match('123', /2/)).to eq true end it "compare_or_match regexp match failure" do - @list.compare_or_match('123', /x/).should == false + expect(@list.compare_or_match('123', /x/)).to eq false end it "find_by_ extact match" do - @list.find_by_bar('two').should == @two + expect(@list.find_by_bar('two')).to eq @two end it "find_by_ regexp match" do - @list.find_by_bar(/.hree/).should == @three + expect(@list.find_by_bar(/.hree/)).to eq @three end it "find_all_by_ exact match" do - @list.find_all_by_bar('one').should == [ @one ] + expect(@list.find_all_by_bar('one')).to eq [ @one ] end it "find_all_by_ regexp match" do - @list.find_all_by_bar(/^...$/).should == [ @one, @two ] + expect(@list.find_all_by_bar(/^...$/)).to eq [ @one, @two ] end it "find_by_ with field not recognized by objects raises no errors" do @@ -53,6 +53,6 @@ def initialize(bar) end it "raises NoMethodError for things other than find_by" do - lambda { @list.does_not_exist }.should raise_error(NoMethodError) + expect { @list.does_not_exist }.to raise_error(NoMethodError) end end diff --git a/spec/fixnum_ext_spec.rb b/spec/fixnum_ext_spec.rb index 04d8cb2..b0b24a8 100644 --- a/spec/fixnum_ext_spec.rb +++ b/spec/fixnum_ext_spec.rb @@ -6,14 +6,14 @@ end it "counts kb" do - @num.kb.should == 2*1024 + expect(@num.kb).to eq 2*1024 end it "counts mb" do - @num.mb.should == 2*1024*1024 + expect(@num.mb).to eq 2*1024*1024 end it "counts gb" do - @num.gb.should == 2*1024*1024*1024 + expect(@num.gb).to eq 2*1024*1024*1024 end end diff --git a/spec/rush_spec.rb b/spec/rush_spec.rb index dfa45d5..ef05146 100644 --- a/spec/rush_spec.rb +++ b/spec/rush_spec.rb @@ -1,28 +1,28 @@ require_relative 'base' describe Rush do - it "fetches a local file path" do - Rush['/etc/hosts'].full_path.should == '/etc/hosts' - end + it 'fetches a local file path' do + expect(Rush['/etc/hosts'].full_path).to eq('/etc/hosts') + end - it "fetches the dir of __FILE__" do - Rush.dir(__FILE__).name.should == 'spec' - end + it 'fetches the dir of __FILE__' do + expect(Rush.dir(__FILE__).name).to eq('spec') + end - it "fetches the launch dir (aka current working directory or pwd)" do - Dir.stub(:pwd).and_return('/tmp') - Rush.launch_dir.should == Rush::Box.new['/tmp/'] - end + it 'fetches the launch dir (aka current working directory or pwd)' do + Dir.stub(:pwd).and_return('/tmp') + expect(Rush.launch_dir).to eq(Rush::Box.new['/tmp/']) + end - it "runs a bash command" do - Rush.bash('echo hi').should == "hi\n" - end + it 'runs a bash command' do + expect(Rush.bash('echo hi')).to eq("hi\n") + end - it "gets the list of local processes" do - Rush.processes.should be_kind_of(Rush::ProcessSet) - end + it 'gets the list of local processes' do + expect(Rush.processes).to be_kind_of(Rush::ProcessSet) + end - it "gets my process" do - Rush.my_process.pid.should == Process.pid - end + it 'gets my process' do + expect(Rush.my_process.pid).to eq(Process.pid) + end end diff --git a/spec/ssh_tunnel_spec.rb b/spec/ssh_tunnel_spec.rb index 96f2d49..87b3bd4 100644 --- a/spec/ssh_tunnel_spec.rb +++ b/spec/ssh_tunnel_spec.rb @@ -1,122 +1,122 @@ -require_relative 'base' - -describe Rush::SshTunnel do - before do - @tunnel = Rush::SshTunnel.new('spec.example.com') - @tunnel.stub(:config).and_return(mock_config_start) - @tunnel.stub(:display) - end - - after do - mock_config_cleanup - end - - it "ensure_tunnel sets everything up for the tunnel when one does not already exist" do - @tunnel.should_receive(:push_credentials) - @tunnel.should_receive(:launch_rushd) - @tunnel.should_receive(:establish_tunnel) - @tunnel.ensure_tunnel - end - - it "ensure_tunnel uses the existing port as long as the tunnel is still alive" do - @tunnel.should_receive(:tunnel_alive?).and_return(true) - @tunnel.instance_eval("@port = 2345") - @tunnel.ensure_tunnel - @tunnel.port.should == 2345 - end - - it "existing tunnel is used when it is specified in the tunnels file" do - @tunnel.config.tunnels_file.write "spec.example.com:4567\n" - @tunnel.should_receive(:tunnel_alive?).and_return(true) - @tunnel.should_not_receive(:setup_everything) - @tunnel.ensure_tunnel - @tunnel.port.should == 4567 - end - - it "tunnel host is always local" do - @tunnel.host.should == 'localhost' - end - - it "picks the first port number when there are no tunnels yet" do - @tunnel.next_available_port.should == 7771 - end - - it "picks the next port number when there is already a tunnel" do - @tunnel.config.tunnels_file.write("#{@tunnel.host}:7771") - @tunnel.next_available_port.should == 7772 - end - - it "establishes a tunnel and saves it to ~/.rush/tunnels" do - @tunnel.should_receive(:make_ssh_tunnel) - @tunnel.should_receive(:port).exactly(0).times - @tunnel.establish_tunnel - @tunnel.config.tunnels_file.contents.should == "spec.example.com:7771\n" - end - - it "converts instance vars to options hash for ssh_tunnel_command" do - @tunnel.instance_eval("@port = 1234") - @tunnel.tunnel_options.should == { - :local_port => 1234, - :remote_port => 7770, - :ssh_host => 'spec.example.com' - } - end - - it "ssh_stall_command uses an infinite loop for :timeout => :infinite" do - @tunnel.ssh_stall_command(:timeout => :infinite).should match(/while .* sleep .* done/) - end - - it "ssh_stall_command sleeps for the number of seconds given as the :timeout option" do - @tunnel.ssh_stall_command(:timeout => 123).should == "sleep 123" - end - - it "ssh_stall_command uses the default timeout when no options are given" do - @tunnel.ssh_stall_command.should == "sleep 9000" - end - - it "constructs the ssh tunnel command (everything but stall) from the options hash" do - @tunnel.should_receive(:tunnel_options).at_least(:once).and_return( - :local_port => 123, - :remote_port => 456, - :ssh_host => 'example.com' - ) - @tunnel.ssh_tunnel_command_without_stall.should == "ssh -f -L 123:127.0.0.1:456 example.com" - end - - it "combines the tunnel command without stall and the stall command into the final command" do - @tunnel.should_receive(:ssh_tunnel_command_without_stall).and_return('ssh command') - @tunnel.should_receive(:ssh_stall_command).and_return('sleep 123') - @tunnel.ssh_tunnel_command.should == 'ssh command "sleep 123"' - end - - it "ssh_tunnel_command request that the port be set" do - @tunnel.should_receive(:tunnel_options).at_least(:once).and_return(:local_port => nil) - lambda { @tunnel.ssh_tunnel_command }.should raise_error(Rush::SshTunnel::NoPortSelectedYet) - end - - - it "push_credentials uses ssh to append to remote host's passwords file" do - @tunnel.should_receive(:ssh_append_to_credentials).and_return(true) - @tunnel.push_credentials - end - - it "launches rushd on the remote host via ssh" do - @tunnel.should_receive(:ssh) do |cmd| - cmd.should match(/rushd/) - end - @tunnel.launch_rushd - end - - it "tunnel_alive? checks whether a tunnel is still up" do - @tunnel.should_receive(:tunnel_count_command).and_return("echo 1") - @tunnel.tunnel_alive?.should be_true - end - - it "tunnel_count_command greps ps to find the ssh tunnel" do - @tunnel.should_receive(:ssh_tunnel_command_without_stall).and_return('ssh command') - command = @tunnel.tunnel_count_command - command.should match(/ps/) - command.should match(/grep/) - command.should match(/ssh command/) - end -end +# require_relative 'base' +# +# describe Rush::SshTunnel do +# before do +# @tunnel = Rush::SshTunnel.new('spec.example.com') +# @tunnel.stub(:config).and_return(mock_config_start) +# @tunnel.stub(:display) +# end +# +# after do +# mock_config_cleanup +# end +# +# it "ensure_tunnel sets everything up for the tunnel when one does not already exist" do +# @tunnel.should_receive(:push_credentials) +# @tunnel.should_receive(:launch_rushd) +# @tunnel.should_receive(:establish_tunnel) +# @tunnel.ensure_tunnel +# end +# +# it "ensure_tunnel uses the existing port as long as the tunnel is still alive" do +# @tunnel.should_receive(:tunnel_alive?).and_return(true) +# @tunnel.instance_eval("@port = 2345") +# @tunnel.ensure_tunnel +# @tunnel.port.should == 2345 +# end +# +# it "existing tunnel is used when it is specified in the tunnels file" do +# @tunnel.config.tunnels_file.write "spec.example.com:4567\n" +# @tunnel.should_receive(:tunnel_alive?).and_return(true) +# @tunnel.should_not_receive(:setup_everything) +# @tunnel.ensure_tunnel +# @tunnel.port.should == 4567 +# end +# +# it "tunnel host is always local" do +# @tunnel.host.should == 'localhost' +# end +# +# it "picks the first port number when there are no tunnels yet" do +# @tunnel.next_available_port.should == 7771 +# end +# +# it "picks the next port number when there is already a tunnel" do +# @tunnel.config.tunnels_file.write("#{@tunnel.host}:7771") +# @tunnel.next_available_port.should == 7772 +# end +# +# it "establishes a tunnel and saves it to ~/.rush/tunnels" do +# @tunnel.should_receive(:make_ssh_tunnel) +# @tunnel.should_receive(:port).exactly(0).times +# @tunnel.establish_tunnel +# @tunnel.config.tunnels_file.contents.should == "spec.example.com:7771\n" +# end +# +# it "converts instance vars to options hash for ssh_tunnel_command" do +# @tunnel.instance_eval("@port = 1234") +# @tunnel.tunnel_options.should == { +# :local_port => 1234, +# :remote_port => 7770, +# :ssh_host => 'spec.example.com' +# } +# end +# +# it "ssh_stall_command uses an infinite loop for :timeout => :infinite" do +# @tunnel.ssh_stall_command(:timeout => :infinite).should match(/while .* sleep .* done/) +# end +# +# it "ssh_stall_command sleeps for the number of seconds given as the :timeout option" do +# @tunnel.ssh_stall_command(:timeout => 123).should == "sleep 123" +# end +# +# it "ssh_stall_command uses the default timeout when no options are given" do +# @tunnel.ssh_stall_command.should == "sleep 9000" +# end +# +# it "constructs the ssh tunnel command (everything but stall) from the options hash" do +# @tunnel.should_receive(:tunnel_options).at_least(:once).and_return( +# :local_port => 123, +# :remote_port => 456, +# :ssh_host => 'example.com' +# ) +# @tunnel.ssh_tunnel_command_without_stall.should == "ssh -f -L 123:127.0.0.1:456 example.com" +# end +# +# it "combines the tunnel command without stall and the stall command into the final command" do +# @tunnel.should_receive(:ssh_tunnel_command_without_stall).and_return('ssh command') +# @tunnel.should_receive(:ssh_stall_command).and_return('sleep 123') +# @tunnel.ssh_tunnel_command.should == 'ssh command "sleep 123"' +# end +# +# it "ssh_tunnel_command request that the port be set" do +# @tunnel.should_receive(:tunnel_options).at_least(:once).and_return(:local_port => nil) +# lambda { @tunnel.ssh_tunnel_command }.should raise_error(Rush::SshTunnel::NoPortSelectedYet) +# end +# +# +# it "push_credentials uses ssh to append to remote host's passwords file" do +# @tunnel.should_receive(:ssh_append_to_credentials).and_return(true) +# @tunnel.push_credentials +# end +# +# it "launches rushd on the remote host via ssh" do +# @tunnel.should_receive(:ssh) do |cmd| +# cmd.should match(/rushd/) +# end +# @tunnel.launch_rushd +# end +# +# it "tunnel_alive? checks whether a tunnel is still up" do +# @tunnel.should_receive(:tunnel_count_command).and_return("echo 1") +# @tunnel.tunnel_alive?.should be_true +# end +# +# it "tunnel_count_command greps ps to find the ssh tunnel" do +# @tunnel.should_receive(:ssh_tunnel_command_without_stall).and_return('ssh command') +# command = @tunnel.tunnel_count_command +# command.should match(/ps/) +# command.should match(/grep/) +# command.should match(/ssh command/) +# end +# end From c3f3d8da2cc23753f7988c40235443e13a67bfab Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Tue, 6 Jan 2015 23:58:31 +0300 Subject: [PATCH 071/119] Moving specs to new syntax -- box_spec --- spec/box_spec.rb | 144 +++++++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/spec/box_spec.rb b/spec/box_spec.rb index df34086..acaddfc 100644 --- a/spec/box_spec.rb +++ b/spec/box_spec.rb @@ -1,76 +1,76 @@ require_relative 'base' describe Rush::Box do - before do - @sandbox_dir = "/tmp/rush_spec.#{Process.pid}" - system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}" - - @box = Rush::Box.new('localhost') - end - - after do - system "rm -rf #{@sandbox_dir}" - end - - it "looks up entries with [] syntax" do - @box['/'].should == Rush::Dir.new('/', @box) - end - - it "looks up processes" do - @box.connection.should_receive(:processes).and_return([ { :pid => 123 } ]) - @box.processes.should == [ Rush::Process.new({ :pid => 123 }, @box) ] - end - - it "executes bash commands" do - @box.connection.should_receive(:bash).with('cmd', nil, false, false).and_return('output') - @box.bash('cmd').should == 'output' - end - - it "executes bash commands with an optional user" do - @box.connection.should_receive(:bash).with('cmd', 'user', false, false) - @box.bash('cmd', :user => 'user') - end - - it "executes bash commands in the background, returning a Rush::Process" do - @box.connection.should_receive(:bash).with('cmd', nil, true, false).and_return(123) - @box.stub(:processes).and_return([ double('ps', :pid => 123) ]) - @box.bash('cmd', :background => true).pid.should == 123 - end - - it "builds a script of environment variables to prefix the bash command" do - @box.command_with_environment('cmd', { :a => 'b' }).should == "export a=\"b\"\ncmd" - end - - it "escapes quotes on environment variables" do - @box.command_with_environment('cmd', { :a => 'a"b' }).should == "export a=\"a\\\"b\"\ncmd" - end - - it "escapes backticks on environment variables" do - @box.command_with_environment('cmd', { :a => 'a`b' }).should == "export a=\"a\\\`b\"\ncmd" - end - - it "converts environment variables to_s" do - @box.command_with_environment('cmd', { :a => nil, :b => 123 }).should == "export a=\"\"\nexport b=\"123\"\ncmd" - end - - it "sets the environment variables from the provided hash" do - @box.connection.stub(:bash) - @box.should_receive(:command_with_environment).with('cmd', { 1 => 2 }) - @box.bash('cmd', :env => { 1 => 2 }) - end - - it "checks the connection to determine if it is alive" do - @box.connection.should_receive(:alive?).and_return(true) - @box.should be_alive - end - - it "establish_connection to set up the connection manually" do - @box.connection.should_receive(:ensure_tunnel) - @box.establish_connection - end - - it "establish_connection can take a hash of options" do - @box.connection.should_receive(:ensure_tunnel).with(:timeout => :infinite) - @box.establish_connection(:timeout => :infinite) - end + before do + @sandbox_dir = "/tmp/rush_spec.#{Process.pid}" + system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}" + + @box = Rush::Box.new('localhost') + end + + after do + system "rm -rf #{@sandbox_dir}" + end + + it "looks up entries with [] syntax" do + expect(@box['/']).to eq Rush::Dir.new('/', @box) + end + + it "looks up processes" do + expect(@box.connection).to receive(:processes).and_return([{ :pid => 123 }]) + expect(@box.processes).to eq [Rush::Process.new({ :pid => 123 }, @box)] + end + + it "executes bash commands" do + expect(@box.connection).to receive(:bash).with('cmd', nil, false, false).and_return('output') + expect(@box.bash('cmd')).to eq 'output' + end + + it "executes bash commands with an optional user" do + expect(@box.connection).to receive(:bash).with('cmd', 'user', false, false) + @box.bash('cmd', :user => 'user') + end + + it "executes bash commands in the background, returning a Rush::Process" do + expect(@box.connection).to receive(:bash).with('cmd', nil, true, false).and_return(123) + allow(@box).to receive(:processes).and_return([ double('ps', :pid => 123) ]) + expect(@box.bash('cmd', :background => true).pid).to eq 123 + end + + it "builds a script of environment variables to prefix the bash command" do + expect(@box.command_with_environment('cmd', { :a => 'b' })).to eq "export a=\"b\"\ncmd" + end + + it "escapes quotes on environment variables" do + expect(@box.command_with_environment('cmd', { :a => 'a"b' })).to eq "export a=\"a\\\"b\"\ncmd" + end + + it "escapes backticks on environment variables" do + expect(@box.command_with_environment('cmd', { :a => 'a`b' })).to eq "export a=\"a\\\`b\"\ncmd" + end + + it "converts environment variables to_s" do + expect(@box.command_with_environment('cmd', { :a => nil, :b => 123 })).to eq "export a=\"\"\nexport b=\"123\"\ncmd" + end + + it "sets the environment variables from the provided hash" do + allow(@box.connection).to receive(:bash) + expect(@box).to receive(:command_with_environment).with('cmd', { 1 => 2 }) + @box.bash('cmd', :env => { 1 => 2 }) + end + + it "checks the connection to determine if it is alive" do + expect(@box.connection).to receive(:alive?).and_return(true) + expect(@box).to be_alive + end + + it "establish_connection to set up the connection manually" do + expect(@box.connection).to receive(:ensure_tunnel) + @box.establish_connection + end + + it "establish_connection can take a hash of options" do + expect(@box.connection).to receive(:ensure_tunnel).with(:timeout => :infinite) + @box.establish_connection(:timeout => :infinite) + end end From 0024cb4cc307f2d93cd78cd38131b8b83f98f0a7 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Fri, 6 Mar 2015 22:30:20 +0300 Subject: [PATCH 072/119] Update dependencies; rewrite some tests --- Gemfile.lock | 45 +++++++++++++++++++++++---------------------- spec/box_spec.rb | 2 +- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index d766fb7..65e0f6d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: http://rubygems.org/ specs: - addressable (2.3.6) + addressable (2.3.7) builder (3.2.2) coderay (1.1.0) coolline (0.5.0) @@ -9,10 +9,10 @@ GEM descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) diff-lcs (1.2.5) - faraday (0.9.0) + faraday (0.9.1) multipart-post (>= 1.2, < 3) - git (1.2.8) - github_api (0.12.2) + git (1.2.9.1) + github_api (0.12.3) addressable (~> 2.3) descendants_tracker (~> 0.0.4) faraday (~> 0.8, < 0.10) @@ -20,8 +20,8 @@ GEM multi_json (>= 1.7.5, < 2.0) nokogiri (~> 1.6.3) oauth2 - hashie (3.3.2) - highline (1.6.21) + hashie (3.4.0) + highline (1.7.1) jeweler (2.0.1) builder bundler (>= 1.0) @@ -31,14 +31,14 @@ GEM nokogiri (>= 1.5.10) rake rdoc - jwt (1.2.0) + jwt (1.3.0) method_source (0.8.2) - mini_portile (0.6.1) - multi_json (1.10.1) + mini_portile (0.6.2) + multi_json (1.11.0) multi_xml (0.5.5) multipart-post (2.0.0) - net-ssh (2.9.1) - nokogiri (1.6.5) + net-ssh (2.9.2) + nokogiri (1.6.6.2) mini_portile (~> 0.6.0) oauth2 (1.0.0) faraday (>= 0.8, < 0.10) @@ -53,18 +53,19 @@ GEM rack (1.6.0) rake (10.4.2) rdoc (4.2.0) - rspec (3.1.0) - rspec-core (~> 3.1.0) - rspec-expectations (~> 3.1.0) - rspec-mocks (~> 3.1.0) - rspec-core (3.1.7) - rspec-support (~> 3.1.0) - rspec-expectations (3.1.2) + rspec (3.2.0) + rspec-core (~> 3.2.0) + rspec-expectations (~> 3.2.0) + rspec-mocks (~> 3.2.0) + rspec-core (3.2.1) + rspec-support (~> 3.2.0) + rspec-expectations (3.2.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.1.0) - rspec-mocks (3.1.3) - rspec-support (~> 3.1.0) - rspec-support (3.1.2) + rspec-support (~> 3.2.0) + rspec-mocks (3.2.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.2.0) + rspec-support (3.2.2) session (3.2.0) slop (3.6.0) thread_safe (0.3.4) diff --git a/spec/box_spec.rb b/spec/box_spec.rb index acaddfc..a31f9fe 100644 --- a/spec/box_spec.rb +++ b/spec/box_spec.rb @@ -33,7 +33,7 @@ it "executes bash commands in the background, returning a Rush::Process" do expect(@box.connection).to receive(:bash).with('cmd', nil, true, false).and_return(123) - allow(@box).to receive(:processes).and_return([ double('ps', :pid => 123) ]) + allow(@box).to receive(:processes).and_return([double('ps', :pid => 123)]) expect(@box.bash('cmd', :background => true).pid).to eq 123 end From cad4b42ba93507b74a04b3a79bd084b2f8bde4af Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Fri, 6 Mar 2015 23:10:41 +0300 Subject: [PATCH 073/119] [refs #13] Hash of keys in open_with and output_of --- lib/rush/commands.rb | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 82b6c5c..b527ab9 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -64,13 +64,23 @@ def open(*args) # Open file with any application you like. # Usage: # home.locate('timetable').open_with :vim - def open_with(app, *args) - names = dir? ? '' : entries.map(&:to_s).join(' ') - system "cd #{dirname}; #{app.to_s} #{names} #{args.join(' ')}" + def open_with(app, *args, **opts) + system open_command(app, *args, opts) + end + + def output_of(app, *args, **opts) + `#{open_command(app, *args, opts)}` end - def output_of(app, *args) - names = entries.map(&:to_s).join(' ') - `cd #{dirname}; #{app.to_s} #{names} #{args.join(' ')}` + def open_command(app, *args, **opts) + names = dir? ? '' : entries.map(&:to_s).join(' ') + options = opts.map do |k, v| + case + when v == true || v == false then "-#{k}" + when k == 'other' || k == :other then v + else "-#{k} #{v}" + end + end.join(' ') + "cd #{dirname}; #{app.to_s} #{names} #{options} #{args.join(' ')}" end end From 1c8eadd3da00bc2b59e14e0fb2b17138766b70fb Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Fri, 6 Mar 2015 23:21:37 +0300 Subject: [PATCH 074/119] [refs #13] Support -k and --key --- lib/rush/commands.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index b527ab9..2ead5c0 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -75,10 +75,11 @@ def output_of(app, *args, **opts) def open_command(app, *args, **opts) names = dir? ? '' : entries.map(&:to_s).join(' ') options = opts.map do |k, v| + key = k.size == 1 ? "-#{k}" : "--#{k}" case - when v == true || v == false then "-#{k}" + when v == true || v == false then key when k == 'other' || k == :other then v - else "-#{k} #{v}" + else "#{key} #{v}" end end.join(' ') "cd #{dirname}; #{app.to_s} #{names} #{options} #{args.join(' ')}" From c59797a060a6f1ecd98d6a7b87441e19fc751904 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Fri, 6 Mar 2015 23:29:32 +0300 Subject: [PATCH 075/119] [refs #13] Update documentation; fix open_command --- lib/rush/commands.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 2ead5c0..0fb0a2c 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -64,6 +64,7 @@ def open(*args) # Open file with any application you like. # Usage: # home.locate('timetable').open_with :vim + # home['.vimrc'].vim { other: '+55', x: true, u: 'other_vimrc', cmd: 'ls' } def open_with(app, *args, **opts) system open_command(app, *args, opts) end @@ -77,7 +78,7 @@ def open_command(app, *args, **opts) options = opts.map do |k, v| key = k.size == 1 ? "-#{k}" : "--#{k}" case - when v == true || v == false then key + when v == true then key when k == 'other' || k == :other then v else "#{key} #{v}" end From 05c8457ccea13204106930ffadb4773db9edc94e Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 28 Mar 2015 14:01:26 +0300 Subject: [PATCH 076/119] Add edit command --- lib/rush/commands.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 0fb0a2c..107b4ed 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -39,6 +39,12 @@ def line_count end end + # Open file with $EDITOR. + # + def edit(*args) + open_with ENV['EDITOR'], *args + end + # Invoke vi on one or more files - only works locally. def vi(*args) if self.class == Rush::Dir From 805081eb0acab87e7e17f05ae87862197770b483 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 28 Mar 2015 14:28:10 +0300 Subject: [PATCH 077/119] Fix process spec and shell spec --- spec/process_spec.rb | 33 ++++++++++++++++++--------------- spec/shell_spec.rb | 2 +- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/spec/process_spec.rb b/spec/process_spec.rb index 73e881c..809bc37 100644 --- a/spec/process_spec.rb +++ b/spec/process_spec.rb @@ -15,59 +15,62 @@ if !RUBY_PLATFORM.match(/darwin/) # OS x reports pids weird it "knows all its child processes" do parent = Rush::Process.all.detect { |p| p.pid == Process.pid } - parent.children.should == [ @process ] + expect(parent.children).to eq [@process] end end it "gets the list of all processes" do list = Rush::Process.all - list.size.should > 5 - list.first.should be_kind_of(Rush::Process) + expect(list.size).to be > 5 + expect(list.first).to be_kind_of Rush::Process end it "knows the pid" do - @process.pid.should == @pid + expect(@process.pid).to eq @pid end it "knows the uid" do - @process.uid.should == ::Process.uid + expect(@process.uid).to eq ::Process.uid end it "knows the executed binary" do - @process.command.should match(/(ruby|rbx)/) + expect(@process.command).to match(/(ruby|rbx)/) end it "knows the command line" do - @process.cmdline.should match(/process_spec.rb/) + expect(@process.cmdline).to match(/process_spec.rb/) end it "knows the memory used" do - @process.mem.should > 0 + expect(@process.mem).to be > 0 end it "knows the cpu used" do - @process.cpu.should >= 0 + expect(@process.cpu).to be >= 0 end it "knows the parent process pid" do - @process.parent_pid.should == Process.pid + expect(@process.parent_pid).to eq Process.pid end it "knows the parent process" do - this = Rush::Box.new.processes.select { |p| p.pid == Process.pid }.first - @process.parent.should == this + this = Rush::Box.new + .processes + .select { |p| p.pid == Process.pid } + .first + expect(@process.parent).to eq this end it "can kill itself" do process = Rush.bash("sleep 30", :background => true) - process.alive?.should be_true + expect(process.alive?).to eq true process.kill sleep 0.1 - process.alive?.should be_false + expect(process.alive?).to eq false end it "if box and pid are the same, process is equal" do other = Rush::Process.new({ :pid => @process.pid }, @process.box) - @process.should == other + expect(@process).to eq other end end diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 0bcec6b..ae4c192 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -23,7 +23,7 @@ it 'Complete method names' do expect(@shell.complete('Rush.meth')). to eq(["Rush.method_part", "Rush.method_defined?", "Rush.methods", "Rush.method"]) - expect(@shell.complete('Rush.methods.inc')).to eq ["Rush.methods.include?"] + expect(@shell.complete('Rush.methods.inc')).to include "Rush.methods.include?" end it 'Complete paths' do From 4496b94af1fb60234ef866973a95d9ee3f1f45e4 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 28 Mar 2015 14:37:58 +0300 Subject: [PATCH 078/119] Fix file spec --- spec/file_spec.rb | 158 +++++++++++++++++++++++----------------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/spec/file_spec.rb b/spec/file_spec.rb index 0a795b0..5fc66f1 100644 --- a/spec/file_spec.rb +++ b/spec/file_spec.rb @@ -1,83 +1,83 @@ require_relative 'base' describe Rush::File do - before do - @sandbox_dir = "/tmp/rush_spec.#{Process.pid}" - system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}" - - @filename = "#{@sandbox_dir}/test_file" - @contents = "1234" - system "echo #{@contents} > #{@filename}" - @contents += "\n" - - @file = Rush::File.new(@filename) - end - - after do - system "rm -rf #{@sandbox_dir}" - end - - it "is a child of Rush::Entry" do - @file.should be_kind_of(Rush::Entry) - end - - it "is not a dir" do - @file.should_not be_dir - end - - it "can create itself as a blank file, and return itself" do - create_me = Rush::File.new("#{@sandbox_dir}/create_me") - create_me.create.should == create_me - File.exists?("#{@sandbox_dir}/create_me").should == true - end - - it "knows its size in bytes" do - @file.size.should == @contents.length - end - - it "can read its contents" do - @file.contents.should == @contents - end - - it "read is an alias for contents" do - @file.read.should == @contents - end - - it "can write new contents" do - @file.write('write test') - @file.contents.should == 'write test' - end - - it "can count the number of lines it contains" do - @file.write("1\n2\n3\n") - @file.line_count.should == 3 - end - - it "searches its contents for matching lines" do - @file.write("a\n1\nb\n2\n") - @file.search(/\d/).should == %w(1 2) - end - - it "search returns nil if no lines match" do - @file.write("a\nb\nc\n") - @file.search(/\d/).should be_nil - end - - it "find-in-file replace" do - @file.replace_contents!(/\d/, 'x') - @file.contents.should == "xxxx\n" - end - - it "can destroy itself" do - @file.destroy - ::File.exists?(@filename).should be_false - end - - it "can fetch contents or blank if doesn't exist" do - Rush::File.new('/does/not/exist').contents_or_blank.should == "" - end - - it "can fetch lines, or empty if doesn't exist" do - Rush::File.new('/does/not/exist').lines_or_empty.should == [] - end + before do + @sandbox_dir = "/tmp/rush_spec.#{Process.pid}" + system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}" + + @filename = "#{@sandbox_dir}/test_file" + @contents = '1234' + system "echo #{@contents} > #{@filename}" + @contents += "\n" + + @file = Rush::File.new(@filename) + end + + after do + system "rm -rf #{@sandbox_dir}" + end + + it 'is a child of Rush::Entry' do + expect(@file).to be_kind_of(Rush::Entry) + end + + it 'is not a dir' do + expect(@file).to_not be_dir + end + + it 'can create itself as a blank file, and return itself' do + create_me = Rush::File.new("#{@sandbox_dir}/create_me") + expect(create_me.create).to eq create_me + expect(File.exist?("#{@sandbox_dir}/create_me")).to eq true + end + + it 'knows its size in bytes' do + expect(@file.size).to eq @contents.length + end + + it 'can read its contents' do + expect(@file.contents).to eq @contents + end + + it 'read is an alias for contents' do + expect(@file.read).to eq @contents + end + + it 'can write new contents' do + @file.write('write test') + expect(@file.contents).to eq 'write test' + end + + it 'can count the number of lines it contains' do + @file.write("1\n2\n3\n") + expect(@file.line_count).to eq 3 + end + + it 'searches its contents for matching lines' do + @file.write("a\n1\nb\n2\n") + expect(@file.search(/\d/)).to eq %w(1 2) + end + + it 'search returns nil if no lines match' do + @file.write("a\nb\nc\n") + expect(@file.search(/\d/)).to eq nil + end + + it 'find-in-file replace' do + @file.replace_contents!(/\d/, 'x') + expect(@file.contents).to eq "xxxx\n" + end + + it 'can destroy itself' do + @file.destroy + expect(::File.exist?(@filename)).to eq false + end + + it "can fetch contents or blank if doesn't exist" do + expect(Rush::File.new('/does/not/exist').contents_or_blank).to eq '' + end + + it 'can fetch lines, or empty if doesn\'t exist' do + expect(Rush::File.new('/does/not/exist').lines_or_empty).to eq [] + end end From d5193577f0d8d69f0b62e0976591b2764fea7ed9 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 28 Mar 2015 14:44:36 +0300 Subject: [PATCH 079/119] Fix process spec --- spec/process_spec.rb | 113 +++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 57 deletions(-) diff --git a/spec/process_spec.rb b/spec/process_spec.rb index 809bc37..e935649 100644 --- a/spec/process_spec.rb +++ b/spec/process_spec.rb @@ -1,76 +1,75 @@ require_relative 'base' describe Rush::Process do - before do - @pid = fork do - sleep 999 - end - @process = Rush::Process.all.detect { |p| p.pid == @pid } - end + before do + @pid = fork do + sleep 999 + end + @process = Rush::Process.all.detect { |p| p.pid == @pid } + end - after do - system "kill -9 #{@pid}" - end + after do + system "kill -9 #{@pid}" + end - if !RUBY_PLATFORM.match(/darwin/) # OS x reports pids weird - it "knows all its child processes" do - parent = Rush::Process.all.detect { |p| p.pid == Process.pid } - expect(parent.children).to eq [@process] - end - end + unless RUBY_PLATFORM.match(/darwin/) # OS x reports pids weird + it 'knows all its child processes' do + parent = Rush::Process.all.detect { |p| p.pid == Process.pid } + expect(parent.children).to eq [@process] + end + end - it "gets the list of all processes" do - list = Rush::Process.all - expect(list.size).to be > 5 - expect(list.first).to be_kind_of Rush::Process - end + it 'gets the list of all processes' do + list = Rush::Process.all + expect(list.size).to be > 5 + expect(list.first).to be_kind_of Rush::Process + end - it "knows the pid" do - expect(@process.pid).to eq @pid - end + it 'knows the pid' do + expect(@process.pid).to eq @pid + end - it "knows the uid" do - expect(@process.uid).to eq ::Process.uid - end + it 'knows the uid' do + expect(@process.uid).to eq ::Process.uid + end - it "knows the executed binary" do - expect(@process.command).to match(/(ruby|rbx)/) - end + it 'knows the executed binary' do + expect(@process.command).to match(/(ruby|rbx)/) + end - it "knows the command line" do - expect(@process.cmdline).to match(/process_spec.rb/) - end + it 'knows the command line' do + expect(@process.cmdline).to match(/rspec/) + end - it "knows the memory used" do - expect(@process.mem).to be > 0 - end + it 'knows the memory used' do + expect(@process.mem).to be > 0 + end - it "knows the cpu used" do - expect(@process.cpu).to be >= 0 - end + it 'knows the cpu used' do + expect(@process.cpu).to be >= 0 + end - it "knows the parent process pid" do - expect(@process.parent_pid).to eq Process.pid - end + it 'knows the parent process pid' do + expect(@process.parent_pid).to eq Process.pid + end - it "knows the parent process" do - this = Rush::Box.new - .processes + it 'knows the parent process' do + this = Rush::Box.new.processes .select { |p| p.pid == Process.pid } .first - expect(@process.parent).to eq this - end + expect(@process.parent).to eq this + end - it "can kill itself" do - process = Rush.bash("sleep 30", :background => true) - expect(process.alive?).to eq true - process.kill - sleep 0.1 - expect(process.alive?).to eq false - end + it 'can kill itself' do + process = Rush.bash('sleep 30', background: true) + expect(process.alive?).to eq true + process.kill + sleep 0.1 + expect(process.alive?).to eq false + end - it "if box and pid are the same, process is equal" do - other = Rush::Process.new({ :pid => @process.pid }, @process.box) - expect(@process).to eq other - end + it 'if box and pid are the same, process is equal' do + other = Rush::Process.new({ pid: @process.pid }, @process.box) + expect(@process).to eq other + end end From 0fce14ab434592aff7fbc6d72c48f115d61e6e23 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 28 Mar 2015 14:46:23 +0300 Subject: [PATCH 080/119] Remove remote and spec for it. Noone use it --- lib/rush.rb | 1 - lib/rush/remote.rb | 33 ----------- spec/remote_spec.rb | 140 -------------------------------------------- 3 files changed, 174 deletions(-) delete mode 100644 lib/rush/remote.rb delete mode 100644 spec/remote_spec.rb diff --git a/lib/rush.rb b/lib/rush.rb index 4983393..0ccb9ce 100644 --- a/lib/rush.rb +++ b/lib/rush.rb @@ -86,6 +86,5 @@ module Rush::Connection; end require_relative 'rush/process' require_relative 'rush/process_set' require_relative 'rush/local' -require_relative 'rush/remote' require_relative 'rush/box' require_relative 'rush/embeddable_shell' diff --git a/lib/rush/remote.rb b/lib/rush/remote.rb deleted file mode 100644 index 997da27..0000000 --- a/lib/rush/remote.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'net/ssh' - -module Rush - # wrapper of command - # sshfs '-o idmap=user @: ' - # - class Connection::Remote - attr_reader :local_path, :full_remote_path, :remote_path, :remote_server, :remote_user - - def initialize(full_remote_path, local_path) - local_path = local_path.full_path if local_path.respond_to?(:full_path) - @full_remote_path = full_remote_path - @local_path = Rush::Dir.new(local_path) - @local_path.create unless @local_path.exists? - @remote_user, server_and_path = *full_remote_path.split('@', 2) - @remote_server, @remote_address = *server_and_path.split(':', 2) - end - - def connect - system "sshfs -o idmap=user #{full_remote_path} #{local_path}" - end - alias_method :mount, :connect - - def disconnect - system "fusermount -u #{local_path.full_path}" - end - alias_method :umount, :disconnect - - def method_missing(meth, *args, &block) - @local_path.send(meth, *args, &block) - end - end -end diff --git a/spec/remote_spec.rb b/spec/remote_spec.rb deleted file mode 100644 index 3272c84..0000000 --- a/spec/remote_spec.rb +++ /dev/null @@ -1,140 +0,0 @@ -require_relative 'base' - -describe Rush::Connection::Local do - before do - @sandbox_dir = "/tmp/rush_spec.#{Process.pid}" - system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}" - - @con = Rush::Connection::Remote.new('spec.example.com') - end - - after do - system "rm -rf #{@sandbox_dir}" - end - - it "transmits write_file" do - @con.should_receive(:transmit).with(:action => 'write_file', :full_path => 'file', :payload => 'contents') - @con.write_file('file', 'contents') - end - - it "transmits append_to_file" do - @con.should_receive(:transmit).with(:action => 'append_to_file', :full_path => 'file', :payload => 'contents') - @con.append_to_file('file', 'contents') - end - - it "transmits file_contents" do - @con.should_receive(:transmit).with(:action => 'file_contents', :full_path => 'file').and_return('contents') - @con.file_contents('file').should == 'contents' - end - - it "transmits destroy" do - @con.should_receive(:transmit).with(:action => 'destroy', :full_path => 'file') - @con.destroy('file') - end - - it "transmits purge" do - @con.should_receive(:transmit).with(:action => 'purge', :full_path => 'dir') - @con.purge('dir') - end - - it "transmits create_dir" do - @con.should_receive(:transmit).with(:action => 'create_dir', :full_path => 'file') - @con.create_dir('file') - end - - it "transmits rename" do - @con.should_receive(:transmit).with(:action => 'rename', :path => 'path', :name => 'name', :new_name => 'new_name') - @con.rename('path', 'name', 'new_name') - end - - it "transmits copy" do - @con.should_receive(:transmit).with(:action => 'copy', :src => 'src', :dst => 'dst') - @con.copy('src', 'dst') - end - - it "transmits read_archive" do - @con.should_receive(:transmit).with(:action => 'read_archive', :full_path => 'full_path').and_return('archive data') - @con.read_archive('full_path').should == 'archive data' - end - - it "transmits write_archive" do - @con.should_receive(:transmit).with(:action => 'write_archive', :dir => 'dir', :payload => 'archive') - @con.write_archive('archive', 'dir') - end - - it "transmits index" do - @con.should_receive(:transmit).with(:action => 'index', :base_path => 'base_path', :glob => '*').and_return("1\n2\n") - @con.index('base_path', '*').should == %w(1 2) - end - - it "transmits stat" do - @con.should_receive(:transmit).with(:action => 'stat', :full_path => 'full_path').and_return(YAML.dump(1 => 2)) - @con.stat('full_path').should == { 1 => 2 } - end - - it "transmits set_access" do - @con.should_receive(:transmit).with(:action => 'set_access', :full_path => 'full_path', :user => 'joe', :user_read => 1) - @con.set_access('full_path', :user => 'joe', :user_read => 1) - end - - it "transmits size" do - @con.should_receive(:transmit).with(:action => 'size', :full_path => 'full_path').and_return("123") - @con.size('full_path').should == 123 - end - - it "transmits processes" do - @con.should_receive(:transmit).with(:action => 'processes').and_return(YAML.dump([ { :pid => 1 } ])) - @con.processes.should == [ { :pid => 1 } ] - end - - it "transmits process_alive" do - @con.should_receive(:transmit).with(:action => 'process_alive', :pid => 123).and_return(true) - @con.process_alive(123).should == true - end - - it "transmits kill_process" do - @con.should_receive(:transmit).with(:action => 'kill_process', :pid => 123, :payload => YAML.dump(:wait => 10)) - @con.kill_process(123, :wait => 10) - end - - it "transmits bash" do - @con.should_receive(:transmit).with(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'bg', :reset_environment => false).and_return('output') - @con.bash('cmd', 'user', 'bg', false).should == 'output' - end - - it "an http result code of 401 raises NotAuthorized" do - lambda { @con.process_result("401", "") }.should raise_error(Rush::NotAuthorized) - end - - it "an http result code of 400 raises the exception passed in the result body" do - @con.stub(:parse_exception).and_return(Rush::DoesNotExist, "message") - lambda { @con.process_result("400", "") }.should raise_error(Rush::DoesNotExist) - end - - it "an http result code of 501 (or anything other than the other defined codes) raises FailedTransmit" do - lambda { @con.process_result("501", "") }.should raise_error(Rush::FailedTransmit) - end - - it "parse_exception takes the class from the first line and the message from the second" do - @con.parse_exception("Rush::DoesNotExist\nthe message\n").should == [ Rush::DoesNotExist, "the message" ] - end - - it "parse_exception rejects unrecognized exceptions" do - lambda { @con.parse_exception("NotARushException\n") }.should raise_error - end - - it "passes through ensure_tunnel" do - @con.tunnel.should_receive(:ensure_tunnel) - @con.ensure_tunnel - end - - it "is alive if the box is responding to commands" do - @con.should_receive(:index).and_return(:dummy) - @con.should be_alive - end - - it "not alive if an attempted command throws an exception" do - @con.should_receive(:index).and_raise(RuntimeError) - @con.should_not be_alive - end -end From 05a1922be8847a65076594bcf7cdb3f2d0acee26 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 28 Mar 2015 15:04:11 +0300 Subject: [PATCH 081/119] Fix entry spec --- spec/entry_spec.rb | 246 ++++++++++++++++++++++----------------------- 1 file changed, 123 insertions(+), 123 deletions(-) diff --git a/spec/entry_spec.rb b/spec/entry_spec.rb index 6eb7094..0dd3df5 100644 --- a/spec/entry_spec.rb +++ b/spec/entry_spec.rb @@ -1,133 +1,133 @@ require_relative 'base' describe Rush::Entry do - before do - @sandbox_dir = "/tmp/rush_spec.#{Process.pid}/" - system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}" + before do + @sandbox_dir = "/tmp/rush_spec.#{Process.pid}/" + system "rm -rf #{@sandbox_dir}; mkdir -p #{@sandbox_dir}" - @filename = "#{@sandbox_dir}/test_file" - system "touch #{@filename}" + @filename = "#{@sandbox_dir}/test_file" + system "touch #{@filename}" - @entry = Rush::Entry.new(@filename) - end + @entry = Rush::Entry.new(@filename) + end - after do - system "rm -rf #{@sandbox_dir}" - end + after do + system "rm -rf #{@sandbox_dir}" + end - it "knows its name" do - @entry.name.should == File.basename(@filename) - end + it 'knows its name' do + expect(@entry.name).to eq File.basename(@filename) + end - it "knows its parent dir" do - @entry.parent.should be_kind_of(Rush::Dir) - @entry.parent.name.should == File.basename(@sandbox_dir) - @entry.parent.full_path.should == @sandbox_dir - end - - it "cleans its pathname" do - Rush::Entry.new('/a//b//c').full_path.should == '/a/b/c' - Rush::Entry.new('/1/2/../3').full_path.should == '/1/3' - end - - it "knows its changed_at time" do - @entry.changed_at.should == File.stat(@filename).ctime - end - - it "knows its last_modified time" do - @entry.last_modified.should == File.stat(@filename).mtime - end - - it "knows its last_accessed time" do - @entry.last_accessed.should == File.stat(@filename).atime - end - - it "considers itself equal to other instances with the same full path" do - Rush::Entry.new('/not/the/same').should_not == @entry - Rush::Entry.new(@entry.full_path).should == @entry - end - - it "can rename itself" do - new_file = "test2" - - @entry.rename(new_file) - - File.exists?(@filename).should be_false - File.exists?("#{@sandbox_dir}/#{new_file}").should be_true - end - - it "rename returns the renamed file" do - @entry.rename('file2').should == @entry.parent['file2'] - end - - it "can't rename itself if another file already exists with that name" do - new_file = "test3" - system "touch #{@sandbox_dir}/#{new_file}" - - lambda { @entry.rename(new_file) }.should raise_error(Rush::NameAlreadyExists, /#{new_file}/) - end - - it "can't rename itself to something with a slash in it" do - lambda { @entry.rename('has/slash') }.should raise_error(Rush::NameCannotContainSlash, /slash/) - end - - it "can duplicate itself within the directory" do - @entry.duplicate('newfile').should == Rush::File.new("#{@sandbox_dir}/newfile") - end - - it "can move itself to another dir" do - newdir = "#{@sandbox_dir}/newdir" - system "mkdir -p #{newdir}" - - dst = Rush::Dir.new(newdir) - @entry.move_to(dst) - - File.exists?(@filename).should be_false - File.exists?("#{newdir}/#{@entry.name}").should be_true - end - - it "can copy itself to another directory" do - newdir = "#{@sandbox_dir}/newdir" - system "mkdir -p #{newdir}" - - dst = Rush::Dir.new(newdir) - @copied_dir = @entry.copy_to(dst) - - File.exists?(@filename).should be_true - File.exists?("#{newdir}/#{@entry.name}").should be_true - - @copied_dir.full_path.should == "#{@sandbox_dir}newdir/#{@entry.name}" - end - - it "considers dotfiles to be hidden" do - Rush::Entry.new("#{@sandbox_dir}/show").should_not be_hidden - Rush::Entry.new("#{@sandbox_dir}/.dont_show").should be_hidden - end + it 'knows its parent dir' do + expect(@entry.parent).to be_kind_of(Rush::Dir) + expect(@entry.parent.name).to eq File.basename(@sandbox_dir) + expect(@entry.parent.full_path).to eq @sandbox_dir + end + + it 'cleans its pathname' do + expect(Rush::Entry.new('/a//b//c').full_path).to eq '/a/b/c' + expect(Rush::Entry.new('/1/2/../3').full_path).to eq '/1/3' + end + + it 'knows its changed_at time' do + expect(@entry.changed_at).to eq File.stat(@filename).ctime + end + + it 'knows its last_modified time' do + expect(@entry.last_modified).to eq File.stat(@filename).mtime + end + + it 'knows its last_accessed time' do + expect(@entry.last_accessed).to eq File.stat(@filename).atime + end + + it 'considers itself equal to other instances with the same full path' do + expect(Rush::Entry.new('/not/the/same')).to_not eq @entry + expect(Rush::Entry.new(@entry.full_path)).to eq @entry + end + + it 'can rename itself' do + new_file = 'test2' + + @entry.rename(new_file) + + expect(File.exist?(@filename)).to eq false + expect(File.exist?("#{@sandbox_dir}/#{new_file}")).to eq true + end + + it 'rename returns the renamed file' do + expect(@entry.rename('file2')).to eq @entry.parent['file2'] + end + + it 'can\'t rename itself if another file already exists with that name' do + new_file = 'test3' + system "touch #{@sandbox_dir}/#{new_file}" + + expect { @entry.rename(new_file) }.to raise_error(Rush::NameAlreadyExists, /#{new_file}/) + end + + it "can't rename itself to something with a slash in it" do + expect { @entry.rename('has/slash') }.to raise_error(Rush::NameCannotContainSlash, /slash/) + end + + it 'can duplicate itself within the directory' do + expect(@entry.duplicate('newfile')).to eq Rush::File.new("#{@sandbox_dir}/newfile") + end + + it 'can move itself to another dir' do + newdir = "#{@sandbox_dir}/newdir" + system "mkdir -p #{newdir}" + + dst = Rush::Dir.new(newdir) + @entry.move_to(dst) + + expect(File.exist?(@filename)).to eq false + expect(File.exist?("#{newdir}/#{@entry.name}")).to eq true + end + + it 'can copy itself to another directory' do + newdir = "#{@sandbox_dir}/newdir" + system "mkdir -p #{newdir}" + + dst = Rush::Dir.new(newdir) + @copied_dir = @entry.copy_to(dst) + + expect(File.exist?(@filename)).to eq true + expect(File.exist?("#{newdir}/#{@entry.name}")).to eq true + + expect(@copied_dir.full_path).to eq "#{@sandbox_dir}newdir/#{@entry.name}" + end + + it 'considers dotfiles to be hidden' do + expect(Rush::Entry.new("#{@sandbox_dir}/show")).to_not be_hidden + expect(Rush::Entry.new("#{@sandbox_dir}/.dont_show")).to be_hidden + end - it "is considered equal to entries with the same full path and on the same box" do - same = Rush::Entry.new(@entry.full_path, @entry.box) - @entry.should == same - end - - it "is considered not equal to entries with the same full path on a different box" do - same = Rush::Entry.new(@entry.full_path, Rush::Box.new('dummy')) - @entry.should_not == same - end - - it "can mimic another entry" do - copy = Rush::Entry.new('abc', :dummy) - copy.mimic(@entry) - copy.path.should == @entry.path - end - - it "can update the read access permission" do - system "chmod 666 #{@filename}" - @entry.access = { :user_can => :read } - `ls -l #{@filename}`.should match(/^-r--------/) - end - - it "reads the file permissions in the access hash" do - system "chmod 640 #{@filename}" - @entry.access.should == { :user_can_read => true, :user_can_write => true, :group_can_read => true } - end + it 'is considered equal to entries with the same full path and on the same box' do + same = Rush::Entry.new(@entry.full_path, @entry.box) + expect(@entry).to eq same + end + + it 'is considered not equal to entries with the same full path on a different box' do + same = Rush::Entry.new(@entry.full_path, Rush::Box.new('dummy')) + expect(@entry).to_not eq same + end + + it 'can mimic another entry' do + copy = Rush::Entry.new('abc', :dummy) + copy.mimic(@entry) + expect(copy.path).to eq @entry.path + end + + it 'can update the read access permission' do + system "chmod 666 #{@filename}" + @entry.access = { :user_can => :read } + expect(`ls -l #{@filename}`).to match(/^-r--------/) + end + + it 'reads the file permissions in the access hash' do + system "chmod 640 #{@filename}" + expect(@entry.access).to eq({ user_can_read: true, user_can_write: true, group_can_read: true }) + end end From 7f78c41a4eebc0b0e88cf71e5a779bab641b93e2 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 28 Mar 2015 17:06:48 +0300 Subject: [PATCH 082/119] All specs passed! --- spec/local_spec.rb | 182 ++++++++++++++++++++++----------------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/spec/local_spec.rb b/spec/local_spec.rb index 6525ffb..f95576a 100644 --- a/spec/local_spec.rb +++ b/spec/local_spec.rb @@ -13,264 +13,262 @@ end it "receive -> write_file(file, contents)" do - @con.should_receive(:write_file).with('file', 'contents') + expect(@con).to receive(:write_file).with('file', 'contents') @con.receive(:action => 'write_file', :full_path => 'file', :payload => 'contents') end it "receive -> append_to_file(file, contents)" do - @con.should_receive(:append_to_file).with('file', 'contents') + expect(@con).to receive(:append_to_file).with('file', 'contents') @con.receive(:action => 'append_to_file', :full_path => 'file', :payload => 'contents') end it "receive -> file_contents(file)" do - @con.should_receive(:file_contents).with('file').and_return('the contents') - @con.receive(:action => 'file_contents', :full_path => 'file').should == 'the contents' + expect(@con).to receive(:file_contents).with('file').and_return('the contents') + expect(@con.receive(:action => 'file_contents', :full_path => 'file')).to eq 'the contents' end it "receive -> destroy(file or dir)" do - @con.should_receive(:destroy).with('file') + expect(@con).to receive(:destroy).with('file') @con.receive(:action => 'destroy', :full_path => 'file') end it "receive -> purge(dir)" do - @con.should_receive(:purge).with('dir') + expect(@con).to receive(:purge).with('dir') @con.receive(:action => 'purge', :full_path => 'dir') end it "receive -> create_dir(path)" do - @con.should_receive(:create_dir).with('dir') + expect(@con).to receive(:create_dir).with('dir') @con.receive(:action => 'create_dir', :full_path => 'dir') end it "receive -> rename(path, name, new_name)" do - @con.should_receive(:rename).with('path', 'name', 'new_name') + expect(@con).to receive(:rename).with('path', 'name', 'new_name') @con.receive(:action => 'rename', :path => 'path', :name => 'name', :new_name => 'new_name') end it "receive -> copy(src, dst)" do - @con.should_receive(:copy).with('src', 'dst') + expect(@con).to receive(:copy).with('src', 'dst') @con.receive(:action => 'copy', :src => 'src', :dst => 'dst') end it "receive -> read_archive(full_path)" do - @con.should_receive(:read_archive).with('full_path').and_return('archive data') - @con.receive(:action => 'read_archive', :full_path => 'full_path').should == 'archive data' + expect(@con).to receive(:read_archive).with('full_path').and_return('archive data') + expect(@con.receive(:action => 'read_archive', :full_path => 'full_path')).to eq 'archive data' end it "receive -> write_archive(archive, dir)" do - @con.should_receive(:write_archive).with('archive', 'dir') + expect(@con).to receive(:write_archive).with('archive', 'dir') @con.receive(:action => 'write_archive', :dir => 'dir', :payload => 'archive') end it "receive -> index(base_path, glob)" do - @con.should_receive(:index).with('base_path', '*').and_return(%w(1 2)) - @con.receive(:action => 'index', :base_path => 'base_path', :glob => '*').should == "1\n2\n" + expect(@con).to receive(:index).with('base_path', '*').and_return(%w(1 2)) + expect(@con.receive(:action => 'index', :base_path => 'base_path', :glob => '*')).to eq "1\n2\n" end it "receive -> stat(full_path)" do - @con.should_receive(:stat).with('full_path').and_return(1 => 2) - @con.receive(:action => 'stat', :full_path => 'full_path').should == YAML.dump(1 => 2) + expect(@con).to receive(:stat).with('full_path').and_return(1 => 2) + expect(@con.receive(:action => 'stat', :full_path => 'full_path')).to eq YAML.dump(1 => 2) end it "receive -> set_access(full_path, user, group, permissions)" do access = double("access") - Rush::Access.should_receive(:from_hash).with(:action => 'set_access', :full_path => 'full_path', :user => 'joe').and_return(access) + expect(Rush::Access).to receive(:from_hash).with(:action => 'set_access', :full_path => 'full_path', :user => 'joe').and_return(access) - @con.should_receive(:set_access).with('full_path', access) + expect(@con).to receive(:set_access).with('full_path', access) @con.receive(:action => 'set_access', :full_path => 'full_path', :user => 'joe') end it "receive -> size(full_path)" do - @con.should_receive(:size).with('full_path').and_return("1024") - @con.receive(:action => 'size', :full_path => 'full_path').should == "1024" + expect(@con).to receive(:size).with('full_path').and_return("1024") + expect(@con.receive(:action => 'size', :full_path => 'full_path')).to eq "1024" end it "receive -> chown(full_path, user, group, options)" do options = { noop: true } - @con.should_receive(:chown).with('full_path', 'username', 'groupname', options ) + expect(@con).to receive(:chown).with('full_path', 'username', 'groupname', options ) @con.receive(:action => 'chown', :full_path => 'full_path', user: 'username', group: 'groupname', options: options) end it "receive -> processes" do - @con.should_receive(:processes).with().and_return([ { :pid => 1 } ]) - @con.receive(:action => 'processes').should == YAML.dump([ { :pid => 1 } ]) + expect(@con).to receive(:processes).with(no_args).and_return([ { :pid => 1 } ]) + expect(@con.receive(:action => 'processes')).to eq YAML.dump([ { :pid => 1 } ]) end it "receive -> process_alive" do - @con.should_receive(:process_alive).with(123).and_return(true) - @con.receive(:action => 'process_alive', :pid => 123).should == '1' + expect(@con).to receive(:process_alive).with(123).and_return(true) + expect(@con.receive(:action => 'process_alive', :pid => 123)).to eq '1' end it "receive -> kill_process" do - @con.should_receive(:kill_process).with(123, :wait => 10).and_return(true) + expect(@con).to receive(:kill_process).with(123, :wait => 10).and_return(true) @con.receive(:action => 'kill_process', :pid => '123', :payload => YAML.dump(:wait => 10)) end it "receive -> bash (reset environment)" do - @con.should_receive(:bash).with('cmd', 'user', false, true).and_return('output') - @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'false', :reset_environment => 'true').should == 'output' + expect(@con).to receive(:bash).with('cmd', 'user', false, true).and_return('output') + expect(@con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'false', :reset_environment => 'true')).to eq 'output' end it "receive -> bash (foreground)" do - @con.should_receive(:bash).with('cmd', 'user', false, false).and_return('output') - @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'false').should == 'output' + expect(@con).to receive(:bash).with('cmd', 'user', false, false).and_return('output') + expect(@con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'false')).to eq 'output' end it "receive -> bash (background)" do - @con.should_receive(:bash).with('cmd', 'user', true, false).and_return('output') - @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'true').should == 'output' + expect(@con).to receive(:bash).with('cmd', 'user', true, false).and_return('output') + expect(@con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'true')).to eq 'output' end it "receive -> unknown action exception" do - lambda { @con.receive(:action => 'does_not_exist') }.should raise_error(Rush::Connection::Local::UnknownAction) + expect { @con.receive(:action => 'does_not_exist') }.to raise_error(Rush::Connection::Local::UnknownAction) end it "write_file writes contents to a file" do fname = "#{@sandbox_dir}/a_file" data = "some data" @con.write_file(fname, data) - File.read(fname).should == data + expect(File.read(fname)).to eq data end it "append_to_file appends contents to a file" do fname = "#{@sandbox_dir}/a_file" system "echo line1 > #{fname}" @con.append_to_file(fname, 'line2') - File.read(fname).should == "line1\nline2" + expect(File.read(fname)).to eq "line1\nline2" end it "file_contents reads a file's contents" do fname = "#{@sandbox_dir}/a_file" system "echo stuff > #{fname}" - @con.file_contents(fname).should == "stuff\n" + expect(@con.file_contents(fname)).to eq "stuff\n" end it "file_contents raises DoesNotExist if the file does not exist" do fname = "#{@sandbox_dir}/does_not_exist" - lambda { @con.file_contents(fname) }.should raise_error(Rush::DoesNotExist, fname) + expect { @con.file_contents(fname) }.to raise_error(Rush::DoesNotExist, fname) end it "destroy to destroy a file or dir" do fname = "#{@sandbox_dir}/delete_me" system "touch #{fname}" @con.destroy(fname) - File.exists?(fname).should be_false + expect(File.exists?(fname)).to eq false end it "purge to purge a dir" do system "cd #{@sandbox_dir}; touch {1,2}; mkdir 3; touch 3/4" @con.purge(@sandbox_dir) - File.exists?(@sandbox_dir).should be_true - Dir.glob("#{@sandbox_dir}/*").should == [] + expect(File.exists?(@sandbox_dir)).to eq true + expect(Dir.glob("#{@sandbox_dir}/*")).to eq([]) end it "purge kills hidden (dotfile) entries too" do system "cd #{@sandbox_dir}; touch .killme" @con.purge(@sandbox_dir) - File.exists?(@sandbox_dir).should be_true - `cd #{@sandbox_dir}; ls -lA | grep -v total | wc -l`.to_i.should == 0 + expect(File.exist?(@sandbox_dir)).to eq true + expect(`cd #{@sandbox_dir}; ls -lA | grep -v total | wc -l`.to_i).to eq 0 end it "create_dir creates a directory" do fname = "#{@sandbox_dir}/a/b/c/" @con.create_dir(fname) - File.directory?(fname).should be_true + expect(File.directory?(fname)).to eq true end it "rename to rename entries within a dir" do system "touch #{@sandbox_dir}/a" @con.rename(@sandbox_dir, 'a', 'b') - File.exists?("#{@sandbox_dir}/a").should be_false - File.exists?("#{@sandbox_dir}/b").should be_true + expect(File.exists?("#{@sandbox_dir}/a")).to eq false + expect(File.exists?("#{@sandbox_dir}/b")).to eq true end it "copy to copy an entry to another dir on the same box" do system "mkdir #{@sandbox_dir}/subdir" system "touch #{@sandbox_dir}/a" @con.copy("#{@sandbox_dir}/a", "#{@sandbox_dir}/subdir") - File.exists?("#{@sandbox_dir}/a").should be_true - File.exists?("#{@sandbox_dir}/subdir/a").should be_true + expect(File.exists?("#{@sandbox_dir}/a")).to eq true + expect(File.exists?("#{@sandbox_dir}/subdir/a")).to eq true end it "copy raises DoesNotExist with source path if it doesn't exist or otherwise can't be accessed" do - lambda { @con.copy('/does/not/exist', '/tmp') }.should raise_error(Rush::DoesNotExist, '/does/not/exist') + expect { @con.copy('/does/not/exist', '/tmp') }.to raise_error(Rush::DoesNotExist, '/does/not/exist') end it "copy raises DoesNotExist with destination path if it can't access the destination" do - lambda { @con.copy('/tmp', '/does/not/exist') }.should raise_error(Rush::DoesNotExist, '/does/not') + expect { @con.copy('/tmp', '/does/not/exist') }.to raise_error(Rush::DoesNotExist, '/does/not') end it "read_archive to pull an archive of a dir into a byte stream" do system "touch #{@sandbox_dir}/a" - @con.read_archive(@sandbox_dir).size.should > 50 + expect(@con.read_archive(@sandbox_dir).size).to be > 50 end it "read_archive works for paths with spaces" do system "mkdir -p #{@sandbox_dir}/with\\ space; touch #{@sandbox_dir}/with\\ space/a" - @con.read_archive("#{@sandbox_dir}/with space").size.should > 50 + expect(@con.read_archive("#{@sandbox_dir}/with space").size).to be > 50 end it "write_archive to turn a byte stream into a dir" do system "cd #{@sandbox_dir}; mkdir -p a; touch a/b; tar cf xfer.tar a; mkdir dst" archive = File.read("#{@sandbox_dir}/xfer.tar") @con.write_archive(archive, "#{@sandbox_dir}/dst") - File.directory?("#{@sandbox_dir}/dst/a").should be_true - File.exists?("#{@sandbox_dir}/dst/a/b").should be_true + expect(File.directory?("#{@sandbox_dir}/dst/a")).to eq true + expect(File.exist?("#{@sandbox_dir}/dst/a/b")).to eq true end it "write_archive works for paths with spaces" do system "cd #{@sandbox_dir}; mkdir -p a; touch a/b; tar cf xfer.tar a; mkdir with\\ space" archive = File.read("#{@sandbox_dir}/xfer.tar") @con.write_archive(archive, "#{@sandbox_dir}/with space") - File.directory?("#{@sandbox_dir}/with space/a").should be_true - File.exists?("#{@sandbox_dir}/with space/a/b").should be_true + expect(File.directory?("#{@sandbox_dir}/with space/a")).to eq true + expect(File.exists?("#{@sandbox_dir}/with space/a/b")).to eq true end it "index fetches list of all files and dirs in a dir when pattern is empty" do system "cd #{@sandbox_dir}; mkdir dir; touch file" - @con.index(@sandbox_dir, '').should == [ 'dir/', 'file' ] + expect(@con.index(@sandbox_dir, '')).to eq([ 'dir/', 'file' ]) end it "index fetches only files with a certain extension with a flat pattern, *.rb" do system "cd #{@sandbox_dir}; touch a.rb; touch b.txt" - @con.index(@sandbox_dir, '*.rb').should == [ 'a.rb' ] + expect(@con.index(@sandbox_dir, '*.rb')).to eq [ 'a.rb' ] end it "index raises DoesNotExist when the base path is invalid" do - lambda { @con.index('/does/not/exist', '*') }.should raise_error(Rush::DoesNotExist, '/does/not/exist') + expect { @con.index('/does/not/exist', '*') }.to raise_error(Rush::DoesNotExist, '/does/not/exist') end it "stat gives file stats like size and timestamps" do - @con.stat(@sandbox_dir).should have_key(:ctime) - @con.stat(@sandbox_dir).should have_key(:size) + expect(@con.stat(@sandbox_dir)).to have_key(:ctime) + expect(@con.stat(@sandbox_dir)).to have_key(:size) end it "stat fetches the octal permissions" do - @con.stat(@sandbox_dir)[:mode].should be_kind_of(Fixnum) + expect(@con.stat(@sandbox_dir)[:mode]).to be_kind_of(Fixnum) end it "stat raises DoesNotExist if the entry does not exist" do fname = "#{@sandbox_dir}/does_not_exist" - lambda { @con.stat(fname) }.should raise_error(Rush::DoesNotExist, fname) + expect { @con.stat(fname) }.to raise_error(Rush::DoesNotExist, fname) end it "set_access invokes the access object" do access = double("access") - access.should_receive(:apply).with('/some/path') + expect(access).to receive(:apply).with('/some/path') @con.set_access('/some/path', access) end - it "chown should change the ownership of a file" - - if !RUBY_PLATFORM.match(/darwin/) # doesn't work on OS X 'cause du switches are different + unless RUBY_PLATFORM.match(/darwin/) # doesn't work on OS X 'cause du switches are different it "size gives size of a directory and all its contents recursively" do system "mkdir -p #{@sandbox_dir}/a/b/; echo 1234 > #{@sandbox_dir}/a/b/c" - @con.size(@sandbox_dir).should == (4096*3 + 5) + expect(@con.size(@sandbox_dir)).to eq (4096*3 + 5) end end it "parses ps output on os x" do - @con.parse_ps("21712 501 21711 1236 0 /usr/bin/vi somefile.rb").should == { + expect(@con.parse_ps("21712 501 21711 1236 0 /usr/bin/vi somefile.rb")).to eq({ :pid => "21712", :uid => "501", :parent_pid => 21711, @@ -278,67 +276,67 @@ :cpu => 0, :command => '/usr/bin/vi', :cmdline => '/usr/bin/vi somefile.rb', - } + }) end it "gets the list of processes on os x via the ps command" do - @con.should_receive(:os_x_raw_ps).and_return < "1", :uid => "0", :parent_pid => 1, :mem => 1111, :cpu => 0, :command => "cmd1", :cmdline => "cmd1 args" }, { :pid => "2", :uid => "501", :parent_pid => 1, :mem => 222, :cpu => 1, :command => "cmd2", :cmdline => "cmd2" }, ] end it "the current process should be alive" do - @con.process_alive(Process.pid).should be_true + expect(@con.process_alive(Process.pid)).to eq true end it "a made-up process should not be alive" do - @con.process_alive(99999).should be_false + expect(@con.process_alive(99999)).to eq false end it "kills a process by pid sending a TERM" do - @con.stub(:process_alive).and_return(false) - ::Process.should_receive(:kill).with('TERM', 123).once + allow(@con).to receive(:process_alive).and_return(false) + expect(::Process).to receive(:kill).with('TERM', 123).once @con.kill_process(123) end it "kills a process by pid sending a KILL signal if TERM doesn't work" do - @con.stub(:process_alive).and_return(true) - ::Process.should_receive(:kill).with('TERM', 123).at_least(:twice) - ::Process.should_receive(:kill).with('KILL', 123) + allow(@con).to receive(:process_alive).and_return(true) + expect(::Process).to receive(:kill).with('TERM', 123).at_least(:twice) + expect(::Process).to receive(:kill).with('KILL', 123) @con.kill_process(123) end it "kills a process by pid without sending TERM if :wait is zero" do - ::Process.should_not_receive(:kill).with('TERM', 123) - ::Process.should_receive(:kill).with('KILL', 123) + expect(::Process).to_not receive(:kill).with('TERM', 123) + expect(::Process).to receive(:kill).with('KILL', 123) @con.kill_process(123, :wait => 0) end it "does not raise an error if the process is already dead" do - ::Process.should_receive(:kill).and_raise(Errno::ESRCH) - lambda { @con.kill_process(123) }.should_not raise_error + expect(::Process).to receive(:kill).and_raise(Errno::ESRCH) + expect { @con.kill_process(123) }.to_not raise_error end it "executes a bash command, returning stdout when successful" do - @con.bash("echo test").should == "test\n" + expect(@con.bash("echo test")).to eq "test\n" end it "executes a bash command, raising and error (with stderr as the message) when return value is nonzero" do - lambda { @con.bash("no_such_bin") }.should raise_error(Rush::BashFailed, /command not found/) + expect { @con.bash("no_such_bin") }.to raise_error(Rush::BashFailed, /command not found/) end it "executes a bash command as another user using sudo" do - @con.bash("echo test2", ENV['USER']).should == "test2\n" + expect(@con.bash("echo test2", ENV['USER'])).to eq "test2\n" end it "executes a bash command in the background, returning the pid" do - @con.bash("true", nil, true).should > 0 + expect(@con.bash("true", nil, true)).to be > 0 end it "ensure_tunnel to match with remote connection" do @@ -346,20 +344,22 @@ end it "always returns true on alive?" do - @con.should be_alive + expect(@con).to be_alive end it "resolves a unix uid to a user" do - @con.resolve_unix_uid_to_user(0).should == "root" - @con.resolve_unix_uid_to_user('0').should == "root" + expect(@con.resolve_unix_uid_to_user(0)).to eq "root" + expect(@con.resolve_unix_uid_to_user('0')).to eq "root" end it "returns nil if the unix uid does not exist" do - @con.resolve_unix_uid_to_user(9999).should be_nil + expect(@con.resolve_unix_uid_to_user(9999)).to eq nil end it "iterates through a process list and resolves the unix uid for each" do list = [ { :uid => 0, :command => 'pureftpd' }, { :uid => 9999, :command => 'defunk' } ] - @con.resolve_unix_uids(list).should == [ { :uid => 0, :user => 'root', :command => 'pureftpd' }, { :uid => 9999, :command => 'defunk', :user => nil } ] + expect(@con.resolve_unix_uids(list)) + .to eq([{ :uid => 0, :user => 'root', :command => 'pureftpd' }, + { :uid => 9999, :command => 'defunk', :user => nil }]) end end From 7ca1a56ce39577753d8d3c4d03499cc32c791f65 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 28 Mar 2015 17:39:10 +0300 Subject: [PATCH 083/119] No deprecation warnings anymore --- spec/process_set_spec.rb | 92 ++++++++++++++++++++-------------------- spec/rush_spec.rb | 2 +- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/spec/process_set_spec.rb b/spec/process_set_spec.rb index dea469f..cc2ce0a 100644 --- a/spec/process_set_spec.rb +++ b/spec/process_set_spec.rb @@ -1,50 +1,50 @@ require_relative 'base' describe Rush::ProcessSet do - before do - @process = double('process') - @set = Rush::ProcessSet.new([ @process ]) - end - - it "is Enumerable" do - @set.select { |s| s == @process }.should == [ @process ] - end - - it "defines size" do - @set.size.should == 1 - end - - it "defines first" do - @set.first.should == @process - end - - it "is equal to sets with the same contents" do - @set.should == Rush::ProcessSet.new([ @process ]) - end - - it "is equal to arrays with the same contents" do - @set.should == [ @process ] - end - - it "kills all processes in the set" do - @process.should_receive(:kill) - @set.kill - end - - it "checks the alive? state of all processes in the set" do - @process.should_receive(:alive?).and_return(true) - @set.alive?.should == [ true ] - end - - it "filters the set from a conditions hash and returns the filtered set" do - @process.stub(:pid).and_return(123) - @set.filter(:pid => 123).first.should == @process - @set.filter(:pid => 456).size.should == 0 - end - - it "filters with regexps if provided in the conditions" do - @process.stub(:command).and_return('foobaz') - @set.filter(:command => /baz/).first.should == @process - @set.filter(:command => /blerg/).size.should == 0 - end + before do + @process = double('process') + @set = Rush::ProcessSet.new([ @process ]) + end + + it "is Enumerable" do + expect(@set.select { |s| s == @process }).to eq [ @process ] + end + + it "defines size" do + expect(@set.size).to eq 1 + end + + it "defines first" do + expect(@set.first).to eq @process + end + + it "is equal to sets with the same contents" do + expect(@set).to eq Rush::ProcessSet.new([ @process ]) + end + + it "is equal to arrays with the same contents" do + expect(@set).to eq [ @process ] + end + + it "kills all processes in the set" do + expect(@process).to receive(:kill) + @set.kill + end + + it "checks the alive? state of all processes in the set" do + expect(@process).to receive(:alive?).and_return(true) + expect(@set.alive?).to eq [ true ] + end + + it "filters the set from a conditions hash and returns the filtered set" do + allow(@process).to receive(:pid).and_return(123) + expect(@set.filter(:pid => 123).first).to eq @process + expect(@set.filter(:pid => 456).size).to eq 0 + end + + it "filters with regexps if provided in the conditions" do + allow(@process).to receive(:command).and_return('foobaz') + expect(@set.filter(:command => /baz/).first).to eq @process + expect(@set.filter(:command => /blerg/).size).to eq 0 + end end diff --git a/spec/rush_spec.rb b/spec/rush_spec.rb index ef05146..825cf2a 100644 --- a/spec/rush_spec.rb +++ b/spec/rush_spec.rb @@ -10,7 +10,7 @@ end it 'fetches the launch dir (aka current working directory or pwd)' do - Dir.stub(:pwd).and_return('/tmp') + allow(Dir).to receive(:pwd).and_return('/tmp') expect(Rush.launch_dir).to eq(Rush::Box.new['/tmp/']) end From d97c4265bf5d110122d39c4907f53629190a2acd Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 29 Mar 2015 10:12:04 +0300 Subject: [PATCH 084/119] Update dependencies; bump version --- Gemfile.lock | 8 ++++---- VERSION | 2 +- rush2.gemspec | 10 ++++------ 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 65e0f6d..a0fecce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: http://rubygems.org/ specs: - addressable (2.3.7) + addressable (2.3.8) builder (3.2.2) coderay (1.1.0) coolline (0.5.0) @@ -31,7 +31,7 @@ GEM nokogiri (>= 1.5.10) rake rdoc - jwt (1.3.0) + jwt (1.4.1) method_source (0.8.2) mini_portile (0.6.2) multi_json (1.11.0) @@ -57,7 +57,7 @@ GEM rspec-core (~> 3.2.0) rspec-expectations (~> 3.2.0) rspec-mocks (~> 3.2.0) - rspec-core (3.2.1) + rspec-core (3.2.2) rspec-support (~> 3.2.0) rspec-expectations (3.2.0) diff-lcs (>= 1.2.0, < 2.0) @@ -68,7 +68,7 @@ GEM rspec-support (3.2.2) session (3.2.0) slop (3.6.0) - thread_safe (0.3.4) + thread_safe (0.3.5) unicode_utils (1.4.0) PLATFORMS diff --git a/VERSION b/VERSION index 7deb86f..a3df0a6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.1 \ No newline at end of file +0.8.0 diff --git a/rush2.gemspec b/rush2.gemspec index 3fbd428..edffd34 100644 --- a/rush2.gemspec +++ b/rush2.gemspec @@ -2,16 +2,16 @@ # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- -# stub: rush2 0.7.1 ruby lib +# stub: rush2 0.8.0 ruby lib Gem::Specification.new do |s| s.name = "rush2" - s.version = "0.7.1" + s.version = "0.8.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] s.authors = ["Sergey Smagin"] - s.date = "2014-10-13" + s.date = "2015-03-29" s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." s.email = "smaginsergey1310@gmail.com" s.executables = ["rush", "rushd"] @@ -43,7 +43,6 @@ Gem::Specification.new do |s| "lib/rush/local.rb", "lib/rush/process.rb", "lib/rush/process_set.rb", - "lib/rush/remote.rb", "lib/rush/search_results.rb", "lib/rush/shell.rb", "lib/rush/shell/completion.rb", @@ -63,7 +62,6 @@ Gem::Specification.new do |s| "spec/local_spec.rb", "spec/process_set_spec.rb", "spec/process_spec.rb", - "spec/remote_spec.rb", "spec/rush_spec.rb", "spec/search_results_spec.rb", "spec/shell_spec.rb", @@ -72,7 +70,7 @@ Gem::Specification.new do |s| ] s.homepage = "https://github.com/s-mage/rush" s.licenses = ["MIT"] - s.rubygems_version = "2.2.2" + s.rubygems_version = "2.4.5" s.summary = "A Ruby replacement for bash+ssh." if s.respond_to? :specification_version then From 139d4a26bbd4e800c8f9aec5c2be9e5ae9e0789c Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 5 Apr 2015 10:33:34 +0300 Subject: [PATCH 085/119] Small code style improvements --- lib/rush/commands.rb | 1 + lib/rush/config.rb | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 107b4ed..4b91b53 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -7,6 +7,7 @@ # box['/etc/hosts'].search /localhost/ # single file # box['/etc/'].search /localhost/ # entire directory # box['/etc/**/*.conf'].search /localhost/ # arbitrary list +# module Rush::Commands # The entries command must return an array of Rush::Entry items. This # varies by class that it is mixed in to. diff --git a/lib/rush/config.rb b/lib/rush/config.rb index 958d76d..a4d94f6 100644 --- a/lib/rush/config.rb +++ b/lib/rush/config.rb @@ -6,7 +6,7 @@ class Rush::Config # By default, reads from the dir ~/.rush, but an optional parameter allows # using another location. - def initialize(location=nil) + def initialize(location = nil) @dir = Rush::Dir.new(location || "#{ENV['HOME']}/.rush") @dir.create end @@ -67,7 +67,7 @@ def passwords_file def passwords passwords_file.lines_or_empty.inject({}) do |result, line| - user, password = line.split(":", 2) + user, password = line.split(':', 2) result.merge user => password end end @@ -80,7 +80,7 @@ def credentials_file end def credentials - credentials_file.lines.first.split(":", 2) + credentials_file.lines.first.split(':', 2) end def save_credentials(user, password) @@ -96,7 +96,7 @@ def credentials_password end def ensure_credentials_exist - generate_credentials if credentials_file.contents_or_blank == "" + generate_credentials if credentials_file.contents_or_blank == '' end def generate_credentials @@ -118,7 +118,7 @@ def generate_secret(min, max) end def secret_characters - [ ('a'..'z'), ('1'..'9') ].inject([]) do |chars, range| + [('a'..'z'), ('1'..'9')].inject([]) do |chars, range| chars += range.to_a end end @@ -138,7 +138,7 @@ def tunnels end def save_tunnels(hash) - string = "" + string = '' hash.each do |host, port| string += "#{host}:#{port}\n" end From db0eeed1bda5cb7a1740f3c53eed6635f637a8aa Mon Sep 17 00:00:00 2001 From: Vlad Bokov Date: Mon, 11 May 2015 02:36:38 +0600 Subject: [PATCH 086/119] dry path manipulation & fix travis-ci --- lib/rush.rb | 1 + lib/rush/entry.rb | 4 +--- lib/rush/path.rb | 9 +++++++++ lib/rush/shell/completion.rb | 4 +--- spec/path_spec.rb | 13 +++++++++++++ 5 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 lib/rush/path.rb create mode 100644 spec/path_spec.rb diff --git a/lib/rush.rb b/lib/rush.rb index 0ccb9ce..3c9cc82 100644 --- a/lib/rush.rb +++ b/lib/rush.rb @@ -88,3 +88,4 @@ module Rush::Connection; end require_relative 'rush/local' require_relative 'rush/box' require_relative 'rush/embeddable_shell' +require_relative 'rush/path' diff --git a/lib/rush/entry.rb b/lib/rush/entry.rb index 21d4381..4c9e483 100644 --- a/lib/rush/entry.rb +++ b/lib/rush/entry.rb @@ -22,9 +22,7 @@ def method_missing(meth, *args, &block) end def executables - ENV['PATH'].split(':') - .map { |x| Rush::Dir.new(x).entries.map(&:name) } - .flatten + Rush::Path.executables end # The factory checks to see if the full path has a trailing slash for diff --git a/lib/rush/path.rb b/lib/rush/path.rb new file mode 100644 index 0000000..239a9b9 --- /dev/null +++ b/lib/rush/path.rb @@ -0,0 +1,9 @@ +# A tiny wrapper around ENV['PATH'] +class Rush::Path + def self.executables + ENV['PATH'].split(':') + .select { |f| ::File.directory?(f) } + .map { |x| Rush::Dir.new(x).entries.map(&:name) } + .flatten + end +end diff --git a/lib/rush/shell/completion.rb b/lib/rush/shell/completion.rb index d6dbf65..b572a62 100644 --- a/lib/rush/shell/completion.rb +++ b/lib/rush/shell/completion.rb @@ -89,9 +89,7 @@ def complete_for(the_binding, method_part) end def executables - ENV['PATH'].split(':') - .map { |x| Rush::Dir.new(x).entries.map(&:name) } - .flatten + Rush::Path.executables end def complete_path(input) diff --git a/spec/path_spec.rb b/spec/path_spec.rb new file mode 100644 index 0000000..74f4875 --- /dev/null +++ b/spec/path_spec.rb @@ -0,0 +1,13 @@ +require_relative 'base' +require_relative '../lib/rush/path' + +describe Rush::Path do + it 'works' do + expect(Rush::Path.executables).to be_kind_of Array + end + + it "doesn't fail with non-existent directories in PATH" do + expect(ENV).to receive(:[]).with("PATH").and_return("/foobar") + expect(Rush::Path.executables).to eq [] + end +end From 1d8c88bf9f32ab1775d209213ad0400d3480ae37 Mon Sep 17 00:00:00 2001 From: Vlad Bokov Date: Mon, 11 May 2015 03:15:04 +0600 Subject: [PATCH 087/119] test on 2.2 --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 575d984..f5c5ef7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,9 @@ script: "bundle exec rake" env: - CI=true rvm: - - 2.0.0 + - 2.0 - 2.1 + - 2.2 - rbx-2 gemfile: - Gemfile From 51ba57f86fe744938fdf0303a4aa8aa78bc95695 Mon Sep 17 00:00:00 2001 From: Vlad Bokov Date: Mon, 11 May 2015 03:15:39 +0600 Subject: [PATCH 088/119] support Dir#size on OSX --- lib/rush/local.rb | 6 +++++- spec/dir_spec.rb | 12 ++++++------ spec/local_spec.rb | 8 +++----- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/rush/local.rb b/lib/rush/local.rb index e6e388d..024e664 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -142,7 +142,11 @@ def chown( full_path, user=nil, group=nil, options={} ) # Fetch the size of a dir, since a standard file stat does not include the # size of the contents. def size(full_path) - `du -sb #{Rush.quote(full_path)}`.match(/(\d+)/)[1].to_i + if RUBY_PLATFORM.match(/darwin/) + `find #{Rush.quote(full_path)} -print0 | xargs -0 stat -f%z`.split(/\n/).map(&:to_i).reduce(:+) + else + `du -sb #{Rush.quote(full_path)}`.match(/(\d+)/)[1].to_i + end end # Get the list of processes as an array of hashes. diff --git a/spec/dir_spec.rb b/spec/dir_spec.rb index f712e2a..34821f6 100644 --- a/spec/dir_spec.rb +++ b/spec/dir_spec.rb @@ -130,12 +130,12 @@ expect(@dir.nonhidden_dirs).to eq @dir.make_entries(%(show/)) end - if !RUBY_PLATFORM.match(/darwin/) # doesn't work on OS X 'cause du switches are different - it "knows its size in bytes, which includes its contents recursively" do - @dir.create_file('a').write('1234') - expect(@dir.size).to eq(4096 + 4) - end - end + it "knows its size in bytes, which includes its contents recursively" do + @dir.create_file('a').write('1234') + # on OSX fs stat's size is 102, even though blksize=4096 + # on Linux size is 4096 + expect(@dir.size).to eq(::File.stat(@dir.path).size + 4) + end it "can destroy itself when empty" do @dir.destroy diff --git a/spec/local_spec.rb b/spec/local_spec.rb index f95576a..9035404 100644 --- a/spec/local_spec.rb +++ b/spec/local_spec.rb @@ -260,11 +260,9 @@ @con.set_access('/some/path', access) end - unless RUBY_PLATFORM.match(/darwin/) # doesn't work on OS X 'cause du switches are different - it "size gives size of a directory and all its contents recursively" do - system "mkdir -p #{@sandbox_dir}/a/b/; echo 1234 > #{@sandbox_dir}/a/b/c" - expect(@con.size(@sandbox_dir)).to eq (4096*3 + 5) - end + it "size gives size of a directory and all its contents recursively" do + system "mkdir -p #{@sandbox_dir}/a/b/; echo 1234 > #{@sandbox_dir}/a/b/c" + expect(@con.size(@sandbox_dir)).to eq (::File.stat(@sandbox_dir).size*3 + 5) end it "parses ps output on os x" do From 60dac494a1aff6172cd0da5f1d2f3382b112bd0d Mon Sep 17 00:00:00 2001 From: Vlad Bokov Date: Mon, 11 May 2015 03:20:15 +0600 Subject: [PATCH 089/119] allow sudo in travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index f5c5ef7..9b2e65c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: ruby +sudo: true script: "bundle exec rake" env: - CI=true From 3a0503316818fe8d8912e44ac83e190b79edd1bd Mon Sep 17 00:00:00 2001 From: Vlad Bokov Date: Mon, 11 May 2015 04:22:23 +0600 Subject: [PATCH 090/119] rbx support --- lib/rush/shell/completion.rb | 2 +- spec/shell_spec.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/rush/shell/completion.rb b/lib/rush/shell/completion.rb index b572a62..45d128a 100644 --- a/lib/rush/shell/completion.rb +++ b/lib/rush/shell/completion.rb @@ -56,7 +56,7 @@ def complete(input) } def complete_constant(input) - Object.constants.map(&:to_s).select { |x| x.start_with? input } + Object.constants.map(&:to_s).select { |x| x.start_with? input } .sort end def complete_object_constant(input) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index ae4c192..8bf490f 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -21,8 +21,9 @@ end it 'Complete method names' do + # rbx has additional Rush.method_table, Rush.method_table= expect(@shell.complete('Rush.meth')). - to eq(["Rush.method_part", "Rush.method_defined?", "Rush.methods", "Rush.method"]) + to include("Rush.method_part", "Rush.method_defined?", "Rush.methods", "Rush.method") expect(@shell.complete('Rush.methods.inc')).to include "Rush.methods.include?" end From 1fe7e1bb7dbe697f81d9a929f851c6567978d1c3 Mon Sep 17 00:00:00 2001 From: Vlad Bokov Date: Mon, 11 May 2015 04:26:24 +0600 Subject: [PATCH 091/119] support 1.9.3 --- .travis.yml | 4 +--- lib/rush/commands.rb | 11 ++++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9b2e65c..152cb04 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ script: "bundle exec rake" env: - CI=true rvm: + - 1.9.3 - 2.0 - 2.1 - 2.2 @@ -13,6 +14,3 @@ gemfile: notifications: recipients: - smaginsergey1310@gmail.com -branches: - only: - - master diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 4b91b53..f8ba71e 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -72,15 +72,16 @@ def open(*args) # Usage: # home.locate('timetable').open_with :vim # home['.vimrc'].vim { other: '+55', x: true, u: 'other_vimrc', cmd: 'ls' } - def open_with(app, *args, **opts) - system open_command(app, *args, opts) + def open_with(app, *args) + system open_command(app, *args) end - def output_of(app, *args, **opts) - `#{open_command(app, *args, opts)}` + def output_of(app, *args) + `#{open_command(app, *args)}` end - def open_command(app, *args, **opts) + def open_command(app, *args) + opts = args.last.is_a?(Hash) ? args.pop : {} names = dir? ? '' : entries.map(&:to_s).join(' ') options = opts.map do |k, v| key = k.size == 1 ? "-#{k}" : "--#{k}" From 475f9f170002c802579ada3d9cc2ac954f10e0d8 Mon Sep 17 00:00:00 2001 From: Vlad Bokov Date: Mon, 11 May 2015 05:04:26 +0600 Subject: [PATCH 092/119] support osx subprocess test --- lib/rush/local.rb | 2 +- spec/process_spec.rb | 30 ++++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/lib/rush/local.rb b/lib/rush/local.rb index 024e664..591db1f 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -157,7 +157,7 @@ def processes windows_processes else os_x_processes - end + end.uniq end # Process list on Linux using /proc. diff --git a/spec/process_spec.rb b/spec/process_spec.rb index e935649..52c3905 100644 --- a/spec/process_spec.rb +++ b/spec/process_spec.rb @@ -1,22 +1,36 @@ require_relative 'base' describe Rush::Process do + let!(:pid_file) { "/tmp/rush_rspec_#{Process.pid}" } + before do - @pid = fork do + @parent_pid = fork do + # OSX includes checking `ps` process into current Process#children + # as such isolate parent from the main thread + # but the instance_valriabes are unavailable since #fork - use fs + @pid = fork do + sleep 999 + end + ::File.write(pid_file, @pid) sleep 999 end + + # wait parent to tell child's pid + sleep 0.3 + + @pid = ::File.read(pid_file).to_i @process = Rush::Process.all.detect { |p| p.pid == @pid } end after do + ::File.unlink(pid_file) if ::File.exists?(pid_file) system "kill -9 #{@pid}" + system "kill -9 #{@parent_pid}" end - unless RUBY_PLATFORM.match(/darwin/) # OS x reports pids weird - it 'knows all its child processes' do - parent = Rush::Process.all.detect { |p| p.pid == Process.pid } - expect(parent.children).to eq [@process] - end + it 'knows all its child processes' do + parent = Rush::Process.all.detect { |p| p.pid == @parent_pid } + expect(parent.children).to eq [@process] end it 'gets the list of all processes' do @@ -50,12 +64,12 @@ end it 'knows the parent process pid' do - expect(@process.parent_pid).to eq Process.pid + expect(@process.parent_pid).to eq @parent_pid end it 'knows the parent process' do this = Rush::Box.new.processes - .select { |p| p.pid == Process.pid } + .select { |p| p.pid == @parent_pid } .first expect(@process.parent).to eq this end From 53e33c7e6ebbc8ef361bd2f93c31b4bf327f9225 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Wed, 3 Jun 2015 16:52:25 +0300 Subject: [PATCH 093/119] Update dependencies --- .ruby-version | 1 + Gemfile.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 .ruby-version diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..ccbccc3 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.2.0 diff --git a/Gemfile.lock b/Gemfile.lock index a0fecce..35d35af 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -20,8 +20,8 @@ GEM multi_json (>= 1.7.5, < 2.0) nokogiri (~> 1.6.3) oauth2 - hashie (3.4.0) - highline (1.7.1) + hashie (3.4.2) + highline (1.7.2) jeweler (2.0.1) builder bundler (>= 1.0) @@ -31,7 +31,7 @@ GEM nokogiri (>= 1.5.10) rake rdoc - jwt (1.4.1) + jwt (1.5.0) method_source (0.8.2) mini_portile (0.6.2) multi_json (1.11.0) @@ -50,16 +50,16 @@ GEM coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - rack (1.6.0) + rack (1.6.1) rake (10.4.2) rdoc (4.2.0) rspec (3.2.0) rspec-core (~> 3.2.0) rspec-expectations (~> 3.2.0) rspec-mocks (~> 3.2.0) - rspec-core (3.2.2) + rspec-core (3.2.3) rspec-support (~> 3.2.0) - rspec-expectations (3.2.0) + rspec-expectations (3.2.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.2.0) rspec-mocks (3.2.1) From 09d57efe7b6a75e5a1fea915e5c4962ce8b9395a Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 12 Jul 2015 09:49:30 +0300 Subject: [PATCH 094/119] Add optional environment variables to commands --- lib/rush/commands.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index f8ba71e..c391b54 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -73,7 +73,7 @@ def open(*args) # home.locate('timetable').open_with :vim # home['.vimrc'].vim { other: '+55', x: true, u: 'other_vimrc', cmd: 'ls' } def open_with(app, *args) - system open_command(app, *args) + system(*open_command(app, *args)) end def output_of(app, *args) @@ -82,8 +82,9 @@ def output_of(app, *args) def open_command(app, *args) opts = args.last.is_a?(Hash) ? args.pop : {} + env = opts[:env] names = dir? ? '' : entries.map(&:to_s).join(' ') - options = opts.map do |k, v| + options = opts.reject { |k, _| k == :env }.map do |k, v| key = k.size == 1 ? "-#{k}" : "--#{k}" case when v == true then key @@ -91,6 +92,10 @@ def open_command(app, *args) else "#{key} #{v}" end end.join(' ') - "cd #{dirname}; #{app.to_s} #{names} #{options} #{args.join(' ')}" + cmd = "cd #{dirname}; #{app} #{names} #{options} #{args.join(' ')}" + if vars = opts[:env] + env = vars.inject({}) { |r, (k, v)| r.merge(k.to_s.upcase => v.to_s) } + end + vars ? [env, cmd] : cmd end end From 54fe5f6c6df4bcee296249e19a6531e4f6fd712e Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 12 Jul 2015 09:51:30 +0300 Subject: [PATCH 095/119] Document changes about env option --- lib/rush/commands.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index c391b54..3df656b 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -72,6 +72,7 @@ def open(*args) # Usage: # home.locate('timetable').open_with :vim # home['.vimrc'].vim { other: '+55', x: true, u: 'other_vimrc', cmd: 'ls' } + # home['my_app'].rails :c, env: { rails_env: 'test' } # environment variables def open_with(app, *args) system(*open_command(app, *args)) end From f07170a19be8630bd24e0598c6a0a318b6ec600a Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Fri, 14 Aug 2015 09:17:11 +0300 Subject: [PATCH 096/119] better ls implementation --- lib/rush/dir.rb | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/rush/dir.rb b/lib/rush/dir.rb index ddaf059..60ad8d4 100644 --- a/lib/rush/dir.rb +++ b/lib/rush/dir.rb @@ -137,16 +137,9 @@ def purge end # Text output of dir listing, equivalent to the regular unix shell's ls command. - def ls - out = [ "#{self}" ] - nonhidden_dirs.each do |dir| - out << " #{dir.name}/" - end - nonhidden_files.each do |file| - out << " #{file.name}" - end - out.join("\n") - end + def ls(*args) + output_of 'ls', *args + end # Run rake within this dir. def rake(*args) From 0cb601694fd91b73332eda3490b7c56f52204f85 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 16 Aug 2015 09:06:05 +0300 Subject: [PATCH 097/119] Print error backtrace only by request (wtf method) --- lib/rush/box.rb | 6 ++++++ lib/rush/shell.rb | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/rush/box.rb b/lib/rush/box.rb index b786ab9..f7a5e16 100644 --- a/lib/rush/box.rb +++ b/lib/rush/box.rb @@ -121,4 +121,10 @@ def make_connection # :nodoc: def ==(other) # :nodoc: host == other.host end + + # Print last backtrace + def wtf + t = $last_backtrace + t.lines.count > 5 ? t.less : puts(t) + end end diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 822f120..22db5a8 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -25,6 +25,7 @@ def initialize load_custom_commands set_readline @multiline_cmd = '' # Multiline commands should be stored somewhere + $last_backtrace = '' # Backtrace should too. end def set_readline @@ -75,7 +76,9 @@ def execute(cmd) @multiline_cmd = '' rescue ::Exception => e puts "Exception #{e.class} -> #{e.message}" - e.backtrace.each { |t| puts "\t#{::File.expand_path(t)}" } + $last_backtrace = e.backtrace + .map { |t| "\t#{::File.expand_path(t)}" } + .join("\n") @multiline_cmd = '' end From c9940ac91aa5ae2dee47c5db4ed78dcf1f20319c Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 17 Oct 2015 18:43:47 +0300 Subject: [PATCH 098/119] Better paths completion --- lib/rush/shell/completion.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rush/shell/completion.rb b/lib/rush/shell/completion.rb index 45d128a..94aabd9 100644 --- a/lib/rush/shell/completion.rb +++ b/lib/rush/shell/completion.rb @@ -50,7 +50,7 @@ def complete(input) method: :complete_method }, path: { - regexp: /^(\w|_|.|:)+[\[\/][\'\"].+$/, + regexp: /^(\w|_|.|:)+[\[\/][\'\"].*$/, method: :complete_path } } From a2a8fa4481509481d092bd009fcdacaee355ae4a Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 17 Oct 2015 20:55:23 +0300 Subject: [PATCH 099/119] update travis config --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 152cb04..2789abc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: ruby -sudo: true -script: "bundle exec rake" +sudo: false +script: "bundle exec rake spec" env: - CI=true rvm: From 030898d9d8c56a52c282101028abeb58024d96ff Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 24 Oct 2015 23:35:41 +0300 Subject: [PATCH 100/119] update dependencies; remove redundant method --- Gemfile.lock | 42 +++++++++++++++++++++--------------------- lib/rush/commands.rb | 5 ----- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 35d35af..ea98a79 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,19 +9,19 @@ GEM descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) diff-lcs (1.2.5) - faraday (0.9.1) + faraday (0.9.2) multipart-post (>= 1.2, < 3) git (1.2.9.1) - github_api (0.12.3) + github_api (0.12.4) addressable (~> 2.3) descendants_tracker (~> 0.0.4) faraday (~> 0.8, < 0.10) - hashie (>= 3.3) + hashie (>= 3.4) multi_json (>= 1.7.5, < 2.0) - nokogiri (~> 1.6.3) + nokogiri (~> 1.6.6) oauth2 hashie (3.4.2) - highline (1.7.2) + highline (1.7.8) jeweler (2.0.1) builder bundler (>= 1.0) @@ -31,13 +31,13 @@ GEM nokogiri (>= 1.5.10) rake rdoc - jwt (1.5.0) + jwt (1.5.1) method_source (0.8.2) mini_portile (0.6.2) - multi_json (1.11.0) + multi_json (1.11.2) multi_xml (0.5.5) multipart-post (2.0.0) - net-ssh (2.9.2) + net-ssh (3.0.1) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) oauth2 (1.0.0) @@ -46,26 +46,26 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (~> 1.2) - pry (0.10.1) + pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - rack (1.6.1) + rack (1.6.4) rake (10.4.2) rdoc (4.2.0) - rspec (3.2.0) - rspec-core (~> 3.2.0) - rspec-expectations (~> 3.2.0) - rspec-mocks (~> 3.2.0) - rspec-core (3.2.3) - rspec-support (~> 3.2.0) - rspec-expectations (3.2.1) + rspec (3.3.0) + rspec-core (~> 3.3.0) + rspec-expectations (~> 3.3.0) + rspec-mocks (~> 3.3.0) + rspec-core (3.3.2) + rspec-support (~> 3.3.0) + rspec-expectations (3.3.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.2.0) - rspec-mocks (3.2.1) + rspec-support (~> 3.3.0) + rspec-mocks (3.3.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.2.0) - rspec-support (3.2.2) + rspec-support (~> 3.3.0) + rspec-support (3.3.0) session (3.2.0) slop (3.6.0) thread_safe (0.3.5) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 3df656b..f58d032 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -56,11 +56,6 @@ def vi(*args) end alias_method :vim, :vi - # Invoke TextMate on one or more files - only works locally. - def mate(*args) - open_with('mate', *args) - end - # Open file with xdg-open. # Usage: # home.locate('mai_failz').open From a4f3142d98852cf7e436e6e8b5edb9f64cb104e3 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 25 Oct 2015 10:40:47 +0300 Subject: [PATCH 101/119] Remove redundant method --- lib/rush/commands.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index f58d032..e616e0e 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -46,16 +46,6 @@ def edit(*args) open_with ENV['EDITOR'], *args end - # Invoke vi on one or more files - only works locally. - def vi(*args) - if self.class == Rush::Dir - system "cd #{full_path}; vim" - else - open_with('vim', *args) - end - end - alias_method :vim, :vi - # Open file with xdg-open. # Usage: # home.locate('mai_failz').open From 72ef1325901b153fcda7783f1c2e74155573aa06 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 25 Oct 2015 12:34:23 +0300 Subject: [PATCH 102/119] change badge to drone.io one --- README.rdoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rdoc b/README.rdoc index 0ca3d84..350faef 100644 --- a/README.rdoc +++ b/README.rdoc @@ -2,8 +2,7 @@ rush is a unix integration library and an interactive shell which uses pure Ruby syntax. Walk directory trees; create, copy, search, and destroy files; find and kill processes - everything you'd normally do with shell commands, now in the strict and elegant world of Ruby. -{Build Status}[https://travis-ci.org/s-mage/rush] - +[![Build Status](https://drone.io/github.com/s-mage/rush/status.png)](https://drone.io/github.com/s-mage/rush/latest) == Install From cca5a936134169dd5368b51f06444ac93b6e89bb Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 25 Oct 2015 12:36:58 +0300 Subject: [PATCH 103/119] update drone badge --- README.rdoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 350faef..d902eeb 100644 --- a/README.rdoc +++ b/README.rdoc @@ -2,7 +2,9 @@ rush is a unix integration library and an interactive shell which uses pure Ruby syntax. Walk directory trees; create, copy, search, and destroy files; find and kill processes - everything you'd normally do with shell commands, now in the strict and elegant world of Ruby. -[![Build Status](https://drone.io/github.com/s-mage/rush/status.png)](https://drone.io/github.com/s-mage/rush/latest) +[![Build Status]()](https://drone.io/github.com/s-mage/rush/latest) +{Build Status}[https://drone.io/github.com/s-mage/rush] + == Install From cce845745387fe9780d4e1742dd84200a8025b1c Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sun, 25 Oct 2015 12:37:24 +0300 Subject: [PATCH 104/119] typo --- README.rdoc | 1 - 1 file changed, 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index d902eeb..40c4dbe 100644 --- a/README.rdoc +++ b/README.rdoc @@ -2,7 +2,6 @@ rush is a unix integration library and an interactive shell which uses pure Ruby syntax. Walk directory trees; create, copy, search, and destroy files; find and kill processes - everything you'd normally do with shell commands, now in the strict and elegant world of Ruby. -[![Build Status]()](https://drone.io/github.com/s-mage/rush/latest) {Build Status}[https://drone.io/github.com/s-mage/rush] From 7ebbda22fc75384c6034898fc15efb40c0f406a7 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Mon, 2 Nov 2015 08:57:41 +0300 Subject: [PATCH 105/119] Add basic support to execute strings with executables --- lib/rush/string_ext.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/rush/string_ext.rb b/lib/rush/string_ext.rb index 5e69f6f..7db0dcb 100644 --- a/lib/rush/string_ext.rb +++ b/lib/rush/string_ext.rb @@ -1,3 +1,5 @@ +require_relative 'path' + class String include Rush::HeadTail @@ -13,4 +15,17 @@ def dir? def locate Rush::Dir.new(ENV['HOME']).locate self end + + def open_with(meth, *args, &block) + if executables.include? meth.to_s + system [meth.to_s, *args, self].join(' ') + else + raise 'No such executable. Maybe something wrong with PATH?' + end + end + alias_method :e, :open_with + + def executables + Rush::Path.executables + end end From f1223f97a6cb98eb03523c74b1e460758abad31e Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Thu, 26 Nov 2015 07:38:45 +0300 Subject: [PATCH 106/119] Add very basic support of pipes --- lib/rush.rb | 2 +- lib/rush/string_ext.rb | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/rush.rb b/lib/rush.rb index 3c9cc82..a8d43c1 100644 --- a/lib/rush.rb +++ b/lib/rush.rb @@ -74,6 +74,7 @@ module Rush::Connection; end require_relative 'rush/config' require_relative 'rush/commands' require_relative 'rush/access' +require_relative 'rush/path' require_relative 'rush/entry' require_relative 'rush/file' require_relative 'rush/dir' @@ -88,4 +89,3 @@ module Rush::Connection; end require_relative 'rush/local' require_relative 'rush/box' require_relative 'rush/embeddable_shell' -require_relative 'rush/path' diff --git a/lib/rush/string_ext.rb b/lib/rush/string_ext.rb index 7db0dcb..6c118fa 100644 --- a/lib/rush/string_ext.rb +++ b/lib/rush/string_ext.rb @@ -1,5 +1,3 @@ -require_relative 'path' - class String include Rush::HeadTail @@ -25,6 +23,10 @@ def open_with(meth, *args, &block) end alias_method :e, :open_with + def |(meth, *args, &block) + Open3.capture2(meth, stdin_data: self).first + end + def executables Rush::Path.executables end From 3e5b18c269fd1a12a78bffe43bc0ebb1000cd935 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 2 Jan 2016 10:19:23 +0300 Subject: [PATCH 107/119] Dir contents is now includes hidden files --- lib/rush/dir.rb | 2 +- lib/rush/shell/completion.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rush/dir.rb b/lib/rush/dir.rb index 60ad8d4..747101d 100644 --- a/lib/rush/dir.rb +++ b/lib/rush/dir.rb @@ -23,7 +23,7 @@ def full_path # Entries contained within this dir - not recursive. def contents - find_by_glob('*') + find_by_glob('*') + find_by_glob('.*') end # Files contained in this dir only. diff --git a/lib/rush/shell/completion.rb b/lib/rush/shell/completion.rb index 94aabd9..40b0414 100644 --- a/lib/rush/shell/completion.rb +++ b/lib/rush/shell/completion.rb @@ -50,13 +50,13 @@ def complete(input) method: :complete_method }, path: { - regexp: /^(\w|_|.|:)+[\[\/][\'\"].*$/, + regexp: /^(\w|_|.|:| )+[\[\/][\'\"].*$/, method: :complete_path } } def complete_constant(input) - Object.constants.map(&:to_s).select { |x| x.start_with? input } .sort + Object.constants.map(&:to_s).select { |x| x.start_with? input }.sort end def complete_object_constant(input) @@ -64,7 +64,7 @@ def complete_object_constant(input) eval(receiver, pure_binding).constants .map(&:to_s) .select { |x| x.start_with? const_part } - .map { |x| receiver + delimiter + x } + .map { |x| receiver + delimiter + x } end def complete_global_method(input) From 3a016eadfbf9a3f51aa2510d0a542ccac7e115ca Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 2 Jan 2016 10:38:30 +0300 Subject: [PATCH 108/119] Quote paths before opening apps --- lib/rush/commands.rb | 5 +++-- lib/rush/entry.rb | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index e616e0e..61b4684 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -69,7 +69,7 @@ def output_of(app, *args) def open_command(app, *args) opts = args.last.is_a?(Hash) ? args.pop : {} env = opts[:env] - names = dir? ? '' : entries.map(&:to_s).join(' ') + names = dir? ? '' : entries.map { |x| Rush.quote x.to_s }.join(' ') options = opts.reject { |k, _| k == :env }.map do |k, v| key = k.size == 1 ? "-#{k}" : "--#{k}" case @@ -78,7 +78,8 @@ def open_command(app, *args) else "#{key} #{v}" end end.join(' ') - cmd = "cd #{dirname}; #{app} #{names} #{options} #{args.join(' ')}" + dir = Rush.quote dirname.to_s + cmd = "cd #{dir}; #{app} #{names} #{options} #{args.join(' ')}" if vars = opts[:env] env = vars.inject({}) { |r, (k, v)| r.merge(k.to_s.upcase => v.to_s) } end diff --git a/lib/rush/entry.rb b/lib/rush/entry.rb index 4c9e483..29c634e 100644 --- a/lib/rush/entry.rb +++ b/lib/rush/entry.rb @@ -39,7 +39,7 @@ def self.factory(full_path, box=nil) def to_s # :nodoc: if box.host == 'localhost' - "#{full_path}" + full_path else inspect end @@ -59,7 +59,7 @@ def parent end def full_path - "#{@path}/#{@name}" + "#{path}/#{name}" end def quoted_path From 1cfc3808f9a870ce2dcbbde9537f81716e907e97 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 6 Feb 2016 18:27:49 +0300 Subject: [PATCH 109/119] Make locate case-insensitive --- lib/rush/dir.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rush/dir.rb b/lib/rush/dir.rb index 747101d..20d7878 100644 --- a/lib/rush/dir.rb +++ b/lib/rush/dir.rb @@ -49,7 +49,7 @@ def [](key) alias_method :/, :[] def locate(path) - located = bash("locate #{path}").split("\n"). + located = bash("locate -i #{path}").split("\n"). map { |x| x.dir? ? Rush::Dir.new(x) : Rush::File.new(x) } located.size == 1 ? located.first : located end From 4c6828ac118cf74a8b8d0b2aecb23ad538f10ca7 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 5 Mar 2016 10:06:16 +0300 Subject: [PATCH 110/119] Fix tests; refactor open_command --- lib/rush/commands.rb | 29 +++++++++++++++++------------ lib/rush/dir.rb | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index 61b4684..a583214 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -1,5 +1,6 @@ # The commands module contains operations against Rush::File entries, and is -# mixed in to Rush::Entry and Array. This means you can run these commands against a single +# mixed in to Rush::Entry and Array. +# This means you can run these commands against a single # file, a dir full of files, or an arbitrary list of files. # # Examples: @@ -12,7 +13,7 @@ module Rush::Commands # The entries command must return an array of Rush::Entry items. This # varies by class that it is mixed in to. def entries - raise "must define me in class mixed in to for command use" + fail 'must define me in class mixed in to for command use' end # Search file contents for a regular expression. A Rush::SearchResults @@ -57,7 +58,7 @@ def open(*args) # Usage: # home.locate('timetable').open_with :vim # home['.vimrc'].vim { other: '+55', x: true, u: 'other_vimrc', cmd: 'ls' } - # home['my_app'].rails :c, env: { rails_env: 'test' } # environment variables + # home['my_app'].rails :c, env: { rails_env: 'test' } # environment vars def open_with(app, *args) system(*open_command(app, *args)) end @@ -66,18 +67,22 @@ def output_of(app, *args) `#{open_command(app, *args)}` end + def opt_to_s(k, v) + key = k.size == 1 ? "-#{k}" : "--#{k}" + case + when v == true then key + when k == 'other' || k == :other then v + else "#{key} #{v}" + end + end + def open_command(app, *args) opts = args.last.is_a?(Hash) ? args.pop : {} - env = opts[:env] names = dir? ? '' : entries.map { |x| Rush.quote x.to_s }.join(' ') - options = opts.reject { |k, _| k == :env }.map do |k, v| - key = k.size == 1 ? "-#{k}" : "--#{k}" - case - when v == true then key - when k == 'other' || k == :other then v - else "#{key} #{v}" - end - end.join(' ') + options = opts + .reject { |k, _| k == :env } + .map { |k, v| opt_to_s(k, v) } + .join(' ') dir = Rush.quote dirname.to_s cmd = "cd #{dir}; #{app} #{names} #{options} #{args.join(' ')}" if vars = opts[:env] diff --git a/lib/rush/dir.rb b/lib/rush/dir.rb index 20d7878..d30ba41 100644 --- a/lib/rush/dir.rb +++ b/lib/rush/dir.rb @@ -23,7 +23,7 @@ def full_path # Entries contained within this dir - not recursive. def contents - find_by_glob('*') + find_by_glob('.*') + find_by_glob('*') + find_by_glob('.[!.]*') end # Files contained in this dir only. From 9e623936a2f3c0ba03c066610cc52e713387d91c Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 5 Mar 2016 10:12:10 +0300 Subject: [PATCH 111/119] Bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a3df0a6..ac39a10 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.0 +0.9.0 From 26c3b438051743f7a806d3a48fbad199537bb9b5 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 5 Mar 2016 10:19:05 +0300 Subject: [PATCH 112/119] Generate gemspec --- rush2.gemspec | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rush2.gemspec b/rush2.gemspec index edffd34..40b8bd7 100644 --- a/rush2.gemspec +++ b/rush2.gemspec @@ -2,16 +2,16 @@ # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- -# stub: rush2 0.8.0 ruby lib +# stub: rush2 0.9.0 ruby lib Gem::Specification.new do |s| s.name = "rush2" - s.version = "0.8.0" + s.version = "0.9.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] s.authors = ["Sergey Smagin"] - s.date = "2015-03-29" + s.date = "2016-03-05" s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." s.email = "smaginsergey1310@gmail.com" s.executables = ["rush", "rushd"] @@ -41,6 +41,7 @@ Gem::Specification.new do |s| "lib/rush/fixnum_ext.rb", "lib/rush/head_tail.rb", "lib/rush/local.rb", + "lib/rush/path.rb", "lib/rush/process.rb", "lib/rush/process_set.rb", "lib/rush/search_results.rb", @@ -60,6 +61,7 @@ Gem::Specification.new do |s| "spec/find_by_spec.rb", "spec/fixnum_ext_spec.rb", "spec/local_spec.rb", + "spec/path_spec.rb", "spec/process_set_spec.rb", "spec/process_spec.rb", "spec/rush_spec.rb", @@ -70,7 +72,7 @@ Gem::Specification.new do |s| ] s.homepage = "https://github.com/s-mage/rush" s.licenses = ["MIT"] - s.rubygems_version = "2.4.5" + s.rubygems_version = "2.5.0" s.summary = "A Ruby replacement for bash+ssh." if s.respond_to? :specification_version then From 17d05a2605071493061587e91af668e961b916c1 Mon Sep 17 00:00:00 2001 From: Sergey Smagin Date: Sat, 5 Mar 2016 10:27:09 +0300 Subject: [PATCH 113/119] Update dependencies --- Gemfile.lock | 55 ++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index ea98a79..9871f1d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ GEM remote: http://rubygems.org/ specs: - addressable (2.3.8) + addressable (2.4.0) builder (3.2.2) - coderay (1.1.0) + coderay (1.1.1) coolline (0.5.0) unicode_utils (~> 1.4) descendants_tracker (0.0.4) @@ -11,16 +11,15 @@ GEM diff-lcs (1.2.5) faraday (0.9.2) multipart-post (>= 1.2, < 3) - git (1.2.9.1) - github_api (0.12.4) - addressable (~> 2.3) + git (1.3.0) + github_api (0.13.1) + addressable (~> 2.4.0) descendants_tracker (~> 0.0.4) faraday (~> 0.8, < 0.10) hashie (>= 3.4) multi_json (>= 1.7.5, < 2.0) - nokogiri (~> 1.6.6) oauth2 - hashie (3.4.2) + hashie (3.4.3) highline (1.7.8) jeweler (2.0.1) builder @@ -31,41 +30,43 @@ GEM nokogiri (>= 1.5.10) rake rdoc + json (1.8.3) jwt (1.5.1) method_source (0.8.2) - mini_portile (0.6.2) + mini_portile2 (2.0.0) multi_json (1.11.2) multi_xml (0.5.5) multipart-post (2.0.0) - net-ssh (3.0.1) - nokogiri (1.6.6.2) - mini_portile (~> 0.6.0) - oauth2 (1.0.0) + net-ssh (3.0.2) + nokogiri (1.6.7.2) + mini_portile2 (~> 2.0.0.rc2) + oauth2 (1.1.0) faraday (>= 0.8, < 0.10) - jwt (~> 1.0) + jwt (~> 1.0, < 1.5.2) multi_json (~> 1.3) multi_xml (~> 0.5) - rack (~> 1.2) + rack (>= 1.2, < 3) pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) rack (1.6.4) - rake (10.4.2) - rdoc (4.2.0) - rspec (3.3.0) - rspec-core (~> 3.3.0) - rspec-expectations (~> 3.3.0) - rspec-mocks (~> 3.3.0) - rspec-core (3.3.2) - rspec-support (~> 3.3.0) - rspec-expectations (3.3.1) + rake (10.5.0) + rdoc (4.2.2) + json (~> 1.4) + rspec (3.4.0) + rspec-core (~> 3.4.0) + rspec-expectations (~> 3.4.0) + rspec-mocks (~> 3.4.0) + rspec-core (3.4.3) + rspec-support (~> 3.4.0) + rspec-expectations (3.4.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.3.0) - rspec-mocks (3.3.2) + rspec-support (~> 3.4.0) + rspec-mocks (3.4.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.3.0) - rspec-support (3.3.0) + rspec-support (~> 3.4.0) + rspec-support (3.4.1) session (3.2.0) slop (3.6.0) thread_safe (0.3.5) From 605614bee490842cba8e35a7798167d888f8a749 Mon Sep 17 00:00:00 2001 From: naja melan Date: Mon, 27 Jun 2016 16:47:29 +0200 Subject: [PATCH 114/119] Add functionality for hard and symlinking files and directories --- lib/rush/entry.rb | 11 +++++++++++ lib/rush/file.rb | 6 ++++++ lib/rush/local.rb | 23 +++++++++++++++++++++++ spec/entry_spec.rb | 29 +++++++++++++++++++++++++++++ spec/file_spec.rb | 12 ++++++++++++ 5 files changed, 81 insertions(+) diff --git a/lib/rush/entry.rb b/lib/rush/entry.rb index 29c634e..9dc982f 100644 --- a/lib/rush/entry.rb +++ b/lib/rush/entry.rb @@ -132,6 +132,17 @@ def move_to(dir) mimic(moved) end + # Symlink the file (see File.ln for options) + def slink(dst, options = {}) + connection.ln_s(full_path, dst, options) + self.class.new(dst, box) + end + + # Return true if the entry is a symlink. + def symlink? + connection.symlink? full_path + end + def mimic(from) # :nodoc: @box = from.box @path = from.path diff --git a/lib/rush/file.rb b/lib/rush/file.rb index bb6051e..f763d76 100644 --- a/lib/rush/file.rb +++ b/lib/rush/file.rb @@ -16,6 +16,12 @@ def create end alias_method :touch, :create + # Hardlink the file (see File.ln for options) + def link(dst, options = {}) + connection.ln(full_path, dst, options) + self.class.new(dst, box) + end + # Size in bytes on disk. def size stat[:size] diff --git a/lib/rush/local.rb b/lib/rush/local.rb index 591db1f..431aa79 100644 --- a/lib/rush/local.rb +++ b/lib/rush/local.rb @@ -76,6 +76,27 @@ def copy(src, dst) true end + # Create a hard link to another file + def ln(src, dst, options = {}) + raise(Rush::DoesNotExist, src) unless File.exists?(src) + raise(Rush::DoesNotExist, File.dirname(dst)) unless File.exists?(File.dirname(dst)) + FileUtils.ln(src, dst, options) + true + end + + # Create a symbolic link to another file + def ln_s(src, dst, options = {}) + raise(Rush::DoesNotExist, src) unless File.exists?(src) + raise(Rush::DoesNotExist, File.dirname(dst)) unless File.exists?(File.dirname(dst)) + FileUtils.ln_s(src, dst, options) + true + end + + # Is this path a symlink? + def symlink?(path) + File.symlink? path + end + # Create an in-memory archive (tgz) of a file or dir, which can be # transmitted to another server for a copy or move. Note that archive # operations have the dir name implicit in the archive. @@ -354,6 +375,8 @@ def receive(params) when 'create_dir' then create_dir(params[:full_path]) when 'rename' then rename(params[:path], params[:name], params[:new_name]) when 'copy' then copy(params[:src], params[:dst]) + when 'ln' then ln(params[:src], params[:dst], params[:options]) + when 'ln_s' then ln_s(params[:src], params[:dst], params[:options]) when 'read_archive' then read_archive(params[:full_path]) when 'write_archive' then write_archive(params[:payload], params[:dir]) when 'index' then index(params[:base_path], params[:glob]).join("\n") + "\n" diff --git a/spec/entry_spec.rb b/spec/entry_spec.rb index 0dd3df5..03e7740 100644 --- a/spec/entry_spec.rb +++ b/spec/entry_spec.rb @@ -99,6 +99,35 @@ expect(@copied_dir.full_path).to eq "#{@sandbox_dir}newdir/#{@entry.name}" end + it 'can symlink itself as file' do + newdir = "#{@sandbox_dir}newdir" + system "mkdir -p #{newdir}" + + dst = newdir + "/link" + link = @entry.slink(dst) + + expect(File.exist?(dst)).to eq true + expect(File.symlink?(dst)).to eq true + expect(link.symlink?).to eq true + + expect(link.full_path).to eq dst + end + + it 'can symlink itself as directory' do + newdir = "#{@sandbox_dir}newdir" + system "mkdir -p #{newdir}" + dir = Rush::Dir.new( newdir + "/direct/") + dir.create + + dst = newdir + "/linked" + link = dir.slink(dst) + + expect(Dir.exist?(dst)).to eq true + expect(File.symlink?(dst)).to eq true + + expect(link.full_path).to eq dst + '/' + end + it 'considers dotfiles to be hidden' do expect(Rush::Entry.new("#{@sandbox_dir}/show")).to_not be_hidden expect(Rush::Entry.new("#{@sandbox_dir}/.dont_show")).to be_hidden diff --git a/spec/file_spec.rb b/spec/file_spec.rb index 5fc66f1..1e044f1 100644 --- a/spec/file_spec.rb +++ b/spec/file_spec.rb @@ -31,6 +31,18 @@ expect(File.exist?("#{@sandbox_dir}/create_me")).to eq true end + it 'can hardlink itself' do + newdir = "#{@sandbox_dir}newdir" + system "mkdir -p #{newdir}" + + dst = newdir + "/link" + link = @file.link(dst) + + expect(File.exist?(dst)).to eq true + + expect(link.full_path).to eq dst + end + it 'knows its size in bytes' do expect(@file.size).to eq @contents.length end From d5bdcd46ed204a0dbee4aa5e0467c86c46f13b80 Mon Sep 17 00:00:00 2001 From: naja melan Date: Mon, 27 Jun 2016 23:44:00 +0200 Subject: [PATCH 115/119] rename slink to symlink --- lib/rush/entry.rb | 2 +- spec/entry_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rush/entry.rb b/lib/rush/entry.rb index 9dc982f..8f4eb39 100644 --- a/lib/rush/entry.rb +++ b/lib/rush/entry.rb @@ -133,7 +133,7 @@ def move_to(dir) end # Symlink the file (see File.ln for options) - def slink(dst, options = {}) + def symlink(dst, options = {}) connection.ln_s(full_path, dst, options) self.class.new(dst, box) end diff --git a/spec/entry_spec.rb b/spec/entry_spec.rb index 03e7740..c2e948a 100644 --- a/spec/entry_spec.rb +++ b/spec/entry_spec.rb @@ -104,7 +104,7 @@ system "mkdir -p #{newdir}" dst = newdir + "/link" - link = @entry.slink(dst) + link = @entry.symlink(dst) expect(File.exist?(dst)).to eq true expect(File.symlink?(dst)).to eq true @@ -120,7 +120,7 @@ dir.create dst = newdir + "/linked" - link = dir.slink(dst) + link = dir.symlink(dst) expect(Dir.exist?(dst)).to eq true expect(File.symlink?(dst)).to eq true From a911f5b67a5a4329abf92dcf97287743bc789b3f Mon Sep 17 00:00:00 2001 From: Sergei Smagin Date: Sat, 25 Feb 2017 10:31:14 +0800 Subject: [PATCH 116/119] Dont track ruby version --- .gitignore | 2 +- .ruby-version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c3de711..ca8fcee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ rdoc pkg -.rbenv-version +.ruby-version *.swp *.gem doc/ diff --git a/.ruby-version b/.ruby-version index ccbccc3..197c4d5 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.2.0 +2.4.0 From d1891d053e2562d2ec6ae3d4b5d24b6b93f918d3 Mon Sep 17 00:00:00 2001 From: Sergei Smagin Date: Sat, 24 Jun 2017 09:16:23 +0300 Subject: [PATCH 117/119] Version bump to 0.10.0 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ac39a10..2774f85 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.0 +0.10.0 \ No newline at end of file From 6ffb201ae3627f78048b87270fb3949ffaefbb19 Mon Sep 17 00:00:00 2001 From: Sergei Smagin Date: Sat, 24 Jun 2017 09:20:09 +0300 Subject: [PATCH 118/119] Updated dependencies; minor refactoring; version bump --- .ruby-version | 2 +- Gemfile.lock | 80 +++++++++++++++++++++++-------------------- Rakefile | 3 +- VERSION | 2 +- lib/rush/array_ext.rb | 5 +-- lib/rush/commands.rb | 3 +- lib/rush/shell.rb | 6 ++-- 7 files changed, 51 insertions(+), 50 deletions(-) diff --git a/.ruby-version b/.ruby-version index 197c4d5..276cbf9 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.4.0 +2.3.0 diff --git a/Gemfile.lock b/Gemfile.lock index 9871f1d..7cbcb9b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,74 +2,77 @@ GEM remote: http://rubygems.org/ specs: addressable (2.4.0) - builder (3.2.2) + builder (3.2.3) coderay (1.1.1) coolline (0.5.0) unicode_utils (~> 1.4) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - diff-lcs (1.2.5) + diff-lcs (1.3) faraday (0.9.2) multipart-post (>= 1.2, < 3) git (1.3.0) - github_api (0.13.1) + github_api (0.16.0) addressable (~> 2.4.0) descendants_tracker (~> 0.0.4) faraday (~> 0.8, < 0.10) hashie (>= 3.4) - multi_json (>= 1.7.5, < 2.0) - oauth2 - hashie (3.4.3) + mime-types (>= 1.16, < 3.0) + oauth2 (~> 1.0) + hashie (3.5.5) highline (1.7.8) - jeweler (2.0.1) + jeweler (2.3.7) builder - bundler (>= 1.0) + bundler (>= 1) git (>= 1.2.5) - github_api + github_api (~> 0.16.0) highline (>= 1.6.15) nokogiri (>= 1.5.10) + psych (~> 2.2) rake rdoc - json (1.8.3) - jwt (1.5.1) + semver2 + jwt (1.5.6) method_source (0.8.2) - mini_portile2 (2.0.0) - multi_json (1.11.2) - multi_xml (0.5.5) + mime-types (2.99.3) + mini_portile2 (2.2.0) + multi_json (1.12.1) + multi_xml (0.6.0) multipart-post (2.0.0) - net-ssh (3.0.2) - nokogiri (1.6.7.2) - mini_portile2 (~> 2.0.0.rc2) - oauth2 (1.1.0) - faraday (>= 0.8, < 0.10) - jwt (~> 1.0, < 1.5.2) + net-ssh (4.1.0) + nokogiri (1.8.0) + mini_portile2 (~> 2.2.0) + oauth2 (1.4.0) + faraday (>= 0.8, < 0.13) + jwt (~> 1.0) multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - pry (0.10.3) + pry (0.10.4) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - rack (1.6.4) - rake (10.5.0) - rdoc (4.2.2) - json (~> 1.4) - rspec (3.4.0) - rspec-core (~> 3.4.0) - rspec-expectations (~> 3.4.0) - rspec-mocks (~> 3.4.0) - rspec-core (3.4.3) - rspec-support (~> 3.4.0) - rspec-expectations (3.4.0) + psych (2.2.4) + rack (2.0.3) + rake (12.0.0) + rdoc (5.1.0) + rspec (3.6.0) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) + rspec-core (3.6.0) + rspec-support (~> 3.6.0) + rspec-expectations (3.6.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) - rspec-mocks (3.4.1) + rspec-support (~> 3.6.0) + rspec-mocks (3.6.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) - rspec-support (3.4.1) + rspec-support (~> 3.6.0) + rspec-support (3.6.0) + semver2 (3.4.2) session (3.2.0) slop (3.6.0) - thread_safe (0.3.5) + thread_safe (0.3.6) unicode_utils (1.4.0) PLATFORMS @@ -84,3 +87,6 @@ DEPENDENCIES rake rspec session + +BUNDLED WITH + 1.14.5 diff --git a/Rakefile b/Rakefile index 623a14d..beea3df 100644 --- a/Rakefile +++ b/Rakefile @@ -5,7 +5,7 @@ Jeweler::Tasks.new do |s| s.name = "rush2" s.summary = "A Ruby replacement for bash+ssh." s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." - s.author = "Sergey Smagin" + s.author = "Sergei Smagin" s.email = "smaginsergey1310@gmail.com" s.homepage = "https://github.com/s-mage/rush" s.licenses = ['MIT'] @@ -54,4 +54,3 @@ Rake::RDocTask.new do |t| t.rdoc_files.include('lib/rush.rb') t.rdoc_files.include('lib/rush/*.rb') end - diff --git a/VERSION b/VERSION index 2774f85..78bc1ab 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.10.0 \ No newline at end of file +0.10.0 diff --git a/lib/rush/array_ext.rb b/lib/rush/array_ext.rb index 5e68a2f..8ab79b2 100644 --- a/lib/rush/array_ext.rb +++ b/lib/rush/array_ext.rb @@ -9,11 +9,8 @@ class Array include Rush::Commands - def entries - self - end + def entries; self; end include Rush::FindBy - include Rush::HeadTail end diff --git a/lib/rush/commands.rb b/lib/rush/commands.rb index a583214..b42f274 100644 --- a/lib/rush/commands.rb +++ b/lib/rush/commands.rb @@ -51,7 +51,8 @@ def edit(*args) # Usage: # home.locate('mai_failz').open def open(*args) - open_with('xdg-open', *args) + cmd = RUBY_PLATFORM.match(/darwin/) ? 'open' : 'xdg-open' + open_with(cmd, *args) end # Open file with any application you like. diff --git a/lib/rush/shell.rb b/lib/rush/shell.rb index 22db5a8..bca494a 100644 --- a/lib/rush/shell.rb +++ b/lib/rush/shell.rb @@ -24,7 +24,7 @@ def initialize $last_res = nil load_custom_commands set_readline - @multiline_cmd = '' # Multiline commands should be stored somewhere + @multiline_cmd = '' # Multiline commands should be stored somewhere $last_backtrace = '' # Backtrace should too. end @@ -41,9 +41,7 @@ def set_readline def load_custom_commands eval config.load_env, @pure_binding commands = config.load_commands - Rush::Dir.class_eval commands - Rush::File.class_eval commands - Array.class_eval commands + [Rush::Dir, Rush::File, Array].each { |x| x.class_eval commands } end # Run the interactive shell using coolline. From c90920f719457e067ddab96ba4cdf21dd0f15bae Mon Sep 17 00:00:00 2001 From: Sergei Smagin Date: Sat, 24 Jun 2017 09:25:26 +0300 Subject: [PATCH 119/119] Updated gemspec --- rush2.gemspec | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rush2.gemspec b/rush2.gemspec index 40b8bd7..106a989 100644 --- a/rush2.gemspec +++ b/rush2.gemspec @@ -2,16 +2,16 @@ # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- -# stub: rush2 0.9.0 ruby lib +# stub: rush2 0.10.0 ruby lib Gem::Specification.new do |s| s.name = "rush2" - s.version = "0.9.0" + s.version = "0.10.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] - s.authors = ["Sergey Smagin"] - s.date = "2016-03-05" + s.authors = ["Sergei Smagin"] + s.date = "2017-06-24" s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client." s.email = "smaginsergey1310@gmail.com" s.executables = ["rush", "rushd"] @@ -72,7 +72,7 @@ Gem::Specification.new do |s| ] s.homepage = "https://github.com/s-mage/rush" s.licenses = ["MIT"] - s.rubygems_version = "2.5.0" + s.rubygems_version = "2.5.1" s.summary = "A Ruby replacement for bash+ssh." if s.respond_to? :specification_version then