---
categories: web
title: Howto Rails
...

* Documentation : <https://guides.rubyonrails.org/>
* YAML Cookbook for Ruby : <http://www.yaml.org/YAML_for_ruby.html>
* Rôle Ansible : <https://forge.evolix.org/projects/ansible-roles/repository/show/rbenv>

[Ruby On Rails](https://rubyonrails.org/) est un framework web libre écrit en Ruby appliquant le principe MVC (Modèle-Vue-Contrôleur). Nous l'utilisons notamment dans le logiciel [Chexpire](https://chexpire.org/).

## Installation

### Installation avec rbenv

[rbenv](https://github.com/rbenv/rbenv) permet d'avoir un environnement compilé par utilisateur.
C'est donc le développeur qui gère sa version de Ruby, ses Gems, etc... on ne lui installe même pas Ruby !

> *Note* : vous devez mettre les droits `exec` sur la partition */home* pour cette installation

~~~
# apt install rbenv build-essential libssl-dev libreadline-dev zlib1g-dev
# apt install liblzma-dev libmariadbclient-dev

$ eval "$(rbenv init -)"
$ mkdir ~/.rbenv/plugins
$ git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
$ rbenv install --list
$ TMPDIR=~/tmp rbenv install 2.5.1
$ rbenv global 2.5.1
$ gem install bundler rails
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc

$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]

$ gem list

*** LOCAL GEMS ***

actioncable (5.2.1)
actionmailer (5.2.1)
actionpack (5.2.1)
actionview (5.2.1)
activejob (5.2.1)
activemodel (5.2.1)
activerecord (5.2.1)
activestorage (5.2.1)
activesupport (5.2.1)
arel (9.0.0)
bigdecimal (default: 1.3.4)
builder (3.2.3)
bundle (0.0.1)
bundler (1.16.3)
[…]
~~~

### Installation avec ruby système

Cela permet de bénéficier des mises à jour de sécurité de Ruby, tout en gérant les Gems par utilisateur.

~~~
# apt install ruby ruby-dev ruby-bundler
# apt install liblzma-dev libmariadbclient-dev

$ export PATH="$HOME/.gem/ruby/2.3.0/bin:$PATH"
$ gem install bundler
$ echo 'PATH="$HOME/.gem/ruby/2.3.0/bin:$PATH"' >> ~/.bashrc

$ ruby -v
ruby 2.3.3p222 (2016-11-21) [x86_64-linux-gnu]

$ gem list

*** LOCAL GEMS ***

actionmailer (4.2.7.1)
actionpack (4.2.7.1)
actionview (4.2.7.1)
activejob (4.2.7.1)
activemodel (4.2.7.1)
activerecord (4.2.7.1)
activesupport (4.2.7.1)
arel (6.0.3)
atomic (1.1.16)
bigdecimal (1.2.8)
binding_of_caller (0.7.2)
blankslate (3.1.3)
builder (3.2.2)
bundle (0.0.1)
bundler (1.16.3, 1.13.6)
[…]
~~~~


## Utilisation

### Démarrer un projet Rails

<https://guides.rubyonrails.org/getting_started.html>

~~~
$ rails --version
Rails 4.2.7.1

$ rails new foo
$ cd foo
~~~

On peut alors ajuster la configuration (routes, base de données, etc.), coder son projet, le stocker dans un repository Git, etc.

Pour voir en local le résultat :

~~~
$ cd foo
$ ./bin/rails server
=> Booting WEBrick
=> Rails 4.2.7.1 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2018-08-12 11:43:15] INFO  WEBrick 1.3.1
[2018-08-12 11:43:15] INFO  ruby 2.3.3 (2016-11-21) [x86_64-linux-gnu]
[2018-08-12 11:43:15] INFO  WEBrick::HTTPServer#start: pid=12383 port=3000
~~~

On peut alors le visualiser sur <http://127.0.0.1:3000>

### Les environnements de Rails

Rails propose trois environnements d'exécution distincts·: `production`, `development` et `test`.

Chacun a un but précis facilement devinable depuis son nom.

Pour lancer dans un certain environnement les commandes qui agissent avec l'application (par exemple les
tâches [Rake](http://guides.rubyonrails.org/command_line.html#rake-is-ruby-make)), il suffit de définir la
variable d'environnement RAILS_ENV, par exemple·: 

~~~
$ RAILS_ENV=production rake db:migrate
~~~

### Redémarrage optimisé

Pour redémarrer une application Rails de façon optimisée, il suffit de créer ou de mettre à jour le timestamp du fichier
`tmp/restart.txt` (avec la commande `touch` par exemple). À la prochaîne requête, l'application sera redémarrée et tout l'environement chargé de nouveau.

Pour que l'application redémarre à chaque requête on peut aussi créer un fichier `tmp/always_restart.txt` et le supprimer une fois qu'on ne souhaite plus ce comportement.

### Unité systemd

TODO, cf https://wiki.evolix.org/HowtoSystemd#systemd-par-utilisateur

### Les Gems et Bundler

Les [Gems](https://rubygems.org/) sont des paquets contentant des librairies et/ou des applications écrites en Ruby.
Elles sont souvent utilisées, notamment dans Rails.

~~~
$ gem -v
2.5.2.1

$ gem list

$ gem env
RubyGems Environment:
  - RUBYGEMS VERSION: 2.5.2.1
  - RUBY VERSION: 2.3.3 (2016-11-21 patchlevel 222) [x86_64-linux-gnu]
[…]
~~~

Les Gems peuvent se trouver à plusieurs endroits :

* installée par un paquet Debian (`/usr/lib/ruby/2.3.0/`)
* installée en temps que Gem système (`/var/lib/gems/2.3.0/`)
* installée en temps que Gem utilisateur (`~/.gem/ruby/2.3.0/`)

Une application Rails contient un fichier `Gemfile` qui précise les Gems nécessaires et leurs versions :

~~~
source 'https://rubygems.org'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.2.7.1'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
[…]
~~~

[Bundler](https://bundler.io/) est l'outil de prédilection pour installer les Gems nécessaires.

Avec la présence d'un fichier `Gemfile`, il suffit de lancer la commande :

~~~
$ bundle install
~~~

Un snapshot des Gems installées est aussi gardé dans un fichier `Gemfile.lock` permettant d'accélérer la résolution des dépendances.

En général, on inclut les fichiers `Gemfile` et `Gemfile.lock` dans le repository du projet.

### Capistrano

[Capistrano](https://capistranorb.com/) est un outil de déploiement particulièrement adapté aux développements en Ruby (dont Rails) mais parfaitement compatible avec d'autres langages ou frameworks (PHP, Java…).

Il est généralement exécuté depuis un poste de développement ou d'intégration. Il se connecte alors en SSH à un ou plusieurs serveurs (via SSH) pour y exécuter des tâches selon les rôles du serveur (application, base de données…).

Il est recommandé d'installer Capistrano via Bundler (dans le cadre d'un projet), mais il est évidemment possible de l'utiliser indépendamment (`# gem install capistrano`). Sa commande principale est `cap`.

À l'initialisation, Capistrano créé plusieurs fichiers, dont ceux nécessaires à gérer plusieurs envvironnements (production, staging…) :

~~~
├── Capfile
├── config
│   ├── deploy
│   │   ├── production.rb
│   │   └── staging.rb
│   └── deploy.rb
└── lib
    └── capistrano
            └── tasks
~~~

* `Capfile` est lu par l'exécutable au chargement pour déterminer les modules à charger ;
* `config/deploy.rb` contient la configuration générale ;
* `config/deploy/*.rb` contiennent les particularités de chaque environnement ;
* `lib/capistrano/tasks/*` permet de créer ses propres tâches à exécuter lors du déploiement.

Il existe de nombreux modules additionnels pour Capistrano afin d'apporter le support de frameworks ou bibliothèques : rails, rbenv, sidekiq, whenever… Il s'agit de gems à installer manuellement ou via Bundler.

Capistrano s'appuie massivement sur Rake, qui est une implémentation en Ruby du principe de Make (et des Makefile). Tout se passe donc sous forme de taches exécutées dans un ordre défini par un arbre de dépendance. De fait toutes les tâches peuvent être surchargées pour en personnaliser l'exécution.

Dans la plupart des cas (surtout avec Rails ou d'autres frameworks supportés) le déploiement en production se fait via la commande `[bundle exec] cap production deploy`. Un déploiement classique d'application Rails va faire :
* mise à jour du code source (via Git)
* création d'une "release" avec copie du code, liaison de fichiers/dossiers clés…
* pré-compilation des assets (images, JS, CSS…)
* migration de la base de données
* activation de la release
* relance du serveur d'application

## Serveur web

Pour une utilisation en production d'une application Rails, on peut utiliser plusieurs solutions : Puma, Unicorn, Passenger, etc.

### Puma

[Puma](http://puma.io/) est un serveur web pour Ruby orienté pour la performance et le traitement en parallèle.
Il peut s'installer par package (`apt install puma`) ou par Gem.

~~~
$ gem install puma

$ cd projet
$ puma -S puma.state -b tcp://127.0.0.1:3042 -b unix:///tmp/puma.sock
Puma starting in single mode...
* Version 3.6.0 (ruby 2.3.3-p222), codename: Sleepy Sunday Serenity
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://127.0.0.1:3042
* Listening on unix:///tmp/puma.sock
Use Ctrl-C to stop

^C- Gracefully stopping, waiting for requests to finish
=== puma shutdown: 2018-08-25 23:15:36 +0200 ===
- Goodbye!
~~~

On peut également créer un fichier de paramètres *puma.rb* que l'on indiquera via `-F puma.rb` :

~~~
#!/usr/bin/env puma

directory '/home/foo/project'
environment 'production'
bind 'unix:///tmp/puma.sock'
port  ENV.fetch("PORT") { 3042 }
[…]
~~~

On peut créer des unités systemd pour gérer cela : <https://github.com/puma/puma/blob/master/docs/systemd.md>

### Unicorn

[Unicorn](https://bogomips.org/unicorn/) est un serveur web pour applications Rack conçu pour les clients rapides et optimisé pour Unix.
Il peut s'installer par package (`apt install unicorn`) ou par Gem.

~~~
$ gem install unicorn

$ cd projet
$ unicorn -l 127.0.0.1:3042
I, [2018-08-26T00:05:09.967114 #2133]  INFO -- : listening on addr=127.0.0.1:3042 fd=9
I, [2018-08-26T00:05:09.967275 #2133]  INFO -- : worker=0 spawning...
I, [2018-08-26T00:05:09.967786 #2133]  INFO -- : master process ready
I, [2018-08-26T00:05:09.968015 #2135]  INFO -- : worker=0 spawned pid=2135
I, [2018-08-26T00:05:09.968129 #2135]  INFO -- : Refreshing Gem list
[…]
~~~

### Passenger

[Passenger](https://www.phusionpassenger.com/) est un serveur d'application qui propose des fonctionnalités avancées.

On peut l'installer sous forme de Gem pour le développement :

~~~
$ gem install passenger

$ cd projet
$ passenger start
=============== Phusion Passenger Standalone web server started ===============
Environment: development
Accessible via: http://0.0.0.0:3000/
You can stop Phusion Passenger Standalone by pressing Ctrl-C.
~~~

On peut l'installer pour [Apache](HowtoApache) ainsi :

~~~
# apt install libapache2-mod-passenger
~~~

Par défaut, Passenger est activé pour tous les sites d'Apache et propose une auto-détection des environnements Rails et Rack, on commence par désactiver ce comportement dans `/etc/apache2/mods-available/passenger.conf` ainsi que d'autres ajustements :

~~~
<IfModule mod_passenger.c>
  PassengerRoot /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini
  PassengerDefaultRuby /usr/bin/ruby

  # On ajoute les lignes suivantes:
  PassengerEnabled off
  RailsAutoDetect off
  RackAutoDetect off
  PassengerFriendlyErrorPages off

  PassengerTempDir /var/tmp/
  PassengerUploadBufferDir /var/tmp
  # S'assurer que les permissions sur le dossier permettent à Apache d'écrire dedans
  # cf. http://modrails.com/documentation/Users%20guide%20Apache.html#PassengerUploadBufferDir
  # C'est surtout important avec Apache ITK !
</IfModule>
~~~

> *Note* : Passenger stocke tous ses fichiers temporaires dans le répertoire /tmp/ par défaut. Pour diverses raisons (de place, de droits voire de performance), il peut être intéressant d'en définir un autre. Cela ce fait via la directive Apache du module Passenger `PassengerTempDir`.

Et voici un VirtualHost minimal pour Apache :

~~~
<VirtualHost>
  ServerName foo.example.com
  PassengerEnabled on
  RailsAutoDetect on
  #RailsEnv development

  DocumentRoot /home/foo/www/current
  <Directory /home/redmine/www/current>
    Allow from all
    Options -MultiViews
  </Directory>

  # Pour Apache ITK
  AssignUserID foo foo
</VirtualHost>
~~~

> *Note* : Passenger détermine sous quel utilisateur il va lancer l'application en se basant sur le propriétaire du fichier `config/environment.rb` de l'application Rails. Pour Apache ITK, il faut donc y placer un propriétaire identique à celui utilisé dans `AssignUserID`.

> *Note* : par défaut, Passenger fait tourner l'application en mode *production*. On peut forcer un certain environnement en ajustant la directive `RailsEnv` dans le VirtualHost.


## FAQ

### GEM_HOME

On peut forcer l'environnement des Gems (pour ignorer `/var/lib/gems/2.3.0` par exemple) :

~~~
$ export GEM_HOME=/$HOME/.gem/ruby/2.3.0
~~~

### rbenv via Github

Si l'on souhaite installer rbenv sans utiliser le paquet Debian, il suffit de commencer la procédure ainsi :

~~~
$ git clone git://github.com/sstephenson/rbenv.git .rbenv
$ git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
$ eval "$(rbenv init -)"
$ rbenv install --list
~~~

puis de suivre les instructions [décrites plus haut](HowtoRails#installation-avec-rbenv)

### Ruby Enterprise Edition (REE)

<http://rubyenterpriseedition.com/documentation.html>

[REE](http://rubyenterpriseedition.com/) est une version patchée de Ruby optimisée pour les applications Web et créée par les mêmes développeurs que Passenger.
On peut l'installer en le compilant à la main (par défaut il s'installera dans e dossier `/opt` en totale isolation du reste du système)
Il suffit de récupérer [les sources](http://rubyenterpriseedition.com/download.html), de la décompresser et de lancer le script `./installer --no-dev-docs` en tant que root. Le reste de la procédure est indiqué par l'installateur.
Les dépendances classiques de compilations seront nécessaires (`apt install build-essential libssl-dev libreadline-dev zlib1g-dev`).

Si l'on utilise avec Passenger, on pourra forcer l'utilisation de REE ainsi :

~~~
<IfModule passenger_module>
    PassengerRoot /opt/ruby-enterprise-<version>/lib/ruby/gems/1.8/gems/passenger-3.0.0
    PassengerRuby /opt/ruby-enterprise-<version>/bin/ruby  
</IfModule>
~~~

### Lister les Gems

Il n'existe pas de moyen de lister les Gems installées uniquement sur le système, la commande `gem` cherche
à la fois dans le dossier `.gem` de l'utilisateur et le dossier système. La solution est d'utiliser un script
spécialisé qui fait cette recherche (un script `list_gems` doit traîner dans un coin…).

### Économiser un peu d'espace

Par défaut, `gem` installe la documentation aux formats RDoc (documentation html) et RI (documentation console)
pour les Gems installées. Pour éviter celà, créer un fichier `.gemrc` dans la `$HOME` de l'utilisateur avec le
contenu suivant :

~~~
gem: --no-rdoc --no-ri
~~~

### rails_env

~~~
rails_env = `head -1 $(HOME}/www/current/config/database.yml | tr ':' ' '`
~~~

### Rails 2.3.2->2.3.5 & Passenger & les logs

Une petite erreur en environnement de production affecte les versions 2.3.2 à 2.3.5 (incluse) de Rails. Les logs de l'application ne sont pas écrits dans le fichier `log/production.log`. Pour pallier cette erreur, il faut appliquer le patch qu'on trouve [ici](https://rails.lighthouseapp.com/projects/8994/tickets/3577-failsafe-middleware-should-flush-the-logger) (deuxième fichier).
