Best Practices for multiple OS consistency when using Ruby's Dir.glob - ruby

I noticed recently during a debugging session that Dir.glob (aka Dir[]) behaves differently depending on the OS. Specifically the order the files are returned in is different.
What are recommended ways to use Dir.glob in Ruby when you know the code will be used on a variety of OSes?
Example Difference:
I cloned the project DeckSchrubber in Linux and Windows
Windows:
irb(main):003:0> puts Dir['./*']
./CHANGELOG.md
./LICENSE
./main.go
./README.md
./types.go
./util
=> nil
Linux:
irb(main):011:0> puts Dir['./*']
./main.go
./LICENSE
./util
./types.go
./README.md
./CHANGELOG.md
=> nil
Once again I am asking for solutions and idioms to ensure the output is canonical.

Usually, FS libraries behave in a different way on Mac and Linux. I don't consider windows as a platform for Ruby.
So, from my experience, it was enough just to add a conditional operator, that checks the current platform name, and sorts the result in required way. As far as I remember, the difference was in the order of returned files.

Related

IRB require mongo fails cannot load such file -- mongo [duplicate]

The latest changesets to Ruby 1.9.2 no longer make the current directory . part of your LOAD_PATH. I have a non-trivial number of Rakefiles that assume that . is part of the LOAD_PATH, so this broke them (they reported "no such file to load" for all require statements that based off the project path). Was there a particular justification for doing this?
As for a fix, adding $: << "." everywhere works, but seems incredibly hacky and I don't want to do that. What's the preferred way to make my Rakefiles 1.9.2+ compatible?
It was deemed a "security" risk.
You can get around it by using absolute paths
File.expand_path(__FILE__) et al
or doing
require './filename' (ironically).
or by using
require_relative 'filename'
or adding an "include" directory
ruby -I . ...
or the same, using irb;
$irb -I .
There's two reasons:
robustness and
security
Both are based on the same underlying principle: in general, you simply cannot know what the current directory is, when your code is run. Which means that, when you require a file and depend on it being in the current directory, you have no way of controlling whether that file will even be there, or whether it is the file that you actually expect to be there.
As others answers point out, it's a security risk because . in your load path refers to the present working directory Dir.pwd, not the directory of the current file being loaded. So whoever is executing your script can change this simply by cding to another directory. Not good!
I've been using full paths constructed from __FILE__ as an alternative.
require File.expand_path(File.join(File.dirname(__FILE__), 'filename'))
Unlike require_relative, this is backward compatible with Ruby 1.8.7.
Use require_relative 'file_to_require'
Throw this in your code to make require_relative work in 1.8.7:
unless Kernel.respond_to?(:require_relative)
module Kernel
def require_relative(path)
require File.join(File.dirname(caller.first), path.to_str)
end
end
end
'.' in your path has long been considered a bad thing in the Unix world (see, for example, http://www.faqs.org/faqs/unix-faq/faq/part2/section-13.html). I assume the Ruby folks have been persuaded of the wisdom of not doing that.
I found this to be a confounding change until I realized a couple of things.
You can set RUBYLIB in your .profile (Unix) and go on with life as you did before:
export RUBYLIB="."
But as mentioned above, it's long been considered unsafe to do so.
For the vast majority of cases you can avoid problems by simply calling your Ruby scripts with a prepended '.' e.g. ./scripts/server.
As Jörg W Mittag pointed out, I think what you want to be using is require_relative so the file you require is relative to the source file of the require declaration and not the current working dir.
Your dependencies should be relative to your rake build file.

Ruby/Buildr―getting user-, host and OS-name

I am looking for an robust, elegant and portable solution to get the user-, host- and osname in Ruby. I would like to create a folder structure which is set up like this:
linux/maschine1/user53, or
linux/maschine2/user53, or
windows/maschine1/user53, or
mac/supermac/superuser
the name of the user- and hostname should reflect the user#computer:~$ on a linux shell and the operating system should be divided in win, linux and mac.
I found several approaches, using the ENV['USER'/'USERNAME'], or Etc.getLogin() for the username, Socket.gethostname for the computername and the RUBY_PLATFORM constant for the os-name. But all of them have issues when running in different Plattforms like JRuby or even on different operating systems.
So which choice would be the best for each?
Thanks!
Edit:
I came to this solution a short moment before the answer was given. Since Buildr can also use Java commands it is possible to get the values like this:
os = Java.java.lang.System.getProperty('os.name')
usr = Java.java.lang.System.getProperty('user.name')
host = Java.java.net.InetAddress.getLocalHost().getHostName()
But I think I am going to use the pure ruby way to keep the language as consistent as possible.
Not sure there is really a gem that does it all, but Etc.getlogin and Socket.getshostname should work across platforms I think. I use this or variations of to get the os:
require 'rbconfig'
def os
#os ||= (
host_os = RbConfig::CONFIG['host_os']
case host_os
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
:windows
when /darwin|mac os/
:macosx
when /linux/
:linux
when /solaris|bsd/
:unix
else
raise "unknown os: #{host_os.inspect}"
end
)
end
which is ripped off from the selenium gem: https://code.google.com/p/selenium/source/browse/rb/lib/selenium/webdriver/common/platform.rb which might provide a bit more inspiration.

What is the correct way to detect if ruby is running on Windows?

What is the correct way to detect from within Ruby whether the interpreter is running on Windows? "Correct" includes that it works on all major flavors of Ruby, including 1.8.x, 1.9.x, JRuby, Rubinius, and IronRuby.
The currently top ranked Google results for "ruby detect windows" are all incorrect or outdated. For example, one incorrect way to do it is:
RUBY_PLATFORM =~ /mswin/
This is incorrect because it fails to detect the mingw version, or JRuby on Windows.
What's the right way?
It turns out, there's this way:
Gem.win_platform?
Preferred Option (Updated based on #John's recommendations):
require 'rbconfig'
is_windows = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
This could also work, but is less reliable (it won't work with much older versions, and the environment variable can be modified)
is_windows = (ENV['OS'] == 'Windows_NT')
(I can't easily test either on all of the rubies listed, or anything but Windows 7, but I know that both will work for 1.9.x, IronRuby, and JRuby).
This works perfectly for me
Also etc does not need to be installed, it comes with ruby.
require "etc"
def check_system
return "windows" if Etc.uname[:sysname] == "Windows_NT"
return "linux" if Etc.uname[:sysname] == "Linux"
end
(File::ALT_SEPARATOR || File::SEPARATOR) == '\\'

Why does Ruby 1.9.2 remove "." from LOAD_PATH, and what's the alternative?

The latest changesets to Ruby 1.9.2 no longer make the current directory . part of your LOAD_PATH. I have a non-trivial number of Rakefiles that assume that . is part of the LOAD_PATH, so this broke them (they reported "no such file to load" for all require statements that based off the project path). Was there a particular justification for doing this?
As for a fix, adding $: << "." everywhere works, but seems incredibly hacky and I don't want to do that. What's the preferred way to make my Rakefiles 1.9.2+ compatible?
It was deemed a "security" risk.
You can get around it by using absolute paths
File.expand_path(__FILE__) et al
or doing
require './filename' (ironically).
or by using
require_relative 'filename'
or adding an "include" directory
ruby -I . ...
or the same, using irb;
$irb -I .
There's two reasons:
robustness and
security
Both are based on the same underlying principle: in general, you simply cannot know what the current directory is, when your code is run. Which means that, when you require a file and depend on it being in the current directory, you have no way of controlling whether that file will even be there, or whether it is the file that you actually expect to be there.
As others answers point out, it's a security risk because . in your load path refers to the present working directory Dir.pwd, not the directory of the current file being loaded. So whoever is executing your script can change this simply by cding to another directory. Not good!
I've been using full paths constructed from __FILE__ as an alternative.
require File.expand_path(File.join(File.dirname(__FILE__), 'filename'))
Unlike require_relative, this is backward compatible with Ruby 1.8.7.
Use require_relative 'file_to_require'
Throw this in your code to make require_relative work in 1.8.7:
unless Kernel.respond_to?(:require_relative)
module Kernel
def require_relative(path)
require File.join(File.dirname(caller.first), path.to_str)
end
end
end
'.' in your path has long been considered a bad thing in the Unix world (see, for example, http://www.faqs.org/faqs/unix-faq/faq/part2/section-13.html). I assume the Ruby folks have been persuaded of the wisdom of not doing that.
I found this to be a confounding change until I realized a couple of things.
You can set RUBYLIB in your .profile (Unix) and go on with life as you did before:
export RUBYLIB="."
But as mentioned above, it's long been considered unsafe to do so.
For the vast majority of cases you can avoid problems by simply calling your Ruby scripts with a prepended '.' e.g. ./scripts/server.
As Jörg W Mittag pointed out, I think what you want to be using is require_relative so the file you require is relative to the source file of the require declaration and not the current working dir.
Your dependencies should be relative to your rake build file.

How do I replace an executable with a mock executable in a test?

Can I replace an executable (accessed via a system call from ruby) with an executable that expects certain input and supplies the expected output in a consistent amount of time? I'm mainly operating on Mac OSX 10.6 (Snow Leopard), but I also have access to Linux and Windows. I'm using MRI ruby 1.8.7.
Background: I'm looking at doing several DNA sequence alignments, one in each thread. When I try using BioRuby for this, either BioRuby or ruby's standard library's tempfile sometimes raise exceptions (which is better than failing silently!).
I set up a test that reproduces the problem, but only some of the time. I assume the main sources of variability between tests are the threading, the tempfile system, and the executable used for alignment (ClustalW). Since ClustalW probably isn't malfunctioning, but can be a source of variability, I'm thinking that eliminating it may aid reproducibility.
For those thinking select isn't broken - that's what I'm wondering too. However, according to the changelog, there was concern about tempfile's thread safety in August 2009. Also, I've checked on the BioRuby mailing list whether I'm calling the BioRuby code correctly, and that seems to be the case.
I really don't understand what the problem is or what exactly are you after, can't you just write something like
#!/bin/sh
#Test for input (syntax might be wrong, but you get the idea)
if [ $* ne "expected input" ]; then
echo "expected output for failure"
exit -1
fi
#have it work in a consistent amount of time
$CONSISTENT_AMOUNT_OF_TIME = 20
sleep $CONSISTENT_AMOUNT_OF_TIME
echo "expected output"
You can. In cases where I'm writing a functional test for program A, I may need to "mock" a program, B, that A runs via system. What I do then is to make program B's pathname configurable, with a default:
class ProgramA
def initialize(argv)
#args = ParseArgs(argv)
#config = Config.new(#args.config_path || default_config_path)
end
def run
command = [
program_b_path,
'--verbose',
'--do_something_wonderful',
].join(' ')
system(command)
...
end
def program_a_path
#config.fetch('program_b_path', default_program_b_path)
end
end
Program A takes a switch, "--config PATH", which can override the default config file path. The test sets up a configuration file in /tmp:
program_b_path: /home/wayne/project/tests/mock_program_b.rb
And passes to program A that configuration file:
program_a.rb --config /tmp/config.yaml
Now program A will run not the real program B, but the mock one.
Have you tried the Mocha gem? It's used a lot for testing, and you describe it perfectly. It "fakes" the method call of an object (which includes just about anything in ruby), and returns the result you want without actually running the method. Take this example file:
# test.rb
require 'rubygems'
require 'mocha'
self.stubs(:system).with('ls').returns('monkey')
puts system('ls')
Running this script outputs "monkey" because I stubbed out the system call. You can use this to bypass parts of an application you don't want test, to factor out irrelevant parts.

Resources