What is Ruby's equivalent of Python's sys.executable? - ruby

In Python, you can do
>>> import sys
>>> sys.executable
'/usr/bin/python'
to get at the executable's location. Can you do the same thing just using something built-in to Ruby? It can be a special variable, method, etc.
If there isn't, what is the cleanest, most reliable way of determining the ruby executable's location in a cross-platform way?
Related:
How to get the python.exe location programmatically?

Run this in IRB:
require 'rbconfig'
key_length = RbConfig::CONFIG.keys.max{ |a,b| a.length <=> b.length }.length
RbConfig::CONFIG.keys.sort_by{ |a| a.downcase }.each { |k| puts "%*s => %s" % [key_length, k, RbConfig::CONFIG[k]] }
It will output an "awesome print" style list of all the Ruby configuration info.
ALLOCA =>
AR => ar
arch => x86_64-darwin10.5.0
ARCH_FLAG =>
archdir => /Users/greg/.rvm/rubies/ruby-1.9.2-p0/lib/ruby/1.9.1/x86_64-darwin10.5.0
ARCHFILE =>
AS => as
ASFLAGS =>
BASERUBY => ruby
bindir => /Users/greg/.rvm/rubies/ruby-1.9.2-p0/bin
bindir is the path to the currently running Ruby interpreter. Above it in the list is BASERUBY => ruby.
RbConfig::CONFIG.values_at('bindir', 'BASERUBY').join('/')
=> "/Users/greg/.rvm/rubies/ruby-1.9.2-p0/bin/ruby"
Checking my work:
greg-mbp-wireless:~ greg$ which ruby
/Users/greg/.rvm/rubies/ruby-1.9.2-p0/bin/ruby
There's a lot more information than this so it's worth running the code I added above to see what's available.

Linux-based systems are OK with
`whereis ruby`.split(" ")[1]
It will call whereis ruby and parse its' output for the second entry (first contains 'whereis:')
The more strict method is to call
puts `ls -al /proc/#{$$}/exe`.split(" ")[-1]
It will get the executable name for the current process (there is $$ variable and Process.pid method to obtain that) from /proc/pid/exe symlink information.

Looks like the only truly reliable way is
system("#{Gem.ruby} another_file.rb")
This works even for odd cases like jruby being run as a jar, etc.
Also see
OS.ruby_bin
https://github.com/rdp/os

It looks like the answer is in RbConfig::CONFIG
I think RbConfig::CONFIG['bindir'] provides the directory where the executable is located, the rest is s (or should be) straight forward.
RbConfig::CONFIG['bindir']+'/ruby' should work, even in windows as the exe can be ommitted

Works in a script, not from irb:
puts open($PROGRAM_NAME).readline.gsub /#! *([^ ]+).*/, '\1'
;-)

Related

What is the best way to inspect a running Ruby process

I was having problems with a Ruby process that was running wild.
What are the best ways to inspect such a process for debugging purposes?
I found a nice article about using GDB to inspect it.
What are you using for this purpose, do you have any good resources to read?
Perhaps a good way is beginning by the Tracer lib
use it with
ruby -rtracer
or
strace
on Linux with -f flag, to following forks
truss
on BSD with the same flag
Haven't tried this, but saw this new library yesterday get_process_mem.
If you are interested in current_process_memory_size you can implement method #memstats (thanks to Avdi Grimm and his rubytapas lessons).
require 'csv'
def memstats
size = `ps -o size= #{$$}`.strip.to_i
p "Size: #{size}"
end
memstats # => "Size: 5168"
CSV.open('simple.csv', headers: true) do |csv|
visitors = csv.each
memstats # => "Size: 5170"
p visitors.count{|v| v} # => 168
end
memstats # => "Size: 5168"
$$ is a special variable in ruby, which allows you to get a numeric process ID of the current process

I18n without Rails?

just having troubles to make I18n to work without Rails environment:
irb> require 'i18n'
=> true
irb> I18n.load_path=Dir['/usr/lib/ruby/gems/1.9.1/gems/rails-i18n-0.6.6/rails/locale/en.yml']
=> ["/usr/lib/ruby/gems/1.9.1/gems/rails-i18n-0.6.6/rails/locale/en.yml"]
irb> I18n.load_path+=Dir['/usr/lib/ruby/gems/1.9.1/gems/rails-i18n-0.6.6/rails/locale/sk.yml']
=> ["/usr/lib/ruby/gems/1.9.1/gems/rails-i18n-0.6.6/rails/locale/en.yml", "/usr/lib/ruby/gems/1.9.1/gems/rails-i18n-0.6.6/rails/locale/sk.yml"]
irb> I18n.locale=:sk
=> :sk
irb> I18n.default_locale=:sk
=> :sk
irb> I18n.l Time.now
I18n::MissingTranslationData: translation missing:
sk.time.formats.default
from /usr/lib/ruby/gems/1.9.1/gems/i18n-0.6.1/lib/i18n.rb:289:in
`handle_exception'
from /usr/lib/ruby/gems/1.9.1/gems/i18n-0.6.1/lib/i18n.rb:159:in
`translate'
from
/usr/lib/ruby/gems/1.9.1/gems/i18n-0.6.1/lib/i18n/backend/base.rb:55:in
`localize'
from /usr/lib/ruby/gems/1.9.1/gems/i18n-0.6.1/lib/i18n.rb:236:in
`localize'
from (irb):11
from /usr/bin/irb:12:in `<main>'
irb>
What am I doing wrong ? The sk.yml DOES contain sk.time.formats.default
element !!
In addition what's the I18n's default load_path(s) so I won't be
bothered to supply full paths to every translation YAML/Ruby file ?
Thanks.
You already set the search path for the language definitions with I18n.load_path.
It seems, this is enough when using rails. Without rails, you must also load the language definitions with I18n.backend.load_translations.
In summary, you need two steps:
I18n.load_path = Dir['*.yml']
I18n.backend.load_translations
The dictionaries are defined with language key, e.g. like:
en:
hello: "Hello world"
If you prefer to define your en.yml without language key, you may load them via
I18n.backend.store_translations(:en , YAML.load(File.read('en.yml')))
(You may also use a here-document or direct a ruby-hash).
It seems like your load_path is not being set correctly.
Try including the whole directory and if it's successful, you should see your :sk and :en files by calling I18n.load_path.
I18n.load_path = Dir['/usr/lib/ruby/gems/1.9.1/gems/rails-i18n-0.6.6/rails/locale/*yml']
Setting the files paths directly can be a bit confusing since I18n won't raise an error if the file doesn't exist.
As a side note, I'd advise against including translations from the rails-i18n gem as the path may be different from one machine to another with different ruby versions etc.. a file local to the project would be better.
You'll need to install rails-i18n gem just to get localization data.
With this gem install, one can e.g. print month names in sk localization with:
require 'rails-i18n'
I18n.load_path += $LOADED_FEATURES
.select {|f| "rails-i18n.rb".in? f }
.collect {|f| f.sub('lib/rails-i18n.rb', 'rails/locale/sk.yml') }
I18n.locale = :sk
puts I18n.t('date.month_names').compact
This yields:
Január
Február
Marec
Apríl
Máj
Jún
Júl
August
September
Október
November
December

How to get fileutils calls to output their actions in a Rakefile?

I have a Rakefile which, on my own-built Ruby 1.9.3 installation, correctly outputs the Unix shell equivalent when I use a FileUtils method such as cp, mkdir etc.
However, on the stock Ruby that ships with Mac OS X (specifically 10.5), which is version 1.8.6, they don't do this.
I'd like them to output the commands as they're performed. Is there a way to enable this in OS X's 1.8.6 Ruby, short of adding :verbose => true to every call? (Which may not even work.)
The Rakefile in question is: https://github.com/dpkendal/tools-osx/blob/master/Rakefile
That doesn't make sense. 1.9.3 should not do :verbose unless explicitly told to do so. You can look at the implementation of mkdir in the 1.9.3 lib for example:
def mkdir(list, options = {})
fu_check_options options, OPT_TABLE['mkdir']
list = fu_list(list)
fu_output_message "mkdir #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
return if options[:noop]
list.each do |dir|
fu_mkdir dir, options[:mode]
end
end
There you can see that the message is not generated unless the :verbose option is explicitly supplied.
However to enable :verbose across all FileUtils methods you can simply include FileUtils::Verbose into your namespace. This works in both 1.8 and 1.9 ruby:
irb(main):001:0> RUBY_VERSION
=> "1.8.7"
irb(main):002:0> include FileUtils::Verbose
=> Object
irb(main):003:0> mkdir 'fooof'
mkdir fooof
=> ["fooof"]
BTW, it might be that Rake already does this in 1.9.3, which would explain why it does what it does in 1.9.3 and not in 1.8.6. I did not check this, but that's the only explanation I can think of.
Rake provides its own FileUtils extension called Rake::FileUtilsExt. This module has a verbose flag. To activate it simply add this to the top of the Rakefile:
Rake::FileUtilsExt.verbose(true)

Ruby / IRB environment issue on MacOSX

I have a ruby script that is failing due to my environment, I think it is demonstrated by this strange behaviour in irb ( I am also using rvm but don't think that is the problem)
>> ruby -v
ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin9.8.0]
>> irb
>> FileUtils.mkdir_p('tmp')
NameError: uninitialized constant FileUtils
from (irb):1
>> help
=> nil
>> FileUtils.mkdir_p('tmp')
=> "tmp"
The FileUtils command initially fails but then after typing Help (which also fails) it seems to work.
I have tried require 'rubygems' and require 'FileUtils' - which does fix the problem - but would like to understand whats happening here.
I didn't know there was a "help" command, but apparently it has dependency on FileUtils, probably to load help files. "help" is loading its requirements into the IRB session.
>> before = ObjectSpace.each_object.map { |i| i.class }.uniq
=> [Regexp, String, Array, Class, Hash, Module, Proc, MatchData, File, Binding, NoMemoryError, Float, SystemStackError, fatal, Bignum, Object, IO, Thread, ThreadGroup, IRB::Locale, IRB::Notifier::LeveledNotifier, IRB::Notifier::CompositeNotifier, IRB::StdioOutputMethod, IRB::Notifier::NoMsgNotifier, Enumerable::Enumerator, RubyToken::TkNL, RubyToken::TkEND, RubyToken::TkBITOR, RubyToken::TkIDENTIFIER, RubyToken::TkDOT, RubyToken::TkRBRACE, RubyToken::TkSPACE, RubyToken::TkfLBRACE, RubyToken::TkCONSTANT, RubyToken::TkASSIGN, IRB::SLex::Node, IRB::SLex, RubyLex, IRB::ReadlineInputMethod, IRB::WorkSpace, IRB::Context, IRB::Irb]
>> help
=> nil
>> after = ObjectSpace.each_object.map { |i| i.class }.uniq
=> [Regexp, String, MatchData, Array, Class, RI::ClassEntry, RI::MethodEntry, Hash, Module, Dir, Proc, File, Binding, NoMemoryError, Float, SystemStackError, fatal, Bignum, Object, IO, Thread, ThreadGroup, IRB::Locale, Range, IRB::Notifier::LeveledNotifier, IRB::Notifier::CompositeNotifier, IRB::StdioOutputMethod, IRB::Notifier::NoMsgNotifier, YAML::Syck::Resolver, Gem::ConfigFile, RubyToken::TkNL, RubyToken::TkIDENTIFIER, IRB::SLex::Node, IRB::SLex, RubyLex, IRB::ReadlineInputMethod, IRB::WorkSpace, IRB::Context, IRB::Irb, RI::TopLevelEntry, RI::RiReader, GetoptLong, RI::RiCache, RI::Options, RiDriver, Rational, Date::Infinity, Enumerable::Enumerator, RubyToken::TkRBRACE, DefaultDisplay, RI::TextFormatter]
>> after == before
=> false
>> after - before
=> [RI::ClassEntry, RI::MethodEntry, Dir, Range, YAML::Syck::Resolver, Gem::ConfigFile, RI::TopLevelEntry, RI::RiReader, GetoptLong, RI::RiCache, RI::Options, RiDriver, Rational, Date::Infinity, DefaultDisplay, RI::TextFormatter]
It loads the classes in after - before. Where is FileUtils you say? I think its a module that is part of Dir, but I am not 100% on that.
You need to require 'fileutils':
require 'fileutils'
FileUtils.pwd # => "/"
It is not included by the interpreter by default, which is why IRB doesn't preload it. Because IRB is interactive, it has to do some things on-the-fly that the interpreter will not, such as load help files. That it does so in response to your request isn't anything unexpected to me, it's just what it was programmed to do. I'm sure if you looked at its code you'd be able to trace it easily enough.
So, basically, all you are seeing is IRB respond correctly to your syntax error, then do what it was told to do in response to your "help" command.
If you absolutely have to know what it's doing, you can figure it out by asking IRB to trace its processing:
echo help | irb -f --trace > irb.out
will generate a tracing of what IRB does when "help" is entered. Searching through the file shows:
#0:/Users/greg/.rvm/rubies/ruby-1.9.2-p136/lib/ruby/1.9.1/rdoc/ri/store.rb:2::-: require 'fileutils'
being required by store.rb as IRB loads 'ri'.
FileUtils is part of the Ruby standard library, so it is bundled with the interpreter, but not included automatically when the interpreter starts, like Dir and File. It is completely standalone, not a part of Dir.

command-line ruby scripts accessing a libs folder

I'm trying to create an application that will primarily consist of ruby scripts that will be run from the command-line (cron, specifically). I want to have a libs folder, so I can put encapsulated, reusable classes/modules in there, and be able to access them from any script.
I want to be able to put my scripts into a "bin" folder.
What is the best way to give them access to the libs folder? I know I can add to the load path via command-line argument, or at the top of each command-line script. In PHP, it sometimes made more sense to create a custom .ini file and point the cli to the ini file, so you got them all in one pop.
Anything similar for ruby? Based on your experience, what's the best way to go here?
At the top of each bin/executable, you can put this at the top
#!/usr/bin/env ruby
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib')
require 'libfile'
[etc.]
Were you looking for something different?
If you turn your application into a Ruby gem and install the gem on your system, you don't even need to put this stuff at the top. The require statement would suffice in that case.
Sean,
There is no way to not have to require a library, that I know of. I guess if you want to personalize your Ruby so much you could "roll your own" using eval.
The script below basically works as the interpreter. You can add your own functions and include libraries. Give the file executable permissions and put it in /usr/bin if you really want. Then just use
$ myruby <source>
Here's the code for a very minimal one. As an example I've included the md5 digest library and created a custom function called md5()
#!/usr/bin/ruby -w
require 'digest/md5';
def executeCode(file)
handle = File.open(file,'r');
for line in handle.readlines()
line = line.strip();
begin
eval(line);
rescue Exception => e
print "Problem with script '" + file + "'\n";
print e + "\n";
end
end
end
def checkFile(file)
if !File.exists?(file)
print "No such source file '" + file + "'\n";
exit(1);
elsif !File.readable?(file)
print "Cannot read from source file '" + file + "'\n";
exit(1);
else
executeCode(file);
end
end
# My custom function for our "interpreter"
def md5(key=nil)
if key.nil?
raise "md5 requires 1 parameter, 0 given!\n";
else
return Digest::MD5.hexdigest(key)
end
end
if ARGV[0].nil?
print "No input file specified!\n"
exit(1);
else
checkFile(ARGV[0]);
end
Save that as myruby or myruby.rb and give it executable permissions (755). Now you're ready to create a normal ruby source file
puts "I will now generate a md5 digest for mypass using the md5() function"
puts md5('mypass')
Save that and run it as you would a normal ruby script but with our new interpreter. You'll notice I didn't need to include any libraries or write the function in the source code because it's all defined in our interpreter.
It's probably not the most ideal method, but it's the only one I can come up with.
Cheers
There is a RUBYLIB environment variable that can be set to any folder on the system
If you want to use your classes/modules globally, why not just move them to your main Ruby lib directory? eg: /usr/lib/ruby/1.8/ ?
Eg:
$ cat > /usr/lib/ruby/1.8/mymodule.rb
module HelloWorld
def hello
puts("Hello, World!");
end
end
We have our module in the main lib directory - should be able to
require it from anywhere in the system now.
$ irb
irb(main):001:0> require 'mymodule'
=> true
irb(main):002:0> include HelloWorld
=> Object
irb(main):003:0> hello
Hello, World!
=> nil

Resources