How to specify rubygems path in environment-less Ruby script? - ruby

I've written a data collection script for Cacti in Ruby and it runs fine from the command line but Cacti runs the script via "env -i" which strips the environment so Ruby can't find the rubygems library ("in `require': no such file to load -- rubygems (LoadError)"). How might I work around this?

#!/bin/sh
#export LOAD_PATH=whatever
#export RUBYLIB=whatever
#export RUBYOPT=whatever
#export RUBYPATH=whatever
#export RUBYSHELL=whatever
#export PATH=$PATH:whatever
exec ruby -x. $0 "$#"
#!/usr/bin/ruby
require 'rubygems'
require 'open4' # or whatever
# rest of ruby script here
This is a shell script that runs ruby with -x, which will cause the interpreter to skip lines until it finds #!.*ruby. This will give you a chance to restore the environment. The . after -x is a noop, you can take out the ., or replace it with a directory. Ruby will cd there before running the script.
I'm actually guessing that this is not really what you want, since this could have been done without any trickery by just making two scripts, one for the shell, one for Ruby. Perhaps the list of environment variables Ruby cares about will help...

I don't think $LOAD_PATH used for gems (at least, not exclusively). You might want to look at a couple environment variables that haven't been mentioned here yet:
ENV['GEM_HOME']
ENV['GEM_PATH']
You can see your current paths for gems with:
require 'rubygems'
puts Gem.path
A partial answer might be here: comp.lang.ruby post

Can you modify any of the following in your Ruby script: $:, $-I or $LOAD_PATH? These all just point to the same array which specifies where Ruby looks for classes and other ephemera...
>> $LOAD_PATH
=> ["/usr/local/lib/ruby/site_ruby/1.8", "/usr/local/lib/ruby/site_ruby/1.8/i686-darwin9.5.0", "/usr/local/lib/ruby/site_ruby", "/usr/local/lib/ruby/1.8", "/usr/local/lib/ruby/1.8/i686-darwin9.5.0", "."]

Related

How to write a Ruby gem that can be invoked without the word 'ruby' preceding it

I would like to write a CLI application wrapped into a Gem that can be invoked the same way git commands are invoked, or gem commands. Eg when running say "git clone " you don't need to precede it with 'ruby'. However, the tutorials and articles I've seen so far about writing gems, don't show this. The examples either require you to run your gem through irb, with appropriate requires, or you run it like 'ruby '. This is not what I want. If you know of any tutorials that cover this, then that would be great.
Thanks.
The "#!" line at the start of a script tells your shell which executable to execute the script with. In this case, it tells it to find the Ruby executable from the environment and give the script to it for execution.
By means of example, I have a file called "hi", with the following:
#!/usr/bin/env ruby
puts "hi!"
I make it executable:
$ chmod a+x hi
Then I can execute it directly, without explicitly invoking the Ruby interpreter:
$ ./hi
hi!
Per the tutuorial you would simply provide such a file which requires your gem and whatnot, and provide it in the executables property of your gemspec:
Gem::Specification.new do |s|
# ...
s.executables << 'hi'
When the gem is installed, the hi script would be installed into a location discoverable on the path, so you could then invoke it.

Ruby script: shebang with absolute path to ruby not working

I am using Mac OS X. I have two versions (2.1.5 and 2.0.0) of Ruby installed. The former installed at /another/.path/to/ruby (there is a dot before "path" to mimic the fact that the path contains a dot-headed directory in between), in addition to the default system one (version 2.0.0) at /usr/bin/ruby. I used rbenv to install Ruby.
After I manually set the PATH environment variable so the default ruby command will be found in another directory: /another/.path/to/ruby. Now I check
which -a ruby
It is using correct ruby first, as output.
/another/.path/to/ruby
/usr/bin/ruby
Now I create a script, rbs, with the first line of shebang specifying the ruby to use.
#!/usr/bin/env ruby
puts 'hey there'
Then I run
./rbs
it outputs 'hey there'. Good. Meanwhile, the Ruby is using the correct version.
/usr/bin/env ruby --version
as well as
ruby --version
both output 2.1.5. So it does great job to use the new version.
However, here is where the problem occurs: now I update rbs file to be:
#!/another/.path/to/ruby
puts 'hey there'
Note that I updated the shebang to use the absolute path to the desired ruby. then I run
./rbs
It outputs:
./rbs: line 2: puts: command not found
which is too weird;
but if I run
ruby ./rbs
it outputs 'hey there' as normal. It looks like the shebang works perfect using /usr/bin/env ruby, but not for absolute path for the newly install ruby?
Why is this? Is there a way to fix it so the updated script can still work by typing the following?
./rbs
Thanks!
The puts: command not found message indicates that your script is not being run by Ruby, but by the shell instead. So first, I would double-check your shebang line's syntax and path.
Second, note that rbenv uses shims that dynamically find and run the right version of ruby (and associated programs like gem, etc). But the shims are scripts, and scripts can't themselves be shebang interpreters; you have to find and use the actual path to the ruby executable (as output by rbenv which ruby).
On the other hand, since /usr/bin/env is an executable, you can always use something like #!/usr/bin/env ruby, which will work even if the ruby it finds in the path is itself a script.
I can't comment, (otherwise I'd add as a comment) but I think its worthwhile to add that the
#!/usr/bin/env ruby
MUST be the first line of the file. This tripped me up for a while.
source

Ruby Script to Command Line Tool

I am trying to make a command line tool. I already have a ruby script (one file). But I want to project it as a normal command line command.
Right now I have to go into the directory where the script is and type ruby script.rb for it to function but i want to make a command such as script [option] from directory and it should process the required option in the script.
Do I need to make an independent ruby gem for this? I have read about some gems like thor and commander but I am not able to use them properly.
How can I make this command line tool?
PS: An example can be the twitter gem and a command line tool 't' which is also a gem.
Ruby, because it's a general-programming language, makes it easy to create command-line scripts. Here's a basic script you can build upon:
#!/usr/bin/env ruby
require 'optparse'
args = {}
OptionParser.new do |opt|
opt.on('-i', '--input FILE', 'File to read') { |o| args[:file_in] = o }
opt.on('-o', '--output FILE', 'File to write') { |o| args[:file_out] = o }
end.parse!
abort "Missing input or output file" unless (args[:file_in] && args[:file_out])
File.write(args[:file_out], File.read(args[:file_in]))
Here's what's happening:
#!/usr/bin/env ruby is commonly called a "bang line". The shell will look for this line at the top of a file to determine what application can read the file and execute/interpret it. env is an application that will look through the user's PATH environment variable and return the first Ruby found as the Ruby to execute the script. Using this makes the script work with Rubies in the normal /usr/bin, /usr/local/bin or when managed by rbenv or RVM.
require 'opt parse' pulls in Ruby's command-line parser class OptionParser, which makes it easy to set up traditional flags, such as -i path/to/file/to/read, -o path/to/file/to/write, or long parameters, like --input or --output. It also automatically supplies the -h and --help flags to return formatted help text for the script. OptionParser is a bit of a learning-curve, so play with the complete example and you'll figure it out.
The rest should be pretty self-explanatory.
Traditionally, executables that are installed by the system go in /usr/bin. Executables we write, or add, go in /usr/local/bin, and I highly recommend sticking with that.
Some OSes don't automatically supply an entry for /usr/local/bin in the PATH, so you might need to modify your PATH setting in your ~/.bashrc, ~/.bash_profile or ~/.profile to allow the shell to locate the script.
Executable scripts need to have their executable flag set: chmod +x /path/to/executable is the basic command. See man chmod for more information.
I tend to leave the script's extension in place; Ruby scripts are "foo.rb", Python are "bar.py", etc. I do that because I prefer to have that extension as a hint of the language it's written in, but YMMV. The extension isn't necessary so go with what works for you.
Beyond all that, you might want to provide logging output, or output to the system's syslog. In the first case use Ruby's built-in Logger class, or the Syslog class in the second case.
Actually there's two great gems for command line apps in Ruby.
First is methadone which is for simpler command line apps.
Another is gli which is for apps with multiple commands, for example something like bundler.
If you want to know more, you can check out book about creating command line apps build awesome command-line apps in ruby by author of these gems.
You do not need to make it a gem, the following suffices:
Change its name from script.rb to script
Add #!/usr/bin/env ruby as the first line of script
Put it somewhere in PATH (e.g. $HOME/bin, making sure it is in PATH), or execute by giving path explicitly, e.g. $HOME/myscriptdir/script

ZSH Ruby command not found

I'm pretty inexperienced with terminal type stuff besides the most basic commands, I recently switched from Bash to ZSH with in oh-my-zsh. I'm trying to make an executable ruby script at usr/bin/test.rb. For what it's worth, I never tried this with bash so I have no idea if its zsh specific.
$~ test.rb
/usr/bin/test.rb: line 2: puts: command not found
$~ ruby test.rb
ruby: No such file or directory -- test.rb (LoadError)
and my .zshrc file:
export ZSH=$HOME/.oh-my-zsh
source $ZSH/oh-my-zsh.sh
export PATH="/Users/jason/.rvm/gems/ruby-2.0.0-p247/bin:/Users/jason/.rvm/gems/ruby- 2.0.0-p247#global/bin:/Users/jason/.rvm/rubies/ruby-2.0.0-p247/bin:/Users/jason/.rvm/bin:.git/safe/../../bin:.git/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11/bin:/Users/jason/.rvm/bin"
.
~ which ruby
/Users/jason/.rvm/rubies/ruby-2.0.0-p247/bin/ruby
test.rb
#!/usr/bin/env ruby
puts 'test!'
$~ test.rb
/usr/bin/test.rb: line 2: puts: command not found
This is probably because your script is missing an essential line, often called a "pound-bang line" or, more simply, a "bang line", which tells the operating system what program to use to execute the rest of the file. Typically, for Ruby scripts, it looks like:
#!/usr/bin/ruby
or
#!/usr/bin/env ruby
and MUST be the first line in the file. When the OS opens the file, it looks for #! and, if it sees those, launches the executable at the path given, and passes the script to it. That's basic script execution on a *nix system, and applies to sh/Bash/Perl/Python/Ruby and any number of other executable applications on a *nix system.
ruby test.rb
ruby: No such file or directory -- test.rb (LoadError)
I suspect the second failed because you weren't in the /usr/bin/ directory when you executed that command. Ruby tried to run the script but couldn't find it in the local/current directory.
I'm not trying to be cruel, but, as a programmer, you'll spend a huge amount of time at the command-line, especially so if you are programming in C/C++, Perl, Ruby, Python, or any non-IDE based language. You have to learn how the OS works otherwise disasters of varying sizes and shapes await you, so, in parallel to learning a language you need to learn how to use, and administer, your OS. You don't have to be a power-user or administrator, but you have to know enough to understand good instructions from ones that don't apply, or are just plain-wrong.
Well I was facing the same problem, I had ruby & rails installed but I couldn't run them on ZSH
The answer is so simple
Just Add the following lines to .zshrc
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
Then run
source ~/.zshrc
After that to check if ruby & rails are found by ZSH run
ruby --version
rails --verison

ruby require relative files

I'm using ruby v1.9.1 in combination with vim and I execute my scripts with:
:!ruby "%"
my scripts are running fine if I add:
$:.unshift File.dirname(__FILE__)
to add the path of this file to the LOAD_PATH of ruby. If I omit this line my require statements to local scripts aren't working anymore.
Is there a way to pass the path of the file to rubys LOAD_PATH? Something like (completly fictional):
:!ruby "%" --add-to-load-path
I did some research before and stubled upon require_relative, but this has the same effect as require and is not working.
You can use the -I option of the ruby executable and write something like the following:
:!ruby -I%:p:h. %
See ruby --help for further information and file modifiers.
Edited: see comments.

Resources