Is there an easy way, when running a Ruby script, to get it to automatically install gems required for script?
For example, consider these require statements at the top of a ruby script:
require 'net/http'
require 'fileutils'
require 'archive/zip'
Now, I know (as a human and programmer) that for this script to run on a given PC with Ruby, the 'gem install archive-zip' command needs to be run first, before the script will work. But if this script needs to run on dozens of PCs, is there anything that can save me from having to ensure ALL the gem dependancies are installed first?
Furthermore, what if there are several gems required?
Not sure if this is exactly what you are after but when I have a server set up how I want I dump a list of my gems to somewhere safe...
gem list > my_gems.txt
If I need to rebuild the box or build another machine I use this script to install the gems...
bulk_gems.rb
#! /usr/local/bin/ruby
STDIN.readlines.each do |l|
m = l.match /^(\S+) \((.*)\)/
unless m.nil?
gem_name, versions = m[1], m[2].split(',')
versions.each do |v|
system "gem install #{gem_name} --version #{v} --ignore-dependencies"
end
end
end
more my_gems.txt | bulk_gems.rb
By using gem unpack you can unpack the gems into a directory. From there, you can include them in your script. For example, randomly picking the gem morse (a gem that encodes/decodes morse code), let's say I use gem unpack morse to put it in a directory /gems/. It unpacks to the directory morse-0.0.2, since that's the version.
$LOAD_PATH << './gems/morse-0.0.2/lib'
require 'morse'
# The gem is included, and Morse is now defined.
Shoes has a really slick way of doing this. See this blog post by _why.
You could port some of that to standard ruby (without the fancy UI)
Related
I'm trying to write a program with colorized output. I looked at a few, and the gem I found was colorize. I've done some research, but I can't find step-by-step instructions on how to install a gem, require it, and use it. The gem I want is colorize, and I also need to know if there's anything else it requires. All I have is the standard ruby console that comes in the Ruby21 file, and notepad++ to write and save it. I need to know how and where to install it, whether I type something into the terminal or download a file and put it somewhere, and how to require it and its prerequisites(if any) in a file.
You can install the gem using your CLI simply by typing: gem install colorize. You can then utilize the gem by requiring it, so at the top of your .rb file add require 'colorize'. Then just test it out by trying puts "This is blue".colorize(:blue).
Your .rb could look like this for example:
require 'colorize'
puts "This is blue".colorize(:blue)
Per http://guides.rubygems.org/rubygems-basics/, you can install the colorize gem with the following command (in terminal or at command prompt):
gem install colorize
To list local gems, run this command:
gem list
And--by adding require 'colorize' to the top of the respective .rb file--that gem's lib directory will be added to your $LOAD_PATH.
How do gems like "rails", "rspec", and "cucumber" allow user to use commands that start with their gem name??
rails new project
rspec spec
cucumber features
Not all gems have this ability. For example, when I type json even though I have it installed, I get
-bash: json: command not found
Gem's .gemspec file looks like this:
Gem::Specification.new do |s|
s.name = "haml"
s.version = "3.1.8"
....
s.executables = ["haml", "html2haml"]
end
This means that when installing this Gem (haml-3.1.8 in this case) also links to executables (also called "binstubs") will be created for the files haml and html2haml which are found inside the gem's bin/ directory.
In this case, for example the file bin/haml could look like:
#!/usr/bin/env ruby
require 'rubygems'
require 'haml'
puts Haml::VERSION
From rubygems.org documentation on building Gems:
In addition to providing libraries of Ruby code, gems can also expose
one or many executable files to your shell’s PATH. Probably the best
known example of this is rake. Another very useful one is
prettify_json.rb, included with the JSON gem, which formats JSON in a
readable manner (and is included with Ruby 1.9).
[...]
Adding an executable to a gem is a simple process. You just need to
place the file in your gem’s bin directory, and then add it to the
list of executables in the gemspec. Let’s add one for the Hola gem.
[...]
The executable file itself just needs a shebang in order to figure out
what program to run it with.
[...]
All it’s doing is loading up the gem, and passing the first command
line argument as the language to say hello with.
These gems have binaries that can be executed from the CLI. Most gems do not need this functionality and only provide code extensions.
Edit: They may not be 'binaries'. They can be just executable Ruby code as well. Thanks #holger
This doesn't seem like it would be a hard thing to do.
I want a ruby script that figures out if a certain gem (any version, and/or a certain version spec) is currently installed on the system (wherever gem list will look), and if not, installs it.
Yes, I know in some cases bundler is great at that. Trust me that I have a case where for weird reasons I don't want to use bundler. I really do want to programmatically interrogate the local gem repo.
One way to programmatially install is just to shell out to gem install, sure.
But I can't manage to figure out a reliable way to programmatically interrogate to see if a particular gem is installed, not with shell out, not with rubygem api, not anything.
Am I missing it?
# The version requirements are optional.
# You can also specify multiple version requirements, just append more at the end
gem_name, *gem_ver_reqs = 'json', '~> 1.8.0'
gdep = Gem::Dependency.new(gem_name, *gem_ver_reqs)
# find latest that satisifies
found_gspec = gdep.matching_specs.max_by(&:version)
# instead of using Gem::Dependency, you can also do:
# Gem::Specification.find_all_by_name(gem_name, *gem_ver_reqs)
if found_gspec
puts "Requirement '#{gdep}' already satisfied by #{found_gspec.name}-#{found_gspec.version}"
else
puts "Requirement '#{gdep}' not satisfied; installing..."
# reqs_string will be in the format: "> 1.0, < 1.2"
reqs_string = gdep.requirements_list.join(', ')
# multi-arg is safer, to avoid injection attacks
system('gem', 'install', gem_name, '-v', reqs_string)
end
More recent rubygems versions provide an installer API, so instead of shelling out to the gem command you could also use:
# using the same "gdep" variable as above
Gem.install gem_name, gdep.requirement
However, I'm not sure if Gem.install respects your .gemrc file.
There are a lot of useful methods for querying your installed gems (see rdocs). Some that might be helpful:
Gem::Specification.find_all_by_name
Gem::Requirement#satisfied_by?(gem_version_instance)
Gem::Specification#satisfies_requirement?(gem_dependency_instance)
Gem.loaded_specs - hash of the gems you've actually loaded via the gem method, or by require
Last answer was good, but this is a little more precise:
`gem install redis` unless `gem list`.lines.grep(/^redis \(.*\)/)
Matches only the gem named redis, and not other gems like redis-native_hash, or something else.
Another way I've seen this done is to try requiring the gem.
begin
require 'some_crazy_gem'
rescue LoadError
`gem install some_crazy_gem`
#...
end
Check that there is installed a gem newer than a particular version.
PROMPT> gem list nakamoto -i -v ">=1.2.3"
true
I have no idea if this is a best practice or not but:
list = `gem list`
`gem install builder` unless list.include? "builder"
... worked for me in irb.
I'd love to have a Gemfile in Bundler that just sets my own personal Gemfiles to always be bult into bundles...
aka ruby-debug, interactive-editor, and so forth.
Any idea how to do this?
We use this technique.
Puth this in your Gemfile:
eval File.read(File.expand_path("Gemfile.personal")) if File.exists?(File.expand_path("Gemfile.personal"))
And then add your personal gems to Gemfile.personal. Of course exclude Gemfile.personal from your version control.
One way to do this is to create different evnironments
group :scott do
end
Then
bundle --with-env=scott
I'm not 100% sure what it is you are trying to achieve, but;
If you just want to specify a number of development-only gems, you can specify a development group that can be excluded from deployments:
group :development do
gem "ruby-debug"
gem "interactive-editor"
end
Then on production or test you would do:
bundle install --without development
The cleanest solution I found so far is to use a separate Gemfile.personal and use a custom Gemfile path. I like this solution because you can use it in any project without modifying project code at all.
1. Add Gemfile.personal in to project root dir
# /path/to/your_ruby_project/Gemfile.personal
eval File.read('Gemfile') # use all gems from Gemfile
gem 'personal-gem1'
gem 'personal-gem2'
2. Install gems using Gemfile.personal file
BUNDLE_GEMFILE="Gemfile.personal" bundle install
# or
bundle install --gemfile=Gemfile.personal
Just remember to specify BUNDLE_GEMFILE every time you execute commands with bundler.
I personally put BUNDLE_GEMFILE=Gemfile.personal env variable in .env file using dotenv which ensures that Gemfile.personal is always used when I execute any command with bundler so I do not need to put it manually every time.
3. Put Gemfile.personal and Gemfile.personal.lock to .gitignore
For linux users:
touch ~/.gitignore
echo "Gemfile.personal\nGemfile.personal.lock" >> ~/.gitignore
This will affect all projects, so you do not need to update each project .gitignore separately.
My proposition does not depend on Bundler. As such does not clutter Gemfile* with your private gems for the price being a bit less convenient than answer by #ScottSchulthess.
How Bundler works
There is an array stored in $LOAD_PATH global variable which is a "load path for scripts and binary modules by load or require" (see Ruby docs) and Bundler modifies this array.
If you're developing a gem, $LOAD_PATH it will contain paths to all gems in the system. You can simply write e.g. require "pry" somewhere and pry gem will be loaded properly even if it's not mentioned in gemspec nor Gemfile. You don't have to add it to dependencies. (Of course it has to be already installed with gem install pry.)
A very different strategy Bundler takes when you're developing an application. In such case most of $LOAD_PATH will be removed on require bundler/setup (Rails calls it in config/boot.rb). Only essential paths and those pointing to gems specified in Gemfile.lock will remain there. So if you want to use pry without adding it to Gemfile, you got to append it to $LOAD_PATH before requiring it.
Solution for applications
gems_root = $LOAD_PATH.detect{ |p| %r{/bundler-} =~ p}.sub(%r{/bundler-.*}, "")
additional_gems = {
"pry" => "pry-0.10.1/lib",
"pry-rails" => "pry-rails-0.3.2/lib",
}
load_paths = additional_gems.values.map{ |p| File.join gems_root, p }
$LOAD_PATH.unshift *load_paths
additional_gems.keys.each{ |r| require r }
If you're using Rails, then save it in /config/initializers/00_custom_gems.rb and that's all. Outside Rails you additionally need to require it, preferably right after require "bundler/setup":
require "path/to/it" if File.exists? "path/to/it"
Remember to mention this file in .gitignore.
Sometimes proper gem path does not end with /lib but with architecture name. The easiest way to learn it is to add it for a moment to Gemfile and do puts $LOAD_PATH in aforementioned initializer. You can also learn those dirs from gemspec.
Solution for gems
When developing gem, you don't need to enhance $LOAD_PATH, only to require gems you want. If you need custom gems in tests and you're using RSpec, it can be done somewhere in /spec/support.
Another (less sane) idea is to add a file lib/development.rb:
require_relative "my_gem_name"
require "path/to/private/requires" if File.exists? "path/to/private/requires"
and refer to this file instead of to "my_gem_name" in your tests, demo application etc..
Is it possible to add a gem dependency only if the person is using a certain version of ruby?
Background: I'm working on a fork of a project that uses Test::Unit::Autorunner and the like. They are part of the standard library in ruby 1.8, but aren't part of the standard library in 1.9.1, and is instead in the "test-unit" gem. I want to add a dependency that says that if someone's using ruby 1.9.1 or later, install the "test-unit" gem, but if they're using 1.8 or earlier, they don't need to install anything.
If you look at the gemspec documentation for add_dependency, there isn't an option for a ruby version. Perhaps you could use the post_install_message attribute to tell the user to install the gem if they're using ruby 1.9.
I did this exact thing for a project. The trick is to add the script as an extension, which will then get executed at install time on the user's machine.
Here are code snippets and links to our github:
First, when in the gemspec (we're actually using a Rakefile to generate it, but the result ends up the same) source
# This file *needs* to be named this, there are other files
# for other extension processors
s.extensions << 'ext/mkrf_conf.rb'
And then the relevant lines in that mkrf_conf.rb source
require 'rubygems/dependency_installer.rb'
inst = Gem::DependencyInstaller.new
inst.install "test-unit" if RUBY_VERSION > "1.9"
Gem doesn't support conditional dependencies (except on gem builder's environment -as noted above), and bundler is not a viable option to solve this either - see https://github.com/carlhuda/bundler/issues/1281
hay ... i'm kind of a ruby newbie ... if this is the best way to do it.
any way ... i wish i can do that using only Ruby .... though u can use your operating system shell to do that, by using this in your program installer, just execute (works for Linux based operating systems):
$ruby --version
(u can execute that from a ruby installer file, just like: ruby --version)
and put a possibility according to output, if it's 1.9.1 add an extra line to execute:
$ sudo gem install gem_name
else, just leave it be.
Checkout this tutorial in the Ruby Programming wikibook.
Tt shows how to install different versions of dependencies depending on what version of ruby the installee is using.
(short answer--it ain't as easy as it should be)
You can't. You need to build two gems, one with
spec.required_ruby_version = '~> 1.8.6'
and one with
spec.required_ruby_version = '~> 1.9.1'
spec.add_dependency 'test-unit', '~> 2.0.5'
Gemspecs are just ruby files anyway, so you can execute any ruby code inside them, so:
spec.add_dependency = 'test-unit', '>= 2.0' if RUBY_VERSION =~ '1.9'
EDIT: Specs run only on the builders machine.