What determines where gems are installed? - ruby

I'm trying to set up a Sinatra app on my web host. I don't have sudo rights to install gems in the system-wide path, which is several subfolders beneath /usr/local, but I do have a gems folder in my app's directory.
Background
This reference gives the following definitions:
GEM_HOME - "Directory containing the master gem repository."
GEM_PATH - "Path list of directories containing gem repositories to be searched in addition to the GEM_HOME directory. The list should be delimited by the appropriate path separator (e.g. ‘:’ on Unix and ‘;’ on Windows)"
Initial settings on login
When I first ssh into this web host, echo $GEM_HOME and echo $GEM_PATH both produce an empty string, but gem list shows several gems.
Trying to change gem location
From the command line, I have set GEM_HOME like this:
GEM_HOME=$PWD/gems # 'gems' folder under present working directory
echo $GEM_HOME # correctly outputs the gem folder I specified
ls $GEM_HOME # shows gems folder contents, namely:
# bin/ cache/ docs/ gems/ specifications/
I also set GEM_PATH to the same folder.
After doing this, gem list still shows global gems rather than the gems in the specified folder, and gem install still tries to install to the global location.
What am I missing?

Use 'export'
Looks like export, as Tass showed, was the missing piece: it makes my local GEM_HOME variable a global one.
Here's what I've done:
export GEM_HOME=$PWD/gems # where to install and look for gems
export PATH=$PWD/gems/bin:$PATH # append the gems' binaries folder to
# the current path, so that I can call
# `bundle install` and have it find
# myapp/gems/bin/bundle

There is no manpage for gem, which doesn't make it easier. I assume GEM_PATH is where to look for the gems, and GEM_HOME is where to install them. Try
export GEM_HOME = "$GEM_PATH"

You could use Bundler as well. Bundler makes it very easy to manage Gem versions, even when sudo access is not possible. You create a file called Gemfile in the root of your application and put lines such as these:
gem "sinatra"
gem "some_other_gem_dependency"
gem "and_so_on_and_so_forth", ">= 1.0"
And then run bundle install --path /where/you/want/your/gems/stored which will install the gems to a path you have access to. You then put this in your config.ru:
require 'rubygems'
require 'bundler'
Bundler.require
require './your_app'
run YourApp
Check out http://gembundler.com/sinatra.html for more info.

Related

How to use Ruby CGI scripts on Uberspace

I want to use Ruby CGI scripts on an Uberspace 7, but ran into several issues with permissions and security settings, in particular when using gems. How do I install CGI scripts with custom gems?
First, note that Uberspace 7 runs on SELinux. This means that CGI script files in ~/html/ not only have to be executable but also need the correct SELinux context. In this case, the type must be httpd_sys_content_t.
You can view the SELinux context with ls -lZ:
$ ls -Z file1
-rw-rw-r-- user1 group1 unconfined_u:object_r:user_home_t:s0 file1
If some files have the wrong context, the context can be restored with the restorecon command, e.g. restorecon -R ~/html/.
The user installation directory for Ruby gems is ~/.gem/. On Uberspace, gem install installs into that directory by default:
$ cat /etc/gemrc
gem: --no-document --user-install
As the home directory cannot be accessed by the apache process, gems installed there cannot be executed from CGI scripts. You can install gems in /var/www/virtual/$USER/gem instead, create the directory with
$ mkdir /var/www/virtual/$USER/gem
You cannot use the --install-dir parameter for gem install directly as this conflicts with the default parameters mentioned above:
$ gem install mygem --install-dir /var/www/virtual/$USER/gem
ERROR: Use --install-dir or --user-install but not both
Instead, create ~/.gemrc with the following content to override the default parameters (replace <USERNAME> with your actual user name):
gem: --install-dir /var/www/virtual/<USERNAME>/gem
Now the installation of gems should work:
$ gem install mygem
To use the gems in CGI scripts, set the Gem.paths variable before requiring gems:
#!/usr/bin/ruby
Gem.paths = { 'GEM_PATH' => '/var/www/virtual/<USERNAME>/gem' }
require 'mygem'
(... rest of the script)
This is needed as we cannot modify the environment variables (i.e. set GEM_PATH) for the apache process.

How to install gem to local path and then require it

I want to setup gem from rubygems.org to some subfolder of my project like "/gems" and then use it from script via require. Please help me resolve it.
The preferred way is to go with a standard scenario: you build a gem, with binaries, pack it and when used it will download all the dependencies itself, according to gemspec. If the above is for some reason non-acceptable for you, one of the options would be:
• You create a gem for your script;
• You specify a path where to install dependencies:
bundle install --path=vendor/gems
• Instead of require you use require_relative in your script because on destination machines there won’t be local bundle’s config file, pointing to the right folder;
• As your script is finished, you pack everything including gems.
The normal way to go in a ruby project is to setup a Gemfile and use bundler (see link for more info) to handle the gems your project requires, without having to think of where they are stored
However, if there really is no way around shipping an own gem-directory, e.g. because your productive system has absolutely no way to access the internet, you could make it this way:
Create a directory in the root of your project (e.g. 'gems').
Download and unpack the gems to that directory (or use the appropriate options of gem install to redirect the installation into that directory)
Create a ruby file in the root directory of your project with following constants:
PROJECT_DIR = __dir__
GEMS_DIR = File.join(PROJECT_DIR, 'gems')
Now you can require your gems link require File.join(GEMS_DIR, <gem_name>)
Nonetheless, you should really think about using bundler, if possible in any way.
EDIT: to install gem via gem install
Delete the PG data from your project's gems directory
uninstall global gem gem uninstall pg
Install pg again, but into your project's directory: gem install -i <path_to_projects_gems_dir> pg
Run you script again with the require pointing to the gem stated above
I solved it like this:
gem install -i ./ pg
ROOT = File.expand_path('..', __FILE__)
ENV['GEM_PATH'] = File.join(ROOT, './')
require 'pg'
puts 'Version of libpg: ' + PG.library_version.to_s

How to install a gem to the current folder

I want to use a Ruby gem locally (not install it for the entire machine) for use in a single script. I know how to install gems with Bundler with a Gemfile and bundle install. But for a simple script, this seems overkill to set up bundler.
Is there a way to install a gem to a subfolder of my script and use it, similar to the way npm installs Node.js packages in node_modules?
Here's what I have tried so far.
gem install -i ruby plist installs the plist gem in ruby/gems/plist-3.1.0
I tried to require it in my script extract.rb by doing require './ruby/gems/plist-3.1.0/lib/plist but that fails with require: cannot load such file: plist/generator (plist/generator.rb is required by lib/plist.rb).
Ruby 2.0 on OSX
You can bundle install to a different location with the --path option, for example:
bundle install --path vendor/bundle
Also see http://bundler.io/v1.1/bundle_install.html
If you don't want to involve Bundler, just install your gems locally as in your example and then set the GEM_PATH env in your script before your require, e.g.:
#!/usr/bin/env ruby
ROOT = File.expand_path('..', __FILE__)
ENV['GEM_PATH'] = File.join(ROOT, 'ruby')
# or to just append to
# ENV['GEM_PATH'] += ":#{ File.join(ROOT, 'ruby') }"
require 'plist'
assuming your script is in the same folder as the ruby folder (otherwise adjust the filepath accordingly).
You can do it by creating gemset for particular application. follow these steps for that -
$ rvm gemset create <gemset_name>
It will create a gemset for currently selected ruby version.
you can check currently selected ruby version by this command -
$ rvm list
Then navigate to your app directory by cd into it.
now execute this command -
$ rvm use #<gemset_name>
Now whenever you install any gem it will be installed in current gemset which is being used not for the entire machine.
Make sure - you run gem install bundler in newly created gemset so it will not raise error when you will run bundle install.

Execute without `bundle exec` via rubygems-bundler

I have a standard gem scaffold, and in inside the bin directory I have a simple executable (bin/do_things.rb):
#!/usr/bin/env ruby
require 'my_gem'
MyGem::doThings()
The gem hasn't been installed via gem install, so running bin/do_things.rb without bundle exec fails (my Gemfile has the gemspec line in it).
Is there a simple way to have rubygems-bundler execute my script in the bundler context? Should I just modify the $LOAD_PATH in my executable instead?
I could create a wrapper script to execute under Bundler as well, but I'd rather leverage rubygems-bundler somehow.
Or should I just type bundle exec?
try:
bundle exec bash -l
it will set you into bundler context - it's an extra shell so running exit will get you back to bundler less context.
Change the file permission
chmod a+x bin/do_things.rb
and resolve bin path, ignoring symlinks
require "pathname"
bin_file = Pathname.new(__FILE__).realpath
post, add self to libpath
$:.unshift File.expand_path("../../lib", bin_file)
Generating binstubs via bundle install --binstubs creates a wrapper script for all executables listed in my gemspec.
#!/usr/bin/env ruby
#
# This file was generated by Bundler.
#
# The application 'do_things.rb' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path('my_gem', 'do_things.rb')
However, by default, the binstubs path is bin, which conflicts with gem's executable path, and will overwrite files in bin.
Running bundle install --binstubs=SOME_DIR and then adding SOME_DIR to .gitignore seem to be the most maintainable way.
Then, I can simple execute SOME_DIR/do_things or any other project-specific executable I add down the line.

How to prevent bundler from generating binstubs?

I am using oh-my-zsh with plugins=(git bundler) in my .zshrc. So, I don't need bundler to generate binstubs. But bundler does it anyway.
➜ bundle
Using rake (0.9.2.2)
...
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
✗ ls bin
erubis haml nokogiri rails rake2thor rdoc resque-web sass scss thor tt
guard html2haml rackup rake rdiscount resque ri sass-convert thin tilt
Why did the binstubs get generated -- I didn't pass an option asking for them. At least, I don't think I am:
➜ which bundle
/Users/david/.rbenv/shims/bundle
➜ cat /Users/david/.rbenv/shims/bundle
#!/usr/bin/env bash
set -e
export RBENV_ROOT="/Users/david/.rbenv"
exec rbenv exec "${0##*/}" "$#"
I don't have anything in my ~/.bundle/config either.
Please help me put the kabosh on the undesired binstubs!
Bundler generates binstubs on a per-application basis. If you ran bundle install --binstubs at some point in the past, Bundler will remember that and generate binstubs anytime you run install again. To disable them, you can either run bundle install --no-binstubs, or run rm -rf .bundle/config. Either way, that will disable binstub generation.
The option --no-binstubs does not remove the remembered option in bundler 1.5.3!
Instead use bundle config --delete bin, or edit .bundle/config and remove the BUNDLE_BIN line from file, then remove unwanted files from the local binstubs directory.
Example:
ianh$ cat .bundle/config
---
BUNDLE_CACHE_ALL: "true"
BUNDLE_BIN: bin
ianh$ bundle install --no-binstubs
Using rake (10.1.1)
... etc etc ...
Using bundler (1.5.3)
Updating files in vendor/cache
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
ianh$ cat .bundle/config
---
BUNDLE_CACHE_ALL: "true"
BUNDLE_BIN: bin
# see ... it didn't remove the option.
ianh$->(15) bundle config --delete bin
ianh$ cat .bundle/config
---
BUNDLE_CACHE_ALL: "true"
ianh$ bundle -v
Bundler version 1.5.3
If you are still getting binstubs after changing your $HOME/users/.bundle/config file it is more than likely you have another config some where. In order to figure out where execute the follow command
$ bundle config
Settings are listed in order of priority. The top value will be used.
gem.coc
Set for the current user (/Users/username/.bundle/config): "true"
gem.mit
Set for the current user (/Users/username/.bundle/config): "true"
gem.test
Set for the current user (/Users/username/.bundle/config): "rspec"
build.libv8
Set for the current user (/Users/username/.bundle/config): "--without-system-v8"
disable_multisource
Set for the current user (/Users/username/.bundle/config): "true"
bin
Set for your local app (/Users/username/apps/ruby/rails_application/.bundle/config): "bin"
Set for the current user (/Users/username/.bundle/config): "false"
What you are looking for is the bin information. This information gives you paths to the files that have the config information in them. what you can do in order to fix this is go into config file and delete the line that says BUNDLE_BIN: bin that or change bundle bin to false BUNDLE_BIN: 'false'
vi /Users/username/apps/ruby/rails_application/.bundle/config
If you run bundle config again should not see the bin config or you should see that it is set to false. In this example I set mine to false so I get this new result.
bin
Set for your local app (/Users/username/apps/ruby/gscs_ci/.bundle/config): "false"
Set for the current user (/Users/username/.bundle/config): "false"
Something to note however each ruby application that responds to bundle could have its own custom .bundle/config
If you update all the .bundle/config you should not have new files created in the bin directory when you ruby bundle or bundle install
Found out something else sometimes it thinks false is a directory so might be better to just delete the line that BUNDLE_BIN might be simpler.

Resources