Module: RailsPwnerer::Base
- Included in:
- App::Assets, App::ClusterConfig, App::Config, App::Database, App::Files, App::Gems, App::Git, App::NginxConfig, App::Perforce, App::Scripts, App::Svn, Config, DevExecutor, Scaffolds::Config, Scaffolds::DirPermissions, Scaffolds::Dirs, Scaffolds::Gems, Scaffolds::HookDaemon, Scaffolds::HookDyndns, Scaffolds::MysqlConfig, Scaffolds::Packages, Scaffolds::RubyGems, Scaffolds::Sshd, Util
- Defined in:
- lib/rails_pwnerer/base/gems.rb,
lib/rails_pwnerer/base.rb,
lib/rails_pwnerer/base/cpus.rb,
lib/rails_pwnerer/base/dirs.rb,
lib/rails_pwnerer/base/gems.rb,
lib/rails_pwnerer/base/input.rb,
lib/rails_pwnerer/base/rails.rb,
lib/rails_pwnerer/base/atomics.rb,
lib/rails_pwnerer/base/process.rb,
lib/rails_pwnerer/base/startup.rb,
lib/rails_pwnerer/base/hostname.rb,
lib/rails_pwnerer/base/packages.rb,
lib/rails_pwnerer/base/packages.rb
Overview
extends Base with Rails-related functions
Class Method Summary collapse
-
._setup_unix ⇒ Object
initializes the module in UNIX mode.
-
._setup_windows ⇒ Object
initializes the module in Windows mode.
- .all_packages ⇒ Object
-
.all_packages_without_caching ⇒ Object
A hash of all the packages in the system, associated with their versions.
- .package_info_hash(output) ⇒ Object
Instance Method Summary collapse
-
#atomic_erase(path, name) ⇒ Object
erases a repository.
-
#atomic_read(path, name) ⇒ Object
reads the data in a repository.
-
#atomic_write(data, path, name, options = {}) ⇒ Object
writes data to a repository.
-
#best_package_matching(patterns) ⇒ Object
Package info for the best package matching a pattern or set of patterns.
-
#check_rails_root(app_path = '.') ⇒ Object
check if the given path is the root of a Rails application.
- #control_boot_script(script_name, action = :restart) ⇒ Object
-
#cpu_cores ⇒ Object
returns information for each core in the system.
-
#current_user ⇒ Object
gets the currently logged on user.
-
#gem_exists?(gem_name) ⇒ Boolean
checks if a gem exists.
-
#gid_for_username(name) ⇒ Object
gets the GID associated with the username.
-
#group_for_username(name) ⇒ Object
gets the main group of the given user.
-
#hook_boot_script(script_location, script_name = File.basename(script_location), options = {}) ⇒ Object
hooks a script into the boot sequence.
-
#install_gem(gem_name) ⇒ Object
TODO: use the Gem API instead of the command line.
- #install_gems(gem_names) ⇒ Object
-
#install_package(package_name, options = {}) ⇒ Object
Installs a package.
-
#install_package_impl(package_name, options) ⇒ Object
Internals for install_package.
-
#install_package_matching(patterns, options = {}) ⇒ Object
Installs a package matching a pattern or list of patterns.
- #install_packages(package_names, options = {}) ⇒ Object
- #kill_tree(pid) ⇒ Object
-
#os_distro ⇒ Object
the distribution of the OS.
-
#path_to_boot_script(script_name) ⇒ Object
returns the filesystem path to a boot script.
-
#path_to_boot_script_defaults(script_name) ⇒ Object
returns the filesystem path to the defaults used by a boot script (or nil if unsupported).
-
#path_to_gemdir ⇒ Object
locates the main file in a gem (used to locate the gem).
-
#process_info(pid = nil) ⇒ Object
returns information about a process.
- #prompt_user_for_password(prompt, fail_prompt) ⇒ Object
-
#remove_package(package_name, options = {}) ⇒ Object
Removes a package.
- #remove_packages(package_names) ⇒ Object
-
#search_packages(pattern) ⇒ Object
Searches for packages matching a name.
-
#uid_for_username(name) ⇒ Object
gets the UID associated with the username.
-
#unroll_collection(arg, &proc) ⇒ Object
unrolls a collection.
-
#update_all_packages(options = {}) ⇒ Object
Upgrades all the packages on the system to the latest version.
-
#update_all_packages_impl(options) ⇒ Object
Internals for upgrade_all_packages.
-
#update_gems ⇒ Object
update the metadata for all the gems.
-
#update_package_metadata(options = {}) ⇒ Object
Updates the metadata for all the packages.
- #upgrade_gem(gem_name) ⇒ Object
- #upgrade_gems(gem_names) ⇒ Object
-
#upgrade_package(package_name, options = {}) ⇒ Object
Upgrades a package to the latest version.
-
#upgrade_package_impl(package_name, options) ⇒ Object
Internals for upgrade_package.
- #upgrade_packages(package_names, options = {}) ⇒ Object
-
#with_package_source(source_url, source_repos = [], options = {}) ⇒ Object
Executes the given block in the context of having new package sources.
-
#with_temp_dir(options = {}) ⇒ Object
executes a block in a temporary directory.
Class Method Details
._setup_unix ⇒ Object
initializes the module in UNIX mode
3 4 5 |
# File 'lib/rails_pwnerer/base.rb', line 3 def self._setup_unix #SUDO_PREFIX = 'sudo ' end |
._setup_windows ⇒ Object
initializes the module in Windows mode
8 9 10 |
# File 'lib/rails_pwnerer/base.rb', line 8 def self._setup_windows #SUDO_PREFIX = '' end |
.all_packages ⇒ Object
220 221 222 |
# File 'lib/rails_pwnerer/base/packages.rb', line 220 def self.all_packages @packages ||= all_packages_without_caching end |
.all_packages_without_caching ⇒ Object
A hash of all the packages in the system, associated with their versions.
This method is slow as hell, so it’s memoized in all_packages.
227 228 229 230 231 232 233 234 |
# File 'lib/rails_pwnerer/base/packages.rb', line 227 def self.all_packages_without_caching output = Kernel.` "apt-cache search --full ." versions = output.split("\n\n").map(&:strip).reject(&:empty?).map { |info| info_hash = package_info_hash info [info_hash['Package'], info_hash['Version']] } Hash[*(versions.flatten)] end |
.package_info_hash(output) ⇒ Object
236 237 238 239 |
# File 'lib/rails_pwnerer/base/packages.rb', line 236 def self.package_info_hash(output) Hash[output.split(/\n(?=\w)/m).map { |s| s.split(': ', 2) }. select { |kvp| kvp.length == 2 }] end |
Instance Method Details
#atomic_erase(path, name) ⇒ Object
erases a repository
58 59 60 61 62 63 64 |
# File 'lib/rails_pwnerer/base/atomics.rb', line 58 def atomic_erase(path, name) main_file = File.join(path, name) + '.yml' dup_file = File.join(path, name) + '.yml2' [main_file, dup_file].each do |file| File.delete file if File.exists? file end end |
#atomic_read(path, name) ⇒ Object
reads the data in a repository
24 25 26 27 28 29 30 31 32 33 |
# File 'lib/rails_pwnerer/base/atomics.rb', line 24 def atomic_read(path, name) main_file = File.join(path, name) + '.yml' dup_file = File.join(path, name) + '.yml2' # choose the single good file or, if both are good, use the latest timestamp # this works as long as the time on a box doesn't go back (it's ok to have it skewed) results = [main_file, dup_file].map { |file| atomic_read_internal file } results.sort { |a, b| b[1] <=> a[1] } return results.first[0] end |
#atomic_write(data, path, name, options = {}) ⇒ Object
writes data to a repository
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/rails_pwnerer/base/atomics.rb', line 36 def atomic_write(data, path, name, = {}) main_file = File.join(path, name) + '.yml' dup_file = File.join(path, name) + '.yml2' # append verification info at the end of the file to guard from incomplete writes ts = Time.now ts_checksum = Digest::MD5.hexdigest("#{ts.tv_sec}.#{ts.tv_usec}") if [:owner] # secure the file File.open(dup_file, 'w').close uid = uid_for_username [:owner] gid = gid_for_username [:owner] File.chown uid, gid, dup_file File.chmod [:permissions] || 0660, dup_file end File.open(dup_file, 'w') { |f| YAML::dump [data, ts.tv_sec, ts.tv_usec, ts_checksum], f } # move the file atomically to the main copy FileUtils.mv(dup_file, main_file) end |
#best_package_matching(patterns) ⇒ Object
Package info for the best package matching a pattern or set of patterns.
Args:
patterns:: a String or Regexp, or an array of such Strings or Regexps
Returns a hash with the following keys:
:name:: the package name
:version:: the package version
Each pattern is searched for in turn. Once there are packages matching a pattern, the
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/rails_pwnerer/base/packages.rb', line 177 def best_package_matching(patterns) patterns = [patterns] unless patterns.kind_of?(Enumerable) patterns.each do |pattern| packages = search_packages(pattern) next if packages.empty? best = packages.sort_by { |key, value| [ pattern.kind_of?(Regexp) ? ((key.index(pattern) == 0) ? 1 : 0) : ((key == pattern) ? 1 : 0), value.split(/[.-]/) ] }.last return { :name => best.first, :version => best.last } end nil end |
#check_rails_root(app_path = '.') ⇒ Object
check if the given path is the root of a Rails application
5 6 7 8 9 10 11 12 |
# File 'lib/rails_pwnerer/base/rails.rb', line 5 def check_rails_root(app_path = '.') ['app', 'config', 'public', 'Rakefile'].all? do |path| File.exists? File.join(app_path, path) end ['script/rails', 'config/database.yml'].any? do |path| File.exists? File.join(app_path, path) end end |
#control_boot_script(script_name, action = :restart) ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/rails_pwnerer/base/startup.rb', line 35 def control_boot_script(script_name, action = :restart) path_to_script = "/etc/init.d/#{script_name}" case action when :stop system "#{path_to_script} stop" when :start system "#{path_to_script} start" when :restart system "#{path_to_script} restart" when :reload system "#{path_to_script} reload" end end |
#cpu_cores ⇒ Object
returns information for each core in the system
49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/rails_pwnerer/base/cpus.rb', line 49 def cpu_cores cpus = [] Sys::CPU.processors do |p| cpus << { :freq => p.cpu_mhz.to_f, :id => p.processor.to_i, :cpu => (p.respond_to?(:physical_id) ? p.physical_id.to_i : 0), :core => (p.respond_to?(:core_id) ? p.core_id.to_i : 0), :num_cores => (p.respond_to?(:cpu_cores) ? p.cpu_cores.to_i : 1) } end return cpus end |
#current_user ⇒ Object
gets the currently logged on user
28 29 30 |
# File 'lib/rails_pwnerer/base/dirs.rb', line 28 def current_user Etc.getpwuid.name end |
#gem_exists?(gem_name) ⇒ Boolean
checks if a gem exists
20 21 22 23 24 25 26 27 28 |
# File 'lib/rails_pwnerer/base/gems.rb', line 20 def gem_exists?(gem_name) begin output = `gem specification --local #{gem_name} 2> /dev/null` return output =~ /^\-\-\- \!ruby\/object\:Gem\:\:Specification/ rescue # we get here if gem exits with an error code return false end end |
#gid_for_username(name) ⇒ Object
gets the GID associated with the username
14 15 16 17 |
# File 'lib/rails_pwnerer/base/dirs.rb', line 14 def gid_for_username(name) passwd_entry = Etc.getpwnam(name) return (passwd_entry.nil?) ? nil : passwd_entry.gid end |
#group_for_username(name) ⇒ Object
gets the main group of the given user
20 21 22 23 24 25 |
# File 'lib/rails_pwnerer/base/dirs.rb', line 20 def group_for_username(name) gid = gid_for_username(name) return nil if gid.nil? group_entry = Etc.getgrgid(gid) return (group_entry.nil?) ? nil : group_entry.name end |
#hook_boot_script(script_location, script_name = File.basename(script_location), options = {}) ⇒ Object
hooks a script into the boot sequence
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/rails_pwnerer/base/startup.rb', line 19 def hook_boot_script(script_location, script_name = File.basename(script_location), = {}) # copy the script to /etc/init.d and chmod +x target_script = path_to_boot_script script_name if [:symlink] FileUtils.ln_s script_location, target_script, :force => true exec_file = script_location else FileUtils.cp script_location, target_script exec_file = target_script end File.chmod 0755, exec_file # add to boot sequence system "update-rc.d #{script_name} defaults" end |
#install_gem(gem_name) ⇒ Object
TODO: use the Gem API instead of the command line
6 7 8 |
# File 'lib/rails_pwnerer/base/gems.rb', line 6 def install_gem(gem_name) system "gem install #{gem_name}" end |
#install_gems(gem_names) ⇒ Object
39 40 41 |
# File 'lib/rails_pwnerer/base/gems.rb', line 39 def install_gems(gem_names) unroll_collection(gem_names) { |n| install_gem(n) } end |
#install_package(package_name, options = {}) ⇒ Object
Installs a package.
Args:
package_name:: the exact name of the package to be installed
options:: accepts the following:
:source:: if true, a source package is installed and built
:skip_proxy:: if true, apt is instructed to bypass any proxy that might
be
Returns true for success, false if something went wrong.
32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/rails_pwnerer/base/packages.rb', line 32 def install_package(package_name, = {}) return true if install_package_impl(package_name, ) if [:source] if [:no_proxy] install_package package_name, .merge(:source => false) else install_package package_name, .merge(:no_proxy => true) end else return false unless [:no_proxy] install_package package_name, .merge(:no_proxy => true) end end |
#install_package_impl(package_name, options) ⇒ Object
Internals for install_package.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/rails_pwnerer/base/packages.rb', line 59 def install_package_impl(package_name, ) prefix, params = apt_params_for if [:source] with_temp_dir(:root => true) do dep_cmd = "#{prefix} apt-get build-dep #{params} #{package_name}" return false unless Kernel.system(dep_cmd) fetch_cmd = "#{prefix} apt-get source -b #{params} #{package_name}" return false unless Kernel.system(fetch_cmd) deb_files = Dir.glob '*.deb', File::FNM_DOTMATCH build_cmd = "#{prefix} dpkg -i #{deb_files.join(' ')}" return false unless Kernel.system(build_cmd) end else install_cmd = "#{prefix} apt-get install #{params} #{package_name}" return false unless Kernel.system(install_cmd) end return true end |
#install_package_matching(patterns, options = {}) ⇒ Object
Installs a package matching a pattern or list of patterns.
Args:
patterns:: same as for best_package_matching
options:: same as for install_package
Returns true for success, false if something went wrong.
17 18 19 20 |
# File 'lib/rails_pwnerer/base/packages.rb', line 17 def install_package_matching(patterns, = {}) package = best_package_matching patterns package ? install_package(package[:name], ) : false end |
#install_packages(package_names, options = {}) ⇒ Object
275 276 277 |
# File 'lib/rails_pwnerer/base/packages.rb', line 275 def install_packages(package_names, = {}) unroll_collection(package_names) { |n| install_package(n, ) } end |
#kill_tree(pid) ⇒ Object
24 25 26 |
# File 'lib/rails_pwnerer/base/process.rb', line 24 def kill_tree(pid) Zerg::Support::Process.kill_tree pid end |
#os_distro ⇒ Object
the distribution of the OS
46 47 48 49 50 51 52 |
# File 'lib/rails_pwnerer/base/dirs.rb', line 46 def os_distro if RUBY_ARCH =~ /win/ return "Windows" else File.open('/etc/issue', 'r') { |f| f.read }.split(/(\r|\n)+/, 2)[0] end end |
#path_to_boot_script(script_name) ⇒ Object
returns the filesystem path to a boot script
9 10 11 |
# File 'lib/rails_pwnerer/base/startup.rb', line 9 def path_to_boot_script(script_name) File.join '', 'etc', 'init.d', script_name end |
#path_to_boot_script_defaults(script_name) ⇒ Object
returns the filesystem path to the defaults used by a boot script (or nil if unsupported)
14 15 16 |
# File 'lib/rails_pwnerer/base/startup.rb', line 14 def path_to_boot_script_defaults(script_name) File.join '', 'etc', 'default', script_name end |
#path_to_gemdir ⇒ Object
locates the main file in a gem (used to locate the gem)
31 32 33 34 35 |
# File 'lib/rails_pwnerer/base/gems.rb', line 31 def path_to_gemdir # TODO: use the rubygems API instead of this hack `gem environment gemdir`.strip end |
#process_info(pid = nil) ⇒ Object
returns information about a process
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/rails_pwnerer/base/process.rb', line 6 def process_info(pid = nil) info = Hash.new Zerg::Support::ProcTable.ps.each do |process| item = { :cmdline => process.cmdline, :pid => process.pid.to_s } if pid.nil? info[process.pid.to_s] = item else return item if item.pid.to_s == pid.to_s end end if pid.nil? return info else return nil end end |
#prompt_user_for_password(prompt, fail_prompt) ⇒ Object
11 12 13 14 15 16 17 18 |
# File 'lib/rails_pwnerer/base/input.rb', line 11 def prompt_user_for_password(prompt, fail_prompt) unless defined?(HighLine) print "#{fail_prompt}\n" return nil end HighLine.new.ask(prompt) { |question| question.echo = '' } end |
#remove_package(package_name, options = {}) ⇒ Object
Removes a package.
Args:
package_name:: the exact name of the package to be installed
Returns true for success, false if something went wrong.
52 53 54 55 56 |
# File 'lib/rails_pwnerer/base/packages.rb', line 52 def remove_package(package_name, = {}) prefix, params = apt_params_for del_cmd = "#{prefix } apt-get remove #{params} #{package_name}" Kernel.system(del_cmd) ? true : false end |
#remove_packages(package_names) ⇒ Object
283 284 285 |
# File 'lib/rails_pwnerer/base/packages.rb', line 283 def remove_packages(package_names) unroll_collection(package_names) { |n| remove_package(n) } end |
#search_packages(pattern) ⇒ Object
Searches for packages matching a name.
Args:
pattern:: a String or Regexp containing a pattern that should be matched
by the package names
Returns a hash where the keys are matching package names, and the values are version numbers.
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/rails_pwnerer/base/packages.rb', line 202 def search_packages(pattern) packages = RailsPwnerer::Base.all_packages Hash[packages.select { |key, value| pattern.kind_of?(Regexp) ? (pattern =~ key) : key.index(pattern) }.map { |key, value| # apt-cache search sometimes leaves version numbers out # Update the cache with version numbers. if value.nil? info = RailsPwnerer::Base.package_info_hash( Kernel.`("apt-cache show #{key}")) packages[key] = value = info['Version'] end [key, value] }] end |
#uid_for_username(name) ⇒ Object
gets the UID associated with the username
8 9 10 11 |
# File 'lib/rails_pwnerer/base/dirs.rb', line 8 def uid_for_username(name) passwd_entry = Etc.getpwnam(name) return (passwd_entry.nil?) ? nil : passwd_entry.uid end |
#unroll_collection(arg, &proc) ⇒ Object
unrolls a collection
20 21 22 23 24 25 26 |
# File 'lib/rails_pwnerer/base.rb', line 20 def unroll_collection(arg, &proc) if arg.kind_of? String yield arg else arg.each { |i| unroll_collection(i, &proc) } end end |
#update_all_packages(options = {}) ⇒ Object
Upgrades all the packages on the system to the latest version.
259 260 261 262 263 264 |
# File 'lib/rails_pwnerer/base/packages.rb', line 259 def update_all_packages( = {}) return true if update_all_packages_impl() return false if [:no_proxy] update_all_packages .merge(:no_proxy => true) end |
#update_all_packages_impl(options) ⇒ Object
Internals for upgrade_all_packages.
267 268 269 270 271 |
# File 'lib/rails_pwnerer/base/packages.rb', line 267 def update_all_packages_impl() prefix, params = apt_params_for success = Kernel.system "#{prefix} apt-get upgrade #{params}" success ? true : false end |
#update_gems ⇒ Object
update the metadata for all the gems
15 16 17 |
# File 'lib/rails_pwnerer/base/gems.rb', line 15 def update_gems() system "gem update --system" end |
#update_package_metadata(options = {}) ⇒ Object
Updates the metadata for all the packages.
Options:
:skip_proxy:: if true, apt is instructed to bypass the proxy
Returns true for success, false if something went wrong.
128 129 130 131 132 133 134 135 136 137 |
# File 'lib/rails_pwnerer/base/packages.rb', line 128 def ( = {}) if () # Reset the metadata cache. RailsPwnerer::Base.instance_variable_set :@packages, nil return true end return false if [:skip_proxy] .merge(:skip_proxy => true) end |
#upgrade_gem(gem_name) ⇒ Object
10 11 12 |
# File 'lib/rails_pwnerer/base/gems.rb', line 10 def upgrade_gem(gem_name) system "gem update #{gem_name.nil ? '' : gem_name}" end |
#upgrade_gems(gem_names) ⇒ Object
43 44 45 |
# File 'lib/rails_pwnerer/base/gems.rb', line 43 def upgrade_gems(gem_names) unroll_collection(gem_names) { |n| upgrade_gem(n) } end |
#upgrade_package(package_name, options = {}) ⇒ Object
Upgrades a package to the latest version.
242 243 244 245 246 247 248 249 |
# File 'lib/rails_pwnerer/base/packages.rb', line 242 def upgrade_package(package_name, = {}) return install_package(package_name, ) if [:source] return true if upgrade_package_impl(package_name, ) return false if [:no_proxy] upgrade_package package_name, .merge(:no_proxy => true) end |
#upgrade_package_impl(package_name, options) ⇒ Object
Internals for upgrade_package.
252 253 254 255 256 |
# File 'lib/rails_pwnerer/base/packages.rb', line 252 def upgrade_package_impl(package_name, ) prefix, params = apt_params_for update_cmd = "#{prefix} apt-get upgrade #{params} #{package_name}" Kernel.system(update_cmd) ? true : false end |
#upgrade_packages(package_names, options = {}) ⇒ Object
279 280 281 |
# File 'lib/rails_pwnerer/base/packages.rb', line 279 def upgrade_packages(package_names, = {}) unroll_collection(package_names) { |n| upgrade_package(n, ) } end |
#with_package_source(source_url, source_repos = [], options = {}) ⇒ Object
Executes the given block in the context of having new package sources.
Args:
source_url:: the source URL, e.g. http://security.ubuntu.com/ubuntu
repositories:: the package repositories to use, e.g. ['main', 'universe']
options:: supports the following keys:
:source:: if true, will use source-form packages from the new
sources; by default, binary packages will be used
Returns the block’s return value.
After adding the new package source, the package metadata is refreshed, so the block can focus on installing new packages.
If the package source already exists, the given block is yielded without making any changes to the package configuration.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/rails_pwnerer/base/packages.rb', line 94 def with_package_source(source_url, source_repos = [], = {}) source_prefix = [:source] ? 'deb-src' : 'deb' source_patterns = [source_prefix, source_url] + source_repos source_contents = File.read '/etc/apt/sources.list' sources = source_contents.split(/(\r|\n)+/) source_exists = sources.any? do |source_line| source_frags = source_line.split(' ') source_patterns.all? { |pattern| source_frags.any? { |frag| frag == pattern } } end unless source_exists File.open('/etc/apt/sources.list', 'a') do |f| f.write "#{source_prefix} #{source_url} #{source_repos.join(' ')}\n" end end begin yield ensure unless source_exists File.open('/etc/apt/sources.list', 'w') { |f| f.write source_contents } end end end |
#with_temp_dir(options = {}) ⇒ Object
executes a block in a temporary directory
33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/rails_pwnerer/base/dirs.rb', line 33 def with_temp_dir( = {}) base_dir = if [:root] File.exists?('/tmp') ? '/tmp/' : '/' else './' end temp_dir = base_dir + "rbpwn_#{(Time.now.to_f * 1000).to_i}" Dir.mkdir temp_dir Dir.chdir(temp_dir) { yield } FileUtils.rm_r temp_dir end |