Approach for installing system service implemented as Ruby gem
You can do it. However it is probably not quite the recommended approach. But yes it is possible to run arbitary code during gem installation using the extensions
option.
From the RubyGems Manual:
Usage
spec.extensions << 'ext/rmagic/extconf.rb'
Notes
These files will be run when the gem is installed, causing the
C (or whatever) code to be compiled on the user’s machine.
Just place whatever ruby code you need into the extconf.rb
(or equivalent) file.
Examples for building C-extensions from the RubyGems Guides:
http://guides.rubygems.org/c-extensions/
Approach for installing system service implemented as Ruby gem
You can do it. However it is probably not quite the recommended approach. But yes it is possible to run arbitary code during gem installation using the extensions
option.
From the RubyGems Manual:
Usage
spec.extensions << 'ext/rmagic/extconf.rb'
Notes
These files will be run when the gem is installed, causing the
C (or whatever) code to be compiled on the user’s machine.
Just place whatever ruby code you need into the extconf.rb
(or equivalent) file.
Examples for building C-extensions from the RubyGems Guides:
http://guides.rubygems.org/c-extensions/
Can't install gems on OS X El Capitan
Disclaimer: @theTinMan and other Ruby developers often point out not to use sudo
when installing gems and point to things like RVM. That's absolutely true when doing Ruby development. Go ahead and use that.
However, many of us just want some binary that happens to be distributed as a gem (e.g. fakes3
, cocoapods
, xcpretty
…). I definitely don't want to bother with managing a separate ruby. Here are your quicker options:
Option 1: Keep using sudo
Using sudo
is probably fine if you want these tools to be installed globally.
The problem is that these binaries are installed into /usr/bin
, which is off-limits since El Capitan. However, you can install them into /usr/local/bin
instead. That's where Homebrew install its stuff, so it probably exists already.
sudo gem install fakes3 -n/usr/local/bin
Gems will be installed into /usr/local/bin
and every user on your system can use them if it's in their PATH.
Option 2: Install in your home directory (without sudo)
The following will install gems in ~/.gem
and put binaries in ~/bin
(which you should then add to your PATH
).
gem install fakes3 --user-install -n~/bin
Make it the default
Either way, you can add these parameters to your ~/.gemrc
so you don't have to remember them:
gem: -n/usr/local/bin
i.e. echo "gem: -n/usr/local/bin" >> ~/.gemrc
or
gem: --user-install -n~/bin
i.e. echo "gem: --user-install -n~/bin" >> ~/.gemrc
(Tip: You can also throw in --no-document
to skip generating Ruby developer documentation.)
gem cannot access rubygems.org
api.rubygems.org
is currently experiencing issues with IPv6 setup: this hostname has 4 IPv6 addresses, but responds on neither of them. Neither to ping
, nor to TCP connection attempts. When you are running gem
, your gem
tries IPv6 addresses first and times out on them, not having time to even try IPv4 addresses.
The solution is to lower priority of IPv6 addresses for api.rubygems.org
, so that gem
will try IPv4 addresses first. In order to do it, put these lines into /etc/gai.conf
:
# Debian defaults.
precedence ::1/128 50
precedence ::/0 40
precedence 2002::/16 30
precedence ::/96 20
precedence ::ffff:0:0/96 10
# Low precedence for api.rubygems.org IPv6 addresses.
precedence 2a04:4e42::0/32 5
NoMethodError: private method `open' called for Gem::Package:Class An error occurred while installing rake (10.0.3), and Bundler cannot continue
You should first update Rubygems:
gem update --system
And then update Bundler:
gem install bundler
Does Rails need to be installed as a system gem?
It totally makes sense to NOT install rails as system gem.
Without messing up rbenv or other ruby version manager you use, below are brief steps to create (initialize) a new Rails app from a directory with a Gemfile:
mkdir rails_app
cd rails_app
vi Gemfile # Edit it to include a rails version you need
bundle --path vendor # Wait for bundler to finish
bundle exec rails new ./
The last step would ask: Overwrite /path/to/rails_app/Gemfile? (enter "h" for help) [Ynaqdh]
. Input y
to get the default Rails Gemfile content.
Note: the above steps specify the local vendor
directory (inside the rails app folder) to avoid installing gems to system global scope.
Native extensions fallback to pure Ruby if not supported on gem install
This is my best result attempting to answer my own question to date. It appears to work for JRuby (tested in Travis and on my local installation under RVM), which was my main goal. However, I would be very interested in confirmations of it working in other environments, and for any input on how to make it more generic and/or robust:
The gem installation code expects a Makefile
as output from extconf.rb
, but has no opinion on what that should contain. Therefore extconf.rb
can decide to create a do nothing Makefile
, instead of calling create_makefile
from mkmf
. In practice that might look like this:
ext/foo/extconf.rb
can_compile_extensions = false
want_extensions = true
begin
require 'mkmf'
can_compile_extensions = true
rescue Exception
# This will appear only in verbose mode.
$stderr.puts "Could not require 'mkmf'. Not fatal, the extensions are optional."
end
if can_compile_extensions && want_extensions
create_makefile( 'foo/foo' )
else
# Create a dummy Makefile, to satisfy Gem::Installer#install
mfile = open("Makefile", "wb")
mfile.puts '.PHONY: install'
mfile.puts 'install:'
mfile.puts "\t" + '@echo "Extensions not installed, falling back to pure Ruby version."'
mfile.close
end
As suggested in the question, this answer also requires the following logic to load the Ruby fallback code in the main library:
lib/foo.rb (excerpt)
begin
# Extension target, might not exist on some installations
require 'foo/foo'
rescue LoadError
# Pure Ruby fallback, should cover all methods that are otherwise in extension
require 'foo/foo_pure_ruby'
end
Following this route also requires some juggling of rake tasks, so that the default rake task doesn't attempt to compile on Rubies that we're testing on that don't have capability to compile extensions:
Rakefile (excerpts)
def can_compile_extensions
return false if RUBY_DESCRIPTION =~ /jruby/
return true
end
if can_compile_extensions
task :default => [:compile, :test]
else
task :default => [:test]
end
Note the Rakefile
part doesn't have to be completely generic, it just has to cover known environments we want to locally build and test the gem on (e.g. all the Travis targets).
I have noticed one annoyance. That is by default you will see Ruby Gems' message Building native extensions. This could take a while...
, and no indication that the extension compilation was skipped. However, if you invoke the installer with gem install foo --verbose
you do see the messages added to extconf.rb
, so it's not too bad.
Related Topics
Simple_Form with Bootstrap Check Box
Argument Out of Range Rails 4 and Bootstrap3-Datetimepicker-Rails
Understand "Current_User" Concept When Creating a Login Session in Ruby
Using Ruby CSV to Extract One Column
Ruby on Rails - Rack-Cors Multiple Origins with Different Resources
Ruby on Rails Triggers Update on Serialized Attribute Every Time
Ruby Gem Installation Error After Osx Yosemite and Xcode 6 Installation
How to Send Mail with Ruby Over Smtp with Ssl (Not with Rails, No Tls for Gmail)
Ruby - No Pid Found in Tmp/Pids/Thin.Pid (Thin::Pidfilenotfound)
Ruby/Rails - How to Create a Class and Access It from the Controller
Ruby Imap "Changes" Since Last Check
Error Nomethoderror: Undefined Method 'Debug_Rjs=' for Actionview::Base:Class