about summary refs log tree commit diff
path: root/nixpkgs/doc/languages-frameworks/ruby.section.md
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2019-08-24 09:47:56 +0000
committerAlyssa Ross <hi@alyssa.is>2019-08-25 09:24:33 +0000
commit6f87d54e6f72bf8f25dad5e9ff9cce82c10b4781 (patch)
treebf82200a42c070620864c19b84222f84c77d4097 /nixpkgs/doc/languages-frameworks/ruby.section.md
parent1b9a13c4689af7e088eb7af5589f8c811282846a (diff)
parent61cc0b580ce601f98eef4c7bba36bbe1a9112236 (diff)
downloadnixlib-6f87d54e6f72bf8f25dad5e9ff9cce82c10b4781.tar
nixlib-6f87d54e6f72bf8f25dad5e9ff9cce82c10b4781.tar.gz
nixlib-6f87d54e6f72bf8f25dad5e9ff9cce82c10b4781.tar.bz2
nixlib-6f87d54e6f72bf8f25dad5e9ff9cce82c10b4781.tar.lz
nixlib-6f87d54e6f72bf8f25dad5e9ff9cce82c10b4781.tar.xz
nixlib-6f87d54e6f72bf8f25dad5e9ff9cce82c10b4781.tar.zst
nixlib-6f87d54e6f72bf8f25dad5e9ff9cce82c10b4781.zip
Merge commit '61cc0b580ce601f98eef4c7bba36bbe1a9112236'
Diffstat (limited to 'nixpkgs/doc/languages-frameworks/ruby.section.md')
-rw-r--r--nixpkgs/doc/languages-frameworks/ruby.section.md441
1 files changed, 441 insertions, 0 deletions
diff --git a/nixpkgs/doc/languages-frameworks/ruby.section.md b/nixpkgs/doc/languages-frameworks/ruby.section.md
new file mode 100644
index 000000000000..f58ada029dce
--- /dev/null
+++ b/nixpkgs/doc/languages-frameworks/ruby.section.md
@@ -0,0 +1,441 @@
+---
+title: Ruby
+author: Michael Fellinger
+date: 2019-05-23
+---
+
+# Ruby
+
+## User Guide
+
+### Using Ruby
+
+#### Overview
+
+Several versions of Ruby interpreters are available on Nix, as well as over 250 gems and many applications written in Ruby.
+The attribute `ruby` refers to the default Ruby interpreter, which is currently
+MRI 2.5. It's also possible to refer to specific versions, e.g. `ruby_2_6`, `jruby`, or `mruby`.
+
+In the nixpkgs tree, Ruby packages can be found throughout, depending on what
+they do, and are called from the main package set. Ruby gems, however are
+separate sets, and there's one default set for each interpreter (currently MRI
+only).
+
+There are two main approaches for using Ruby with gems.
+One is to use a specifically locked `Gemfile` for an application that has very strict dependencies.
+The other is to depend on the common gems, which we'll explain further down, and
+rely on them being updated regularly.
+
+The interpreters have common attributes, namely `gems`, and `withPackages`. So
+you can refer to `ruby.gems.nokogiri`, or `ruby_2_5.gems.nokogiri` to get the
+Nokogiri gem already compiled and ready to use.
+
+Since not all gems have executables like `nokogiri`, it's usually more
+convenient to use the `withPackages` function like this:
+`ruby.withPackages (p: with p; [ nokogiri ])`. This will also make sure that the
+Ruby in your environment will be able to find the gem and it can be used in your
+Ruby code (for example via `ruby` or `irb` executables) via `require "nokogiri"`
+as usual.
+
+#### Installing Ruby and gems
+
+The Nix and NixOS manuals explain how packages are generally installed. In the
+case of Ruby and Nix, it is important to make a distinction between whether the
+package is considered an application or a library.
+
+Applications on Nix are typically installed into your user
+profile imperatively using `nix-env -iA`, and on NixOS declaratively by adding the
+package name to `environment.systemPackages` in `/etc/nixos/configuration.nix`.
+Dependencies such as libraries are automatically installed and should not be
+installed explicitly.
+
+The same goes for Ruby applications and libraries. Ruby applications can be
+installed in your profile. But Ruby libraries you would like to use for
+development cannot be installed, at least not individually, because they won't
+be able to find each other resulting in load errors. Instead, it is possible to
+create an environment with `bundlerEnv` or `ruby.withPackages` where the
+interpreter and other executables are able to find each other and all of the
+gems.
+
+In the following examples we create an environment with Ruby 2.5, `nokogiri` and
+`pry`. There is one limitation here, and that's that you can install only one
+environment at a time, otherwise you'll encounter collisions.
+
+##### Environment defined in separate `.nix` file
+
+Create a file, e.g. `build.nix`, with the following expression:
+
+```nix
+with import <nixpkgs> {};
+ruby_2_5.withPackages (ps: with ps; [ nokogiri pry ])
+```
+
+and install it in your profile with:
+
+```shell
+nix-env -if build.nix
+```
+
+Now you can use the Ruby interpreter, as well as the extra packages (`pry`,
+`nokogiri`) that you added to the environment.
+
+##### Environment defined in ~/.config/nixpkgs/config.nix`
+
+If you prefer to, you could also add the environment as a package override to
+the nixpkgs set like this:
+
+```nix
+{ # ...
+  packageOverrides = pkgs:
+  with pkgs; {
+    myRubyEnv = ruby.withPackages (ps: with ps; [ nokogiri pry ]);
+  };
+}
+```
+
+and install it in your profile path with:
+
+```shell
+nix-env -iA nixpkgs.myRubyEnv
+```
+
+The environment is installed by referring to the attribute, and considering the
+`nixpkgs` channel was used.
+
+##### Environment defined in `/etc/nixos/configuration.nix`
+
+For the sake of completeness, here's another example of how to install the
+environment system-wide.
+
+```nix
+{ # ...
+  environment.systemPackages = with pkgs;
+  [ (ruby.withPackages (ps: with ps; [ nokogiri pry ])) ];
+}
+```
+
+#### Temporary Ruby environment with `nix-shell`
+
+The examples in the previous section showed how to install a Ruby environment
+into a profile. For development you may need to use multiple environments.
+`nix-shell` gives you the possibility to temporarily load another environment
+akin to a combined `chruby` or `rvm` and `bundle exec`.
+
+There are two methods for loading a shell with Ruby packages. The first and
+recommended method is to create an environment with `ruby.withPackages` and load
+that.
+
+```shell
+nix-shell -p "ruby.withPackages (ps: with ps; [ nokogiri pry ])"
+```
+
+The other method, which is not recommended, is to create an environment and list
+all the packages directly.
+
+```shell
+nix-shell -p ruby.gems.nokogiri ruby.gems.pry
+```
+
+Again, it's possible to launch the interpreter from the shell. The Ruby
+interpreter has the attribute `gems` which contains all Ruby gems for that
+specific interpreter.
+
+##### Load environment from `.nix` expression
+
+As explained in the Nix manual, `nix-shell` can also load an expression from a
+`.nix` file. Say we want to have Ruby 2.5, `nokogori`, and `pry`. Consider a
+`shell.nix` file with:
+
+```nix
+with import <nixpkgs> {};
+ruby.withPackages (ps: with ps; [ nokogiri pry ])
+```
+
+What's happening here?
+
+1. We begin with importing the Nix Packages collections. `import <nixpkgs>`
+   imports the `<nixpkgs>` function, `{}` calls it and the `with` statement
+   brings all attributes of `nixpkgs` in the local scope. These attributes form
+   the main package set.
+2. Then we create a Ruby environment with the `withPackages` function.
+3. The `withPackages` function expects us to provide a function as an argument
+   that takes the set of all ruby gems and returns a list of packages to include
+   in the environment. Here, we select the packages `nokogiri` and `pry` from
+   the package set.
+
+##### Execute command with `--run`
+
+A convenient flag for `nix-shell` is `--run`. It executes a command in the
+`nix-shell`. We can e.g. directly open a `pry` REPL:
+
+```shell
+nix-shell -p "ruby.withPackages (ps: with ps; [ nokogiri pry ])" --run "pry"
+```
+
+Or immediately require `nokogiri` in pry:
+
+```shell
+nix-shell -p "ruby.withPackages (ps: with ps; [ nokogiri pry ])" --run "pry -rnokogiri"
+```
+
+Or run a script using this environment:
+
+```shell
+nix-shell -p "ruby.withPackages (ps: with ps; [ nokogiri pry ])" --run "ruby example.rb"
+```
+
+##### Using `nix-shell` as shebang
+
+In fact, for the last case, there is a more convenient method. You can add a
+[shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) to your script
+specifying which dependencies `nix-shell` needs. With the following shebang, you
+can just execute `./example.rb`, and it will run with all dependencies.
+
+```ruby
+#! /usr/bin/env nix-shell
+#! nix-shell -i ruby -p "ruby.withPackages (ps: with ps; [ nokogiri rest-client ])"
+
+require 'nokogiri'
+require 'rest-client'
+
+body = RestClient.get('http://example.com').body
+puts Nokogiri::HTML(body).at('h1').text
+```
+
+### Developing with Ruby
+
+#### Using an existing Gemfile
+
+In most cases, you'll already have a `Gemfile.lock` listing all your dependencies.
+This can be used to generate a `gemset.nix` which is used to fetch the gems and
+combine them into a single environment.
+The reason why you need to have a separate file for this, is that Nix requires
+you to have a checksum for each input to your build.
+Since the `Gemfile.lock` that `bundler` generates doesn't provide us with
+checksums, we have to first download each gem, calculate its SHA256, and store
+it in this separate file.
+
+So the steps from having just a `Gemfile` to a `gemset.nix` are:
+
+```shell
+bundle lock
+bundix
+```
+
+If you already have a `Gemfile.lock`, you can simply run `bundix` and it will
+work the same.
+
+To update the gmes in your `Gemfile.lock`, you may use the `bundix -l` flag,
+which will create a new `Gemfile.lock` in case the `Gemfile` has a more recent
+time of modification.
+
+Once the `gemset.nix` is generated, it can be used in a
+`bundlerEnv` derivation. Here is an example you could use for your `shell.nix`:
+
+```nix
+# ...
+let
+  gems = bundlerEnv {
+    name = "gems-for-some-project";
+    gemdir = ./.;
+  };
+in mkShell { buildInputs = [ gems gems.wrappedRuby ]; }
+```
+
+With this file in your directory, you can run `nix-shell` to build and use the gems.
+The important parts here are `bundlerEnv` and `wrappedRuby`.
+
+The `bundlerEnv` is a wrapper over all the gems in your gemset. This means that
+all the `/lib` and `/bin` directories will be available, and the executables of
+all gems (even of indirect dependencies) will end up in your `$PATH`.
+The `wrappedRuby` provides you with all executables that come with Ruby itself,
+but wrapped so they can easily find the gems in your gemset.
+
+One common issue that you might have is that you have Ruby 2.6, but also
+`bundler` in your gemset. That leads to a conflict for `/bin/bundle` and
+`/bin/bundler`. You can resolve this by wrapping either your Ruby or your gems
+in a `lowPrio` call. So in order to give the `bundler` from your gemset
+priority, it would be used like this:
+
+```nix
+# ...
+mkShell { buildInputs = [ gems (lowPrio gems.wrappedRuby) ]; }
+```
+
+
+#### Gem-specific configurations and workarounds
+
+In some cases, especially if the gem has native extensions, you might need to
+modify the way the gem is built.
+
+This is done via a common configuration file that includes all of the
+workarounds for each gem.
+
+This file lives at `/pkgs/development/ruby-modules/gem-config/default.nix`,
+since it already contains a lot of entries, it should be pretty easy to add the
+modifications you need for your needs.
+
+In the meanwhile, or if the modification is for a private gem, you can also add
+the configuration to only your own environment.
+
+Two places that allow this modification are the `ruby` derivation, or `bundlerEnv`.
+
+Here's the `ruby` one:
+
+```nix
+{ pg_version ? "10", pkgs ? import <nixpkgs> { } }:
+let
+  myRuby = pkgs.ruby.override {
+    defaultGemConfig = pkgs.defaultGemConfig // {
+      pg = attrs: {
+        buildFlags =
+        [ "--with-pg-config=${pkgs."postgresql_${pg_version}"}/bin/pg_config" ];
+      };
+    };
+  };
+in myRuby.withPackages (ps: with ps; [ pg ])
+```
+
+And an example with `bundlerEnv`:
+
+```nix
+{ pg_version ? "10", pkgs ? import <nixpkgs> { } }:
+let
+  gems = pkgs.bundlerEnv {
+    name = "gems-for-some-project";
+    gemdir = ./.;
+    gemConfig = pkgs.defaultGemConfig // {
+      pg = attrs: {
+        buildFlags =
+        [ "--with-pg-config=${pkgs."postgresql_${pg_version}"}/bin/pg_config" ];
+      };
+    };
+  };
+in mkShell { buildInputs = [ gems gems.wrappedRuby ]; }
+```
+
+And finally via overlays:
+
+```nix
+{ pg_version ? "10" }:
+let
+  pkgs = import <nixpkgs> {
+    overlays = [
+      (self: super: {
+        defaultGemConfig = super.defaultGemConfig // {
+          pg = attrs: {
+            buildFlags = [
+              "--with-pg-config=${
+                pkgs."postgresql_${pg_version}"
+              }/bin/pg_config"
+            ];
+          };
+        };
+      })
+    ];
+  };
+in pkgs.ruby.withPackages (ps: with ps; [ pg ])
+```
+
+Then we can get whichever postgresql version we desire and the `pg` gem will
+always reference it correctly:
+
+```shell
+$ nix-shell --argstr pg_version 9_4 --run 'ruby -rpg -e "puts PG.library_version"'
+90421
+
+$ nix-shell --run 'ruby -rpg -e "puts PG.library_version"'
+100007
+```
+
+Of course for this use-case one could also use overlays since the configuration
+for `pg` depends on the `postgresql` alias, but for demonstration purposes this
+has to suffice.
+
+#### Adding a gem to the default gemset
+
+Now that you know how to get a working Ruby environment with Nix, it's time to
+go forward and start actually developing with Ruby.
+We will first have a look at how Ruby gems are packaged on Nix. Then, we will
+look at how you can use development mode with your code.
+
+All gems in the standard set are automatically generated from a single
+`Gemfile`. The dependency resolution is done with `bundler` and makes it more
+likely that all gems are compatible to each other.
+
+In order to add a new gem to nixpkgs, you can put it into the
+`/pkgs/development/ruby-modules/with-packages/Gemfile` and run
+`./maintainers/scripts/update-ruby-packages`.
+
+To test that it works, you can then try using the gem with:
+
+```shell
+NIX_PATH=nixpkgs=$PWD nix-shell -p "ruby.withPackages (ps: with ps; [ name-of-your-gem ])"
+```
+
+#### Packaging applications
+
+A common task is to add a ruby executable to nixpkgs, popular examples would be
+`chef`, `jekyll`, or `sass`. A good way to do that is to use the `bundlerApp`
+function, that allows you to make a package that only exposes the listed
+executables, otherwise the package may cause conflicts through common paths like
+`bin/rake` or `bin/bundler` that aren't meant to be used.
+
+The absolute easiest way to do that is to write a
+`Gemfile` along these lines:
+
+```ruby
+source 'https://rubygems.org' do
+  gem 'mdl'
+end
+```
+
+If you want to package a specific version, you can use the standard Gemfile
+syntax for that, e.g. `gem 'mdl', '0.5.0'`, but if you want the latest stable
+version anyway, it's easier to update by simply running the `bundle lock` and
+`bundix` steps again.
+
+Now you can also also make a `default.nix` that looks like this:
+
+```nix
+{ lib, bundlerApp }:
+
+bundlerApp {
+  pname = "mdl";
+  gemdir = ./.;
+  exes = [ "mdl" ];
+}
+```
+
+All that's left to do is to generate the corresponding `Gemfile.lock` and
+`gemset.nix` as described above in the `Using an existing Gemfile` section.
+
+##### Packaging executables that require wrapping
+
+Sometimes your app will depend on other executables at runtime, and tries to
+find it through the `PATH` environment variable.
+
+In this case, you can provide a `postBuild` hook to `bundlerApp` that wraps the
+gem in another script that prefixes the `PATH`.
+
+Of course you could also make a custom `gemConfig` if you know exactly how to
+patch it, but it's usually much easier to maintain with a simple wrapper so the
+patch doesn't have to be adjusted for each version.
+
+Here's another example:
+
+```nix
+{ lib, bundlerApp, makeWrapper, git, gnutar, gzip }:
+
+bundlerApp {
+  pname = "r10k";
+  gemdir = ./.;
+  exes = [ "r10k" ];
+
+  buildInputs = [ makeWrapper ];
+
+  postBuild = ''
+    wrapProgram $out/bin/r10k --prefix PATH : ${lib.makeBinPath [ git gnutar gzip ]}
+  '';
+}
+```