I am running within a Ruby script (a Vagrantfile, specifically) and I want to invoke another ruby executable (berks installed against system ruby, specifically). I know I can do something like
PATH=/usr/bin GEM_PATH=/var/lib/ruby/1.9.1 berks ...
But, that's not very portable. (Different machines will need different GEM_PATH, for instance). So, how can I invoke a script installed against a different Ruby environment from within a Ruby script?
Well, the task at hand sounds not-very-portable, since its entire reason for being is a system-specific quirk of different Rubies being installed in different places. Not all systems will even have those specific Ruby versions.
It sounds to me like your best bet would probably be to allow the user to set certain environment variables (I dunno, $BERKS_SUBRUBY_PATH or something) and use those if they are set. That way anyone who needs to use the workaround has an easy way to do so, but you're not forcing everybody to have the same system config.
Related
For predictability, and to avoid messing up the user's system, I want the program's dependent gems isolated from any global gems the user/system has installed. So just distributing the program as a gem won't really work.
I know that Heroku's "toolbelt" CLI package used to be written in Ruby (though it's now Node.js, apparently); how did they do it? Is what they did a best practice?
I expect only developers to use the program (it's a release-management tool), so it's safe to assume/require the existence of a package-management tool like Homebrew on OSes that don't already ship with one.
The program is written to work on mostly any version of Ruby, so I can depend on some non-version-constrained Ruby package provided by the OS / package-manager, if that makes things easier.
I'm writing a Common Lisp application. I'd like to have a Bash script which will serve as the entry point to the application. Currently, I've written the script so that the user must pass in their name of the Common Lisp implementation to run it, so I would write ./script.sh clisp for GNU CLISP but someone with SBCL would have to write ./script.sh sbcl. This is necessary since, unlike languages like Python and Ruby, Common Lisp implementations do not have any standard name or standardized way of invoking them.
Is there any trick to detecting which Common Lisp implementation is installed, perhaps an environment variable or something? Basically, I'm looking for something better than forcing the user to pass in the name of the implementation.
You could use Roswell, which provides ways to set the implementation on a user or invocation level. You still need wrapper scripts, but roswell standardizes them.
Install the cl-launch Unix utility program which implements the abstraction described in #bishop's answer. The utility will detect most implementations of Common Lisp and can be used to execute a script or dump an executable which calls the content of a script (loads faster).
TL;DR: I don't think there's a trick, but you need not require the clisp interpreter on every invocation.
This is a relatively common pattern: you have a bash script that depends upon a certain executable being available, and it may well be available, but in different locations, possibly with the user having their own compiled version and/or the system having several alternatives.
The approach I've seen boils down to this algorithm:
If there is an environment variable that specifies the full path to an executable, prefer that
Otherwise, if there is a configuration file in the user's home directory that specifies the location, and possibly other parameters, prefer that
Otherwise, if there is a configuration in /etc that specifies the location, and possibly other parameters, prefer that
Otherwise, ask the system package manager to list the packages matching your application's typical installation names
The first three are easy enough to implement using the bash test functions and, I'm guessing, if you got this far you know how to do that. (If not, ask and I'll post examples.)
It is the fourth point that becomes interesting. There are two variables to deal with. First, determining the package manager in the installed environment. There are no shortage of these, and I've seen both table approaches (mapping OS to a package manager) and inquiry approaches (looking for executable that match expected names like rpm, yum, emerge, etc). Second, determining the package name appropriate for your package manager. This too can be tricky. On the one hand, you're probably safe iterating through the list of known executable and grepping the list. On the other hand, your package manager may provide "virtual" or "alternative" packages that generically provide a service, regardless of the specific implementation. For example, you could grep the portage tree for dev-lisp and be reasonably sure to find one installed package.
The easiest case is when your script is meant to be run in a small number of well-known environments: implement the one or more of the first three points to the let the user override the script's auto-selection, then your script's auto-selection just iterates over the known alternatives in your known environment until it finds one it prefers.
The hard case is when you have to support multiple environments. You end up writing an abstraction layer that knows about the different possible package managers and how to interrogate those package systems for various packages, either at a generic level or for specific packages. Having done this for a script set that deployed on AIX, HP-UX, Solaris, a couple of Linux distros, and cygwin Windows I can say: not fun.
As I read your question, you have a script that will be distributed to different users' machines whose environments you don't control. The only requirement of these target machines is they have bash and at least one Common LISP interpreter installed. From this, I inferred you couldn't install any loaders. However, if you can install, require, or detect the presence of any of the launchers mentioned in other answers, that will certainly save a ton of work.
I'm trying to read the windows registry to figure out what scripting environments are installed and where the stand alone interpreter executables are available.
When I did python, for example, I searched for
HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/InstallPath
This gives me the install path for the python executable for the environment, this allows me to find if I have python 2.7, 3, etc and where those exes are.
I'm looking for something similar for Lua for windows.
I must use the registry for this search.
What Nicol said. You will be better served by scanning PATH against the list of known executables (but even that is not a guarantee as many of my local installs are not in PATH). Still, I think there is a better chance of finding those scripting engines that don't leave their traces in the registry. Or maybe use a combination of mechanisms.
Lua does not have an install path. Lua does not have an installation. Lua is not like Python, with an installer that puts everything in one specific place and sets up registry and PATH entries and such.
The standalone interpreter does not have a mechanism to query its location. If you're interested in learning the Lua version, you can always check the _VERSION variable field. But other than that, no, there is no mechanism for doing what you want.
What are the benefits of using FileUtils methods http://ruby-doc.org/core/classes/FileUtils.html than the equivalent Bash commands?
Over and above the fact that you don't have to worry about ensuring your target platform has the specific tools you're using installed, and over and above the problem of doing proper quoting of shell oddities (especially problematical if you target both Windows and Unix-alikes -- Cygwin, GNUWin32, etc. notwithstanding), if you use Ruby's FileUtils you have the moderately-sized overhead of a Ruby function call while if you use external utilities you have the rather sizable overhead of firing up an external process each and every "call".
The FileUtils methods work on Windows.
They are easier to call from inside Ruby scripts because they accept Ruby objects as arguments. This means that you don't have to handle escaping and what not every time you call them.
when you farm stuff out to the shell, you are adding a dependency on those apps. FileUtils is pure ruby, so it works (and works the same, more or less) anywhere that ruby works.
Works across multiple platforms
Does not spawn a new process to issue the command (so it consumes less resources)
I would not say there is no benefits in using Ruby's FileUtils, since you can use them anywhere you have Ruby( especially if your task is in web development). But that does not mean you can't use those shell tools in other platforms as well. Yes, you can write your scripts in *nix shell, and you can run them as well with little or no modification in, say, Windows using cygwin or GNU win32.(and others).
In terms of benefits of Ruby's FileUtils against shell's, its only minimal, since what you can do with FileUtils you can do also with shell's.
So ruby 1.9 is really nice in that it'll automatically require rubygems, and hence when you call require 'somegem' without first requiring rubygems it'll work, and that's generally awesome.
But I have a ton of shell scripts using ruby, and they generally don't rely on rubygems. Shell tools should run instantly, and loading rubygems for nothing is a major drag, mostly because it involves a bunch of disk operations with scattered small files.
I want to be able to tell ruby, when running these shell scripts, to skip loading gems. Ideally, something like #!ruby --no-rubygems in the shebang line.
Is there such a thing? Or maybe a compile option that'll tell ruby rubygems must be required manually?
Yes, you can use the --disable-gems option.
Note that whether or not passing options in the shebang line works depends on your operating system. Some operating systems don't support passing options at all, some only support passing one option or argument.
So, if you have for example
#!/usr/bin/env ruby
Then it's pretty unlikely that you will be able to attach the option to the end. If OTOH you change that to
#!/usr/local/bin/ruby --disable-gems
Then you have hardwired the location of the Ruby binary into your script.
And of course there are operating systems that don't interpret shebang lines at all. (After all, they were never specified in any standard, and aren't even properly documented.)
An alternative would be to set the RUBYOPT environment variable in your shell environment and simply switch to a different environment with RUBYOPT unset (or set to -w, my personal favorite) for your Ruby development.