Ruby gem typhoeus: Could not open library 'libcurl'

I installed the Ruby gem typhoeus, but when I require it, I get the error below.

Anyone know how to get it to work?

# gem install typhoeus
Building native extensions. This could take a while...
Successfully installed ffi-1.10.0
Fetching: ethon-0.12.0.gem (100%)
Successfully installed ethon-0.12.0
Fetching: typhoeus-1.3.1.gem (100%)
Successfully installed typhoeus-1.3.1
Parsing documentation for ffi-1.10.0
Installing ri documentation for ffi-1.10.0
Parsing documentation for ethon-0.12.0
Installing ri documentation for ethon-0.12.0
Parsing documentation for typhoeus-1.3.1
Installing ri documentation for typhoeus-1.3.1
Done installing documentation for ffi, ethon, typhoeus after 10 seconds
3 gems installed

# irb
irb(main):001:0> require 'typhoeus'
Traceback (most recent call last):
       16: from /nix/store/77i8a6rqnaqsc9av37xfw4sw4p61nfka-ruby-2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
       15: from /nix/store/77i8a6rqnaqsc9av37xfw4sw4p61nfka-ruby-2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
       14: from /root/.gem/ruby/2.5.0/gems/ethon-0.12.0/lib/ethon.rb:15:in `<top (required)>'
       13: from /nix/store/77i8a6rqnaqsc9av37xfw4sw4p61nfka-ruby-2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
       12: from /nix/store/77i8a6rqnaqsc9av37xfw4sw4p61nfka-ruby-2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
       11: from /root/.gem/ruby/2.5.0/gems/ethon-0.12.0/lib/ethon/curl.rb:8:in `<top (required)>'
       10: from /root/.gem/ruby/2.5.0/gems/ethon-0.12.0/lib/ethon/curl.rb:13:in `<module:Ethon>'
        9: from /root/.gem/ruby/2.5.0/gems/ethon-0.12.0/lib/ethon/curl.rb:27:in `<module:Curl>'
        8: from /nix/store/77i8a6rqnaqsc9av37xfw4sw4p61nfka-ruby-2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
        7: from /nix/store/77i8a6rqnaqsc9av37xfw4sw4p61nfka-ruby-2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
        6: from /root/.gem/ruby/2.5.0/gems/ethon-0.12.0/lib/ethon/curls/settings.rb:1:in `<top (required)>'
        5: from /root/.gem/ruby/2.5.0/gems/ethon-0.12.0/lib/ethon/curls/settings.rb:2:in `<module:Ethon>'
        4: from /root/.gem/ruby/2.5.0/gems/ethon-0.12.0/lib/ethon/curls/settings.rb:7:in `<module:Curl>'
        3: from /root/.gem/ruby/2.5.0/gems/ffi-1.10.0/lib/ffi/library.rb:99:in `ffi_lib'
        2: from /root/.gem/ruby/2.5.0/gems/ffi-1.10.0/lib/ffi/library.rb:99:in `map'
        1: from /root/.gem/ruby/2.5.0/gems/ffi-1.10.0/lib/ffi/library.rb:145:in `block in ffi_lib'
LoadError (Could not open library 'libcurl': libcurl: cannot open shared object file: No such file or directory.)
Could not open library 'libcurl.so': libcurl.so: cannot open shared object file: No such file or directory.
Could not open library 'libcurl.so.4': libcurl.so.4: cannot open shared object file: No such file or directory

The error is that it cannot find curl, you should try to install it either in a local nix-shell or globally, I’m fairly certain you’d find it in the curlFull package (nix-env -iA nixos.curlFull).

I installed curl and curlFull, and also ran nix-shell -p curl, but in every case I get the same error when I try to require the gem.

What else can I try?

# gem install typhoeus

Installing libraries with native dependencies through RubyGems won’t work, because it won’t be able to find Nix’s libraries.

You probably need to use something like Bundix. If it still doesn’t work, you’ll need to tell Nixpkgs what libraries typhoeus needs in the gem-config.nix file in Nixpkgs.

Thank you for the response. I did some searching and came across a file in the Nix repo which mentions typhoeus and its dependency on curl:

https://github.com/NixOS/nixpkgs/blob/bce47ea9d5fa962736ddd4a254a27a5fd2cdee9a/pkgs/development/ruby-modules/gem-config/default.nix

But I don’t know how to make use of this file. Are you saying I can’t actually install the gem for development, but can only “bundle” it as part of a packaged application? How would I require the gem from irb in that case?

Assuming you already have a Gemfile, here’s what you need to get set up:

nix-shell -p bundix --run 'bundix -l'

cat <<EOF > shell.nix
{ pkgs ? import <nixpkgs> {} }: with pkgs;

(bundlerEnv {
  name = "foo";
  gemdir = ./.;
}).env
EOF

Then, you can run nix-shell to start a shell with all of your project’s dependencies available to you.

1 Like

That worked! Thanks for your help.

So in NixOS, gems like typhoeus that have dependencies cannot be installed system-wide, but only per project, using a combination of bundler, bundix, and nix-shell, with configuration files (Gemfile, gemset.nix, shell.nix) in each project directory? That is quite a contrast to the approach taken by Guix (another functional package manager), where gems are just packages like any other, and are installed through the package manager, with all dependencies handled automatically.

https://github.com/pjotrp/guix-notes/blob/78992ed93f1ff91ee755e3a4f1ef3af7aa8410df/RUBY.org

Behind the scenes gems are packaged as derivations like everything else but, unlike haskellPackages those derivations are not directly accessible (e.g. by using rubyPackages), they are accessed through bundlerEnv.
When bundlerEnv needs a gem with native dependency this is added as a dependency of the gem derivation and it is transparent to the user, like in the @qyliss example where curl is never mentioned.

I’m wondering if doing something ‘a la guix’ and allow the user to do something like nix-env -iA nixos.rubyGems.typhoeus (and have something like)is something feasible and what its drawbacks are, would that imply that we have to manually keep a consistent set of non-incompatible gems?

cc @cstrahan @manveru

Just to expand on this, the reason for that is that Nixpkgs doesn’t provide derivations for specific versions of Ruby Gems. Instead, it provides functions for building arbitrary versions of Ruby Gems, since different programs will be locked to different versions, and would be unhappy with Nixpkgs supplying different versions.

I’ve thought about this too, in particular because it would be useful for nix-shell-shebang scripts, like ghcWithPackages. I haven’t yet come up with a way it would be useful or maintainable though, for the reasons you describe.

Perhaps, rather than exposing packages, we could support such a use case in a similar way Bundler does with bundler/inline… We could have some library you could make available to your script that would essentially run Bundix and then apply the resulting environment to your script. I’d need to think that through a little more to determine whether it would even be possible, but my instinct is that it should be.

1 Like

Thank you kindly for the explanation

It looks like it’s happening! Thanks @manveru

https://github.com/NixOS/nixpkgs/pull/61114

Yes, it is now possible to do:

# nix-shell -p "ruby.withPackages (ps: with ps; [ typhoeus ])"

# irb
irb(main):001:0> require 'typhoeus'
=> true

Documentation: https://github.com/NixOS/nixpkgs/blob/cda41cf743f0942aa3f1833d5e0385ad585fa411/doc/languages-frameworks/ruby.section.md

This is not quite the same as installing a gem with the package manager à la Guix, but definitely an improvement over what we had before.

While ruby.withPackages is nifty for small scripts, it’s not the recommend approach when building anything larger than that due version constraints so you might as well get started with regular ruby/bundler for development and then use bundix to create the execution environment when deploying.

I don’t mean to take anything away from @manveru and his work to add .withPackages - it’s definitely handy.