Framework de Programmation des Serveurs Web

Initiation à Ruby on Rails (RoR)

Olivier Dalle (@unice.fr)

Université de Nice Sophia Antipolis

On crée une nouvelle application d’example


L’objectif de ce cours est de créer une nouvelle app example

Mise en place de la nouvelle application


Pour commencer, on se prépare à utiliser une nouvelle version de ruby:

$ rvm install 1.9.3
$ rvm use 1.9.3@rails3tutorial2ndEd --create --default
$ gem update --system 1.8.24
$ gedit ~/.gemrc
(.gemrc) install: --no-rdoc --no-ri
(.gmerc) update: --no-rdoc --no-ri
$ gem install rails -v 3.2.8
$ sudo apt-get install libxslt-dev libxml2-dev libsqlite3-dev
$ sudo apt-get install nodejs
$ gem install execjs

Création de la nouvelle App


$ cd ~/rails_projects
$ rails new example_app --skip-test-unit
$ cd example_app

Modification du Gemfile:

source 'https://rubygems.org'

gem 'rails', '3.2.8'

group :development do
  gem 'sqlite3', '1.3.5'
  gem 'rspec-rails', '2.11.0'
end

group :assets do
  gem 'sass-rails',   '3.2.3'
  gem 'coffee-rails', '3.2.2'
  gem 'uglifier', '1.2.3'
end
gem 'jquery-rails', '2.0.2'

group :test do
  gem 'capybara', '1.1.2'
end

group :production do
  gem 'pg', '0.12.2'
end 

Installation postgres


Base de donnée sqlite n’est plus supportée par Heroku…

Installation postgres


Base de donnée sqlite n’est plus supportée par Heroku…

Donc passage a Postgres…

Installation postgres


Base de donnée sqlite n’est plus supportée par Heroku…

Donc passage a Postgres…

Mais Postgres est compliquée à administrer, donc on garde sqlite pour les développements!

Installation postgres


Base de donnée sqlite n’est plus supportée par Heroku…

Donc passage a Postgres…

Mais Postgres est compliquée à administrer, donc on garde sqlite pour les développements!

group :development do
  gem 'sqlite3', '1.3.5'
  gem 'rspec-rails', '2.11.0'
end
group :production do
  gem 'pg', '0.12.2'
end

Installation Gems avec bundler


Avant installation avec bundle, il faut installer les dépendances de postgres:

$ sudo apt-get install postgresql-server-dev-8.4

Installation Gems avec bundler


Avant installation avec bundle, il faut installer les dépendances de postgres:

$ sudo apt-get install postgresql-server-dev-8.4

Puis on utilise la commande bundle, mais seulement pour le développement!

$ bundle install --without production

Configuration de Git


$ git init

Puis changer le contenu de .gitignore:

# Ignore bundler config
/.bundle

# Ignore the default SQLite database.
/db/*.sqlite3

# Ignore all logfiles and tempfiles.
/log/*.log
/tmp
# Ignore other unneeded files.
doc/
*.swp
*~
.project
.DS_Store
.idea

Configuration de GitHub sur linux


Si vous n’avez pas encore généré votre paire de clefs publiques/privées:

 $ ssh-keygen -t rsa -C "olivier.dalle@unice.fr"

  1. Saisissez une passphrase vide
  2. Copiez la clef dans le tampon d’édition:
     $ cat ~/.ssh/id_rsa.pub 

Configuration de GitHub sur linux


  1. Saisissez une passphrase vide.
  2. Copiez la clef dans le tampon d’édition:
  3. Rendez-vous sur votre page profile sur GitHub
    1. Allez dans le menu ssh-keys
    2. CLiquez sur ajouter une clef
    3. Collez la clef dans la zone prévue et sauvez

Mise en ligne sur GitHub


  1. Créer le projet example_app.git sur le site de GitHub…
  2. Lancer les commandes suivantes:
    $ git add .
    $ git commit -m "Initial import"
    $ git remote add origin git@github.com:<username>/first_app.git
    $ git push -u origin master

Déploiement sur Heroku


La première fois, après ouverture du compte sur Heroku, installer la trousse à outils:

wget -qO- https://toolbelt.heroku.com/install.sh | sh

Déploiement sur Heroku


La première fois, après ouverture du compte sur Heroku, installer la trousse à outils:

wget -qO- https://toolbelt.heroku.com/install.sh | sh

Puis créer un projet et deployer:

$ heroku create --stack cedar
$ git push heroku master
$ heroku run rake db:migrate

Initialisation des tests


Génération d’un squelette:

$ rails generate rspec:install

Initialisation des tests


Génération d’un squelette:

$ rails generate rspec:install

Puis m-à-j Git, dépôt GitHub et déploiement:

$ git add .
$ git commit -m "Added rspec support"
$ git push origin master
$ git push heroku master

Pages essentiellement statiques


On recommence l’application example sans scaffold pour bien comprendre.

Première étape: les pages statiques:

En parallèle, on va commencer à utiliser les tests automatisés:

Les pages statiques dans Rails


Deux méthodes:

  1. Des vraies pages HTML
  2. Des vues (views) contenant du HTML natif
    1. Mise en page finale par rails lors du chargement de la page
    2. Mais code HTML statique (sans instructions autres que HTML)

Où regarder?


Pour les pages statiques, ca se passe:

Les vraies pages HTML statiques


Un exemple: la page d’accueil par défaut se trouve …

Les vraies pages HTML statiques


Un exemple: la page d’accueil par défaut se trouve …

Dans public/index.html.

Page complètement statique, contenant son code de style (CSS), donc totalement self-contained.

Créons notre propre page statique


$ gedit public/hello.html

Et ajoutons le code suivant:

hello.html

Chargeons notre page statique


$ rails server

Puis ouvrir la page http://localhost:3000/hello.html

hello.html

Pages statiques de type action


On commence par créer une nouvelle branche avec git:

$ git checkout -b static-pages

Pages statiques de type action


La page est associée à une action dans un contrôleur.

Utilisons cette technique pour créer une novelle page d’accueil:

$ rails generate controller StaticPages home help \
> --no-test-framework
      create  app/controllers/static_pages_controller.rb
       route  get "static_pages/help"
       route  get "static_pages/home"
      invoke  erb
      create    app/views/static_pages
      create    app/views/static_pages/home.html.erb
      create    app/views/static_pages/help.html.erb
      invoke  helper
      create    app/helpers/static_pages_helper.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/static_pages.js.coffee
      invoke    scss
      create      app/assets/stylesheets/static_pages.css.scss

Pages statiques de type action


$ rails generate controller StaticPages home help...

Routes vers les pages statiques de type action


$ rails generate controller StaticPages home help...
create  app/controllers/static_pages_controller.rb
 route  get "static_pages/help"
 route  get "static_pages/home"
...

Les routes ont automatiquement été créées dans config/routes.rb:

config/routes.rb

Routes vers les pages statiques de type action


Les routes ont automatiquement été créées dans config/routes.rb:

config/routes.rb

static_pages/home.thml

Contrôleur de pages statiques


Regardons le contenu du nouveau contrôleur en

app/controllers/static_pages_controller.rb

class StaticPagesController < ApplicationController

  def home
  end

  def help
  end
end 

Contrôleur de pages statiques


Regardons le contenu du nouveau contrôleur en

app/controllers/static_pages_controller.rb

Actions vides??

Contrôleur de pages statiques


Regardons le contenu du nouveau contrôleur en

app/controllers/static_pages_controller.rb

Actions vides??

Rails = actions + vues

Ici: action vide + vue (non vide)

Vues des pages statiques


Les vues sont dans app/views/static_pages/:

Exemple: app/views/static_pages/home.html.erb

<h1>StaticPages#home</h1>
<p>Find me in app/views/static_pages/home.html.erb</p> 

Bien-sûr, ces vues ne sont que des points de départ…

Pensez-y


De temps en temps, il faut sauvegarder:

$ git add .
$ git commit -m "Ajout d'un comtroleur de pages statiques"

Premiers tests


On utilise une variant du développement dirigé par les test (TDD) appelée Behavior-driven development (BDD) : développement dirigé par le comportement.

Premiers tests


On utilise une variant du développement dirigé par les test (TDD) appelée Behavior-driven development (BDD) : développement dirigé par le comportement.

Outils principaux:

Tests d’integration


Appelés Request specs dans le framework Rspec:

Tests d’integration


Appelés Request specs dans le framework Rspec:

Tests d’integration


Appelés Request specs dans le framework Rspec:

Tests d’integration


Appelés Request specs dans le framework Rspec:

Tests d’integration


Appelés Request specs dans le framework Rspec:

Tests d’integration


Appelés Request specs dans le framework Rspec:

Nous utilisons Capybara (concurrent de Cucumber)

Principe fondamentaux des tests


Ecrire les tests d’abord !

Principe fondamentaux des tests


Ecrire les tests d’abord !

Et le code à tester après.

Pourquoi??

Principe fondamentaux des tests


Ecrire les tests d’abord !

Et le code à tester après.

Pourquoi?? Deux résultats possibles:

Principe fondamentaux des tests


  1. On commence par écrire un test qui échoue pour être sûr qu’il marche (c-a-d qu’il sait échouer)

Principe fondamentaux des tests


  1. On commence par écrire un test qui échoue pour être sûr qu’il marche (c-a-d qu’il sait échouer)
  2. On écrit ensuite le code de facon à faire réussir le test

Principe fondamentaux des tests


  1. On commence par écrire un test qui échoue pour être sûr qu’il marche (c-a-d qu’il sait échouer)
  2. On écrit ensuite le code de facon à faire réussir le test

Ca n’empêche pas d’expérimenter d’abord sans tests…

Principe fondamentaux des tests


  1. On commence par écrire un test qui échoue pour être sûr qu’il marche (c-a-d qu’il sait échouer)
  2. On écrit ensuite le code de facon à faire réussir le test

Ca n’empêche pas d’expérimenter d’abord sans tests…

On écrit les tests pour la version de production.

Le cycle rouge, vert, re-usinage


  1. Rouge = test qui échoue → écrit avant le code applicatif
  2. Vert = test qui passe → on écrit un premier jet pour que le test réussisse
  3. Ré-usinage (refactoring) → on améliore le code

Que va-t-on tester?

Que va-t-on tester?


Dépend de ce que l’on souhaite développer:

Que va-t-on tester?


Dépend de ce que l’on souhaite développer:

Une page d’accueil:

Que va-t-on tester?


Dépend de ce que l’on souhaite développer:

Une page d’accueil:

→ Test d’intégration pour page statique

Génération d’un test d’intégration


$ rails generate integration_test static_pages
      invoke  rspec
      create    spec/requests/static_pages_spec.rb

crée spec/requests/static_pages_spec.rb

Edition du test d’intégration


Le code généré par défaut n’est pas très utile (test le page d’index du controller qui n’existe pas de toute facon…)

On remplace avec le code suivant:

require 'spec_helper'
describe "Static pages" do
  describe "Home page" do
    it "should have the content 'Example App'" do
      visit '/static_pages/home'
      page.should have_content('Example App')
    end
  end
end

Expliquons un peu le test


  describe "Home page" do
    it "should have the content 'Example App'" do
      visit '/static_pages/home'
      page.should have_content('Example App')
    end
  end

On lance le test…


On exécute avec bundle pour être sûr de respecter la configuration du Gemfile:

$ bundle exec rspec spec/requests/static_pages_spec.rb

On lance le test… ROUGE.

On change le code pour passer le test


Le test exige une page “home” avec le contenu “Example App”.

Soit, éditons le fichier app/views/static_pages/home.html.erb:

<h1>Example App</h1>
   <p> This is the home page for the
	  <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
	  sample application.
   </p>

On change le code pour passer le test


Le test exige une page “home” avec le contenu “Example App”.

Soit, éditons le fichier app/views/static_pages/home.html.erb.

Résultat:

On lance le test …


On exécute avec bundle pour être sûr de respecter la configuration du Gemfile:

$ bundle exec rspec spec/requests/static_pages_spec.rb

On lance le test… VERT.


On exécute avec bundle pour être sûr de respecter la configuration du Gemfile:

$ bundle exec rspec spec/requests/static_pages_spec.rb

On répète avec la page d’aide


  1. On écrit le test pour une page d’aide

On répète avec la page d’aide


  1. On écrit le test pour une page d’aide
describe "Help page" do
  it "should have the content 'Help'" do
    visit '/static_pages/help'
    page.should have_content('Help')
  end
end

On répète avec la page d’aide


  1. On écrit le test pour une page d’aide: ROUGE

On répète avec la page d’aide


  1. On écrit le test pour une page d’aide: ROUGE
  2. On écrit/change le code pour que le test passe

On répète avec la page d’aide


  1. On écrit le test pour une page d’aide: ROUGE
  2. On écrit/change le code pour que le test passe
<h1>Help</h1>
<p>
	Get help on the Ruby on Rails Tutorial at the
	<a href="http://railstutorial.org/help">Rails Tutorial help page</a>.
	To get help on this sample app, see the
	<a href="http://railstutorial.org/book">Rails Tutorial book</a>.
</p>

On répète avec la page d’aide


  1. On écrit le test pour une page d’aide: ROUGE
  2. On écrit/change le code pour que le test passe: VERT

Compliquons un peu…


Exercice: écrire une nouvelle page “A propos (about)” en utilisant approche TDD:

Compliquons un peu…


Exercice: écrire une nouvelle page “A propos (about)” en utilisant approche TDD:

  1. Ecrire le test (rouge)

Compliquons un peu…


Exercice: écrire une nouvelle page “A propos (about)” en utilisant approche TDD:

  1. Ecrire le test (rouge)
  2. Créer la page

Compliquons un peu…


Exercice: écrire une nouvelle page “A propos (about)” en utilisant approche TDD:

  1. Ecrire le test (rouge)
  2. Créer la page
    • le code html (template app/views/static_pages/about.html.erb)

Compliquons un peu…


Exercice: écrire une nouvelle page “A propos (about)” en utilisant approche TDD:

  1. Ecrire le test (rouge)
  2. Créer la page
    • le code html (template app/views/static_pages/about.html.erb) → ROUGE

Compliquons un peu…


Exercice: écrire une nouvelle page “A propos (about)” en utilisant approche TDD:

  1. Ecrire le test (rouge)
  2. Créer la page
    • le code html (template app/views/static_pages/about.html.erb)
    • sans oublier d’ajouter la route !

Compliquons un peu…


Exercice: écrire une nouvelle page “A propos (about)” en utilisant approche TDD:

  1. Ecrire le test (rouge)
  2. Créer la page
    • le code html (template app/views/static_pages/about.html.erb)
    • sans oublier d’ajouter la route ! → ROUGE

Compliquons un peu…


Exercice: écrire une nouvelle page “A propos (about)” en utilisant approche TDD:

  1. Ecrire le test (rouge)
  2. Créer la page
    • le code html (template app/views/static_pages/about.html.erb)
    • sans oublier d’ajouter la route !
    • sans oublier l’action dans le contrôleur!

Compliquons un peu…


Exercice: écrire une nouvelle page “A propos (about)” en utilisant approche TDD:

  1. Ecrire le test (rouge)
  2. Créer la page
    • le code html (template app/views/static_pages/about.html.erb)
    • sans oublier d’ajouter la route !
    • sans oublier l’action dans le contrôleur! → VERT

Compliquons un peu…


Exercice: écrire une nouvelle page “A propos (about)” en utilisant approche TDD:

  1. Ecrire le test (rouge)
  2. Créer la page
  3. Ré-usinage: Construire une page avec un titre construit selon un modèle commun

Compliquons un peu: Ré-usinage


Titre avec partie fixe + partie variable

Exemple: /static_pages/home doit avoir le titre:

"Ruby on Rails Tutorial Example App | Home"

Ecrivons un nouveau test


Pour tester le titre de la page home:

it "should have the right title" do
  visit '/static_pages/home'
  page.should have_selector('title',
       :text => "Ruby on Rails Tutorial Example App | Home")
end

Le test complet (pour Home)


require 'spec_helper'

describe "Static pages" do

  describe "Home page" do

    it "should have the h1 'Example App'" do
      visit '/static_pages/home'
      page.should have_selector('h1', :text => 'Example App')
    end

    it "should have the title 'Home'" do
      visit '/static_pages/home'
      page.should have_selector('title',
          :text => "Ruby on Rails Tutorial Example App | Home")
    end
  end

  ... (idem pour pages help et about )

Passer le test du nouveau titre


Ajouter une section head:

<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Example App | Home</title>
  </head>
  <body>
    <h1>Example App</h1>
    <p>
      This is the home page for the
      <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
      sample application.
    </p>
  </body>
</html>	

Optimisons un peu avec ruby embarqué


Approche “DRY” : Don’t Repeat Yourself !

On veut éviter les répétition = factoriser les parties communes.

On change le code de app/views/static_pages/home.html.erb

Zoom sur le code ERB


Optimisons un peu avec ruby embarqué


Les seules parties variables sont:

→ On déplace la partie qui se répète dans application.html.erb

Format de mise en page commun pour toutes les pages

Le modèle de page

Le modèle de page


yield correspond a du code inséré

NB: csrf = cross-site request forgery : protection contre une attaque malicieuse…

La nouvelle page optimisée


Idem pour pages “Help” et “About”…

Retour à: Compliquons un peu…


  1. Ecrire le test (rouge)
  2. Créer la page
  3. Ré-usinage: Construire une page avec un titre construit selon un modèle commun

Vérifier que le test est toujours VERT!