{"id":19445702,"url":"https://github.com/quarkgluant/day07_piscine_ruby_42","last_synced_at":"2026-05-09T07:40:07.058Z","repository":{"id":40008959,"uuid":"212593439","full_name":"quarkgluant/day07_piscine_Ruby_42","owner":"quarkgluant","description":"le jour 07 de la piscine Ruby de 42 refait/Day07 of the 42 Ruby on rails swimming pool","archived":false,"fork":false,"pushed_at":"2023-01-19T10:19:25.000Z","size":1641,"stargazers_count":0,"open_issues_count":51,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-07T22:51:45.020Z","etag":null,"topics":["42","42born2code","piscine-ruby","ruby","ruby-on-rails"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/quarkgluant.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-03T13:55:36.000Z","updated_at":"2022-02-14T07:55:39.000Z","dependencies_parsed_at":"2023-01-28T23:31:38.738Z","dependency_job_id":null,"html_url":"https://github.com/quarkgluant/day07_piscine_Ruby_42","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quarkgluant%2Fday07_piscine_Ruby_42","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quarkgluant%2Fday07_piscine_Ruby_42/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quarkgluant%2Fday07_piscine_Ruby_42/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quarkgluant%2Fday07_piscine_Ruby_42/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quarkgluant","download_url":"https://codeload.github.com/quarkgluant/day07_piscine_Ruby_42/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240630575,"owners_count":19832088,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["42","42born2code","piscine-ruby","ruby","ruby-on-rails"],"created_at":"2024-11-10T16:11:32.219Z","updated_at":"2026-05-09T07:40:02.028Z","avatar_url":"https://github.com/quarkgluant.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# day07 de la piscine Ruby/Rails de 42 Paris\nle jour 07 de la piscine Ruby de 42 refait/day06 of the 42 swimming pool Ruby/rails  \n  \n## Salut à toi duoquadratain qui se retrouve tout seul à faire la piscine Ruby !  \n  \n  \n### Tes meilleurs amis  \n* les [railsguides](https://guides.rubyonrails.org/) pour des explications d'ensemble et la doc de l'[API](https://api.rubyonrails.org/) pour de la doc sur une méthode précise.  \n  \nMais aussi:  \n* Une très bonne adresse en français [grafikart](https://www.grafikart.fr/tutoriels/ruby-on-rails)  \n* L'excellent [Rails tutorial](https://www.railstutorial.org/book) de Michael Hartl  \n* [goRails](https://gorails.com/series), d'autant qu'avec le GitHub Student Pack, on bénéficie d'un abonnement d'un an gratos  \n* Les vidéos de [RailsCast](http://railscasts.com/)  \n* Et une toute nouvelle ressource (doc + code), très complète, [RubyGarage](https://rubygarage.github.io/)  \n* ton cerveau, il peut servir...    \n  \n### Avant de coder, installons !  \nAvant toute chose et tout code, il vous faut utiliser un gestionnaire de version de Ruby (et donc de Rails) [RVM](https://rvm.io/)\n et [rbenv](https://github.com/rbenv/rbenv#readme) étant les plus connus et utilisés, mais existe aussi chruby, uru, etc.  \n  \nAvec RVM, on installe la version de Ruby désirée, en regardant sur [site officel de Ruby](https://www.ruby-lang.org/en/downloads/releases/) \nles versions de Ruby (`rvm install 2.6.5` par exemple), créer un gemset (un environnement isolé) avec `rvm gemset create \nnom_du_gemset`, je nomme en général mes gemsets avec la version de Ruby@rails_version, par ex `rvm gemset create rails427`.  \nUne fois le gemset créé, on rentre dedans/l'utilise avec `rvm 2.6.5@rails4.2.7` dans cet exemple et ensuite, seulement on \ninstalle rails avec la bonne version avec `gem install rails  -v 4.2.7 -no-ri -no-rdoc` sans la doc incluse \n\n\noutre le code de ce repo voici quelques pistes pour t'aider:  \n\n## ex00  \n\nExtrait de l'excellent ebook de Xavier Nayrac **Créer votre framework web en Ruby**\nà acheter sur [leanpub](https://leanpub.com)  \n  \n\u003e## 15 - La base de données Postgresql\n\u003e\n\u003eAvant de pouvoir déployer une application réalisée à l’aide de notre framework sur Heroku, nous devons apprendre à utiliser\n\u003e la base de données Postgresql. Pour la bonne raison qu’Heroku ne gère pas Sqlite.\n\u003e\n\u003e### Installation\n\u003ePour l’installation sur Windows, téléchargez ce dont vous avez besoin sur le site web de Postgresql. Sur ce même site \n\u003eil y a une section consacrée à OS X. Sur Linux vous devriez pouvoir utiliser votre gestionnaire de paquets, par exemple \n\u003esudo apt-get install postgresql, ou vous pouvez là aussi consulter le site web de Postgresql, section Linux.\n\u003e\n\u003e### Configuration\n\u003eLa base de données Postgresql est pré configurée avec un utilisateur répondant au nom de postgres. Vous allez ajouter\n\u003e un nouvel utilisateur nommé framework avec lequel vous pourrez pratiquer tous les tests qu’ils vous plaira. Le programme\n\u003e en ligne de commande que nous allons utiliser pour gérer Postgresql est psql. Vous verrez qu’il ressemble assez à sqlite3,\n\u003e vous ne serez pas perdu. Tout d’abord vous allez empruntez l’identité de l’utilisateur postgres pour vous connecter.\n\u003e\n\u003eConnectez vous comme si vous étiez postgres  \n\u003e```bash\n\u003e    $ sudo -u postgres psql  \n\u003e    psql (9.5.6)  \n\u003e    Type \"help\" for help.  \n\u003e    postgres=# \n\u003e```\n\u003eLa commande `sudo -u nom commande` permet de lancer le programme commande comme si vous étiez l’utilisateur nom.\n\u003e\n\u003eUne fois connecté, vous pouvez créer un nouvel utilisateur. Ici je crée un utilisateur ayant pour nom framework et pour \n\u003emot de passe bonjour :\n\u003e\n\u003e### Création d’un utilisateur  \n\u003e```postgresql\n\u003e    postgres=# create user framework password 'bonjour';\n\u003e```\n\u003e\n\u003eMaintenant que l’utilisateur est créé, nous allons lui fournir des droits. Et même \n\u003ebeaucoup de droit pour être tranquille pour la suite. Nous ne sommes pas entrain \n\u003ed’administrer des serveurs d’entreprises où il faut rester prudent sur qui peut \n\u003efaire quoi. Nous sommes entrain de configurer une machine de développement pour \n\u003etester une nouvelle base de données. Saisissez donc la commande suivante telle \n\u003equelle pour donner un maximum de droits à notre utilisateur :\n\u003e\n\u003eBeaucoup de droits:  \n\u003e```postgresql\n\u003epostgres=# alter user framework with superuser;  \n\u003e```\n\u003e### Création de la base de données  \n\u003eAvec Sqlite il fallait simplement créer un nouveau fichier pour obtenir une nouvelle base de données. Avec Postgresql \n\u003enous devons utiliser le langage SQL. Nous créons une base de donnée nommée framework_blog et nous spécifions qu’elle \n\u003eappartient à l’utilisateur framework.  \n\u003e\n\u003e#### Création d’une base de données  \n\u003e```postgresql\n\u003epostgres=# create database framework_blog owner framework;\n\u003e```\n\u003eVous pouvez lister toutes les bases de données avec la commande \\l. Vous devriez voir apparaitre celle que vous venez de \n\u003ecréer à l’instant. (J’ai supprimé plusieurs lignes et colonnes dans la sortie suivant pour qu’elle tienne sur une page)\n\u003e\n\u003eLister les bases  \n\u003e```postgresql\n\u003epostgres=# \\l\n\u003e                          List of databases  \n\u003e     Name       |    Owner  | Encoding |      Access privileges      \n\u003e----------------+-----------+----------+-------------------------  \n\u003e xxx            | xavier    | UTF8     |                           \n\u003e xxx            | xavier    | UTF8     |                           \n\u003e ...  \n\u003e framework_blog | framework | UTF8     | =Tc/framework          +  \n\u003e                |           |          | framework=CTc/framework  \n\u003e```  \n\u003e\n\u003eAvant de poursuivre nous devons nous connecter à la base de données framework_blog. Pour ce faire nous utilisons la \n\u003ecommande `\\c nom_de_la_base`  \n\u003e\n\u003e### Connexion à la base framework_blog  \n\u003e```postgresql\n\u003e postgres=# \\c framework_blog \n\u003e You are now connected to database \"framework_blog\" as user \"postgres\".\n\u003e framework_blog=#  \n\u003e```\n\u003e Vous remarquerez comme le prompt a changé. À tout moment vous pouvez demander à quelle base vous êtes connecté et avec\n\u003e quel utilisateur en utilisant la commande `\\c` sans arguments :\n\u003e \n\u003e Voir la connexion actuelle\n\u003e```postgresql\n\u003e framework_blog=# \\c  \n\u003e You are now connected to database \"framework_blog\" as user \"postgres\".  \n\u003e ```  \n\u003e### Création de la table posts  \n\u003e\n\u003e Contrairement à Sqlite, Postgresql possède une foultitude de type de données. Vous les trouverez sur le site de Postgresql. \nPour la colonne id nous utiliserons le type serial et pour la date nous pourrons utiliser le type timestamp. Quant aux colonnes\n title et content elles restent inchangées. Saisissez la commande suivante dans psql.  \n\u003e \n\u003e### Création d’une table  \n\u003e```postgresql\n\u003e     create table posts(\n\u003e        id serial primary key,\n\u003e        title text,\n\u003e        content text,\n\u003e        date timestamp\n\u003e    );  \n \u003e```\n\u003e Le type serial n’est pas vraiment un type. C’est plutôt un raccourci pour dire qu’on veut un entier qui soit auto incrémenté.\n\u003e \n\u003e Vous pouvez maintenant créer deux ou trois posts. Pour entrer la date courante vous avez deux possibilités, soit now(),\n\u003e soit current_timestamp. Voici un exemple de chaque :\n\u003e \n\u003e### Création de deux posts  \n\u003e```postgresql\n\u003e     insert into posts(title, content, date)\n\u003e      values('Bonjour !', 'Premier post avec Postgresql', now());\n\u003e     \n\u003e     insert into posts(title, content, date)\n\u003e      values('Hello !', 'In english this time', current_timestamp);  \n\u003e```  \n\u003e Vous pouvez bien sûr afficher le contenu de la table posts pour vous assurer que tout s’est bien déroulé.\n\u003e \n\u003e Afficher les posts dans Postgresql\n\u003e```postgresql\n\u003e framework_blog=# select * from posts;\n\u003e  id |   title   |           content            |            date            \n\u003e ----+-----------+------------------------------+----------------------------\n\u003e   1 | Bonjour ! | Premier post avec Postgresql | 2017-04-12 10:18:09.367729\n\u003e   2 | Hello !   | In english this time         | 2017-04-12 10:19:09.474741\n\u003e (2 rows)  \n\u003e```  \n\u003e Finalement vous pouvez quitter psql avec le raccourci Ctrl+d, ou bien en saisissant la commande `\\q`.\n\u003e \n\u003e### Utiliser Postgresql dans notre framework  \n\u003e Pour être capable d’utiliser Postgresql avec notre framework nous devons ajouter la gem pg dans le Gemfile, et retirer \n\u003ela gem sqlite3. Votre Gemfile devrait ressembler à celui-ci :\n\u003e \n\u003e#### Ajouter la gem pg au Gemfile  \n\u003e\n\u003e```ruby\n\u003e\n\u003e 1 source \"https://rubygems.org/\"\n\u003e 2 \n\u003e 3 gem 'rack', '~\u003e 2.0.1'\n\u003e 4 gem 'puma'\n\u003e 5 gem 'sequel'\n\u003e 6 gem 'pg'\n\u003e```\n\u003e N’oubliez pas de faire un bundle…\n\u003e \n\u003e Ça faisait longtemps qu’on avait pas fait un bundle…\n\u003e```bash\n\u003e $ bundle\n\u003e Resolving dependencies...\n\u003e Using pg 0.19.0\n\u003e Using puma 3.7.1\n\u003e Using rack 2.0.1\n\u003e Using sequel 4.43.0\n\u003e Using bundler 1.13.7\n\u003e``` \n\u003e La chaîne de connexion dont a besoin Sequel pour se connecter à une base de données Postgresql est de la forme suivante :\n\u003e  \n\u003e `postgres://utilisateur:mot_de_passe@serveur:port/nom_de_la_base`  \n  \nautre doc pour la configuration sur Linux ([fedora](https://doc.fedora-fr.org/wiki/Installation_et_configuration_de_PostgreSQL))\n\n\u003e###2 Installation de PostgreSQL\n\u003e#### 2.1 Installation\n\u003e L'installation du serveur de base de données se fera par dnf, le gestionnaire de paquets de Fedora. Voici la commande \n\u003eà taper dans un terminal :\n\u003e```bash\n\u003e# dnf install postgresql-server\n\u003e``` \n\u003e   \n\u003e#### 2.2 Activation  \n\u003e Pour utiliser PostgreSQL, il vous faut avant tout faire une initialisation de la base de données, pour cela tapez cette \n\u003ecommande :\n\u003e```bash\n\u003e# postgresql-setup initdb\n\u003e``` \n\u003e   \n\u003e Si tout se passe bien vous avez pour réponse :\n\u003e   \n\u003e base de données : [ ok ]  \n\u003e Ensuite, il est nécessaire de démarrer le service :\n\u003e```bash\n\u003e# systemctl start postgresql.service\n\u003e``` \n\u003e \n\u003e Afin de s'assurer que le service est bien démarré sans erreur, son état peut être consulté :\n\u003e```bash\n\u003e# systemctl status postgresql.service\n\u003e``` \n\u003e \n\u003e Si vous souhaitez que son lancement soit automatique lors du démarrage de la machine :\n\u003e```bash\n\u003e# systemctl enable postgresql.service\n\u003e``` \n\u003e  \n\u003e#### 2.3 L'utilisateur postgres  \n\u003e\u003e#####    L'utilisateur postgres  \n\u003e\u003e Dès le départ, PostgreSQL crée un utilisateur nommé postgres.\n\u003e\u003e Cet utilisateur est le super-utilisateur (l'équivalent de root sur votre système) du serveur de base de donnée.\n\u003e \n\u003e Par mesure de sécurité, il est impératif d'affecter un mot de passe chiffré à cet utilisateur :\n\u003e```bash\n\u003e# su - postgres\n\u003e``` \n\u003e \n\u003e \n\u003e Désormais vous êtes en tant qu'utilisateur postgres, ensuite tapez :  \n\u003e```bash\n\u003e$ psql\n\u003e```\n\u003e Pour résultat vous devriez avoir ceci :\n\u003e```postgresql\n\u003e could not change directory to \"/root\"\n\u003e Welcome to psql 10.6.1, the PostgreSQL interactive terminal.\n\u003e Type:  \\copyright for distribution terms\n\u003e        \\h for help with SQL commands\n\u003e        \\? for help with psql commands\n\u003e        \\g or terminate with semicolon to execute query\n\u003e        \\q to quit\n\u003e postgres=# \n\u003e``` \n\u003e \n\u003e \n\u003e\u003eA noter\n\u003e\u003e Le # indique bien que vous utilisez un super-utilisateur du serveur de base de donnée PostgreSQL.  \n\u003e\n\u003e C'est le moment de lui attribuer un mot de passe, \u003cmot_de_passe\u003e est à remplacer par le mot passe de votre choix, pour\n\u003e cela tapez :  \n\u003e```postgresql\n\u003e# ALTER USER postgres WITH PASSWORD '\u003cmot_de_passe\u003e';\n\u003e``` \n\u003e  \n\u003e#### 2.4 Sécurisation\n\u003e La sécurité, lorsque l'on a une base de données sur le réseau, c'est un point crucial.\n\u003e En effet, il est tout à fait possible de chiffrer les mots de passes des utilisateurs du serveur de base de données.\n\u003e \n\u003e Pour cela, il faut éditer le fichier `/var/lib/pgsql/pg_hba.conf` en tant qu'utilisateur root et modifier le fichier en\n\u003e remplaçant `ident sameuser` par `md5` afin d'obtenir les lignes suivantes :\n\u003e \n\u003e\n\u003e```editorconfig\n\u003e # \"local\" is for Unix domain socket connections only\n\u003e local   all         all                               md5\n\u003e # IPv4 local connections:\n\u003e host    all         all         127.0.0.1/32          md5\n\u003e # IPv6 local connections:\n\u003e host    all         all         ::1/128               md5\n\u003e```\n\u003e Pour que les modifications prennent effet il faut redémarrer le serveur :\n\u003e  \n\u003e```bash\n\u003e# systemctl restart postgresql.service\n\u003e``` \n\u003e \n\u003e De plus amples informations sur la sécurisation d'une base de données PostgreSQL sont disponibles dans le documentation\n\u003e du serveur.\n\u003e \n\u003e#### 2.5 Création d'un utilisateur\n\u003e##### 2.5.1 À la main  \n\u003e Il est recommandé de ne pas utiliser le super utilisateur postgres pour lire et écrire dans vos base de données.\n\u003e Pour cela, il est intéressant de créer un utilisateur dédié :\n\u003e```bash\n\u003e # su - postgres\n\u003e $ psql\n\u003e``` \n\u003e \n\u003e\n\u003e Pour créer l'utilisateur, tapez (\u003cmon_user\u003e est à changer par un nom d'utilisateur de votre choix) :\n\u003e```postgresql\n\u003e# CREATE USER \u003cmon_user\u003e;\n\u003e``` \n\u003e \n\u003e \n\u003e Pour lui donner le droit de créer des bases de données, tapez ceci (\u003cmon_user\u003e est à changer par le nom d'utilisateur \n\u003echoisi au préalable) :\n\u003e```postgresql\n\u003e# ALTER ROLE \u003cmon_user\u003e WITH CREATEDB;\n\u003e``` \n\u003e \n\u003e \n\u003e Il est vivement conseillé d'attribuer un mot de passe pour votre nouvel utilisateur (\u003cmon_user\u003e est à changer par le nom \n\u003ed'utilisateur choisi au préalable, \u003cpassword\u003e est à changer par le mot de passe correspondant) :\n\u003e```postgresql\n\u003e # ALTER USER \u003cmon_user\u003e WITH ENCRYPTED PASSWORD '\u003cpassword\u003e';\n\u003e``` \n\u003e \n\u003e\n\u003e Ensuite, créez la base de donnée de l'utilisateur (\u003cmon_user\u003e est à changer par le nom d'utilisateur choisi au préalable) :\n\u003e```postgresql\n\u003e# CREATE DATABASE \u003cmon_user\u003e;\n\u003e``` \n\u003e\u003eUtilisateur virtuel éponyme\n\u003e\u003e Si l'utilisateur virtuel que vous venez de créer porte le même nom que votre utilisateur système, il est possible \n\u003e\u003ed'utiliser l'interface psql depuis celui-ci.\n\u003e\u003e Remarquez tout de même le changement du signe de l'invite de commande qui devient alors \u003e, cela signifie bien que vous\n\u003e\u003e n'êtes plus en utilisateur root.\n  \n  \nAprès ces extraits de doc, voici pour Mac  \n```bash\nbrew update\nbrew install postgresql\n#==\u003e Caveats\n#If builds of PostgreSQL 9 are failing and you have version 8.x installed,\n#you may need to remove the previous version first. See:\n#  https://github.com/Homebrew/homebrew/issues/2510\n#\n#To migrate existing data from a previous major version (pre-9.0) of PostgreSQL, see:\n#  https://www.postgresql.org/docs/9.6/static/upgrading.html\n#\n#To migrate existing data from a previous minor version (9.0-9.5) of PostgreSQL, see:\n#  https://www.postgresql.org/docs/9.6/static/pgupgrade.html\n\n#  You will need your previous PostgreSQL installation from brew to perform `pg_upgrade`.\n#  Do not run `brew cleanup postgresql` until you have performed the migration.\n#\n#To have launchd start postgresql now and restart at login:\n#  brew services start postgresql\n#Or, if you don't want/need a background service you can just run:\n#  pg_ctl -D /Users/pcadiot/.brew/var/postgres start\npg_ctl -D /Users/pcadiot/.brew/var/postgres start\ncd ex00\nrvm 2.3.4\nrvm gemset create rails4.2.7\nrvm 2.3.4@rails4.2.7\ngem install rails -v 4.2.7\nbundle update\n# createdb d07\nrails new acme -d postgresql\ncd acme\ncp -f ~/Downloads/gemfile .\nrake db:create  \n```  \n  \nEn cas de problème de connexion, vérifier le fichier pg_hba.conf, en remplaçant sur les lignes `local` et `host` les `ident`\npar `md5` ou mieux `scram-sha-256`  en oubliant pas de relancer postgres après avoir sauvegardé les modifications sur ce \nfichier.  \n\n## ex01  \n  \n  Ha [**Devise**](http://devise.plataformatec.com.br/)\n```bash\nrails g devise:install  # Permet de générer la configuration\nrails g devise:views    # Permet d'importer les vues\nrails g devise User     # Permet de générer le model et les migrations pour le model User\n```  \nAprès la première commande, devise vous affiche ceci:  \n    \n\u003e Some setup you must do manually if you haven't yet:\n\u003e  \n\u003e    1. Ensure you have defined default url options in your environments files. Here\n\u003e       is an example of default_url_options appropriate for a development environment\n\u003e       in config/environments/development.rb:\n\u003e```ruby\n\u003econfig.action_mailer.default_url_options = { host: 'localhost', port: 3000 }\n\u003e```  \n\u003e    In production, :host should be set to the actual host of your application.\n\u003e  \n\u003e    2. Ensure you have defined root_url to *something* in your config/routes.rb.\n\u003e       For example:\n\u003e```ruby\n\u003eroot to: \"home#index\"\n\u003e```  \n\u003e         \n\u003e  \n\u003e    3. Ensure you have flash messages in app/views/layouts/application.html.erb.\n\u003e       For example:\n\u003e  \n\u003e```html+erb\n\u003e\u003cp class=\"notice\"\u003e\u003c%= notice %\u003e\u003c/p\u003e\n\u003e\u003cp class=\"alert\"\u003e\u003c%= alert %\u003e\u003c/p\u003e\n\u003e```         \n\u003e    \n\u003e    4. You can copy Devise views (for customization) to your app by running:\n\u003e```bash\n\u003erails g devise:views\n\u003e```  \n\u003e         \n\u003e  \n\n##### Erreurs possibles  \n- Lors de `rails generate devise:install` cette erreur:\n \u003e`in `\u003ctop (required)\u003e': undefined method `to_time_preserves_timezone=' for ActiveSupport:Module (NoMethodError)`   \n  \n Commentez la ligne 10 dans acme/config/initializers/to_time_preserves_timezone.rb, cette erreur survient si votre version \n de Ruby est inférieure à la 2.4\n\n- Lorsque vous faites votre `rails generate devise user` et malgré la présence dans votre Gemfile de la gem pg\n \u003e`in `rescue in spec': Specified 'postgresql' for database adapter, but the gem is not loaded. Add `gem 'pg'` to your  \n \u003eGemfile (and ensure its version is at the minimum required by ActiveRecord). (Gem::LoadError)`  \n\n rajoutez dans le Gemfile une version compatible, la 0.21 par exemple\n ```ruby\n # extrait du Gemfile\n gem 'pg', '~\u003e 0.21' \n ```\n Puis faite un `bundle update`\n \nPour les validations, vous pouvez rajouter dans le `app\\models\\user.rb`\n```ruby\n  # Include default devise modules. Others available are:\n  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable\n  devise :database_authenticatable, :registerable,\n         :recoverable, :rememberable, :validatable\n\n  validates :password, presence: true, length: { minimum: 6 }, on: :create\n  validates :name, presence: true, on: :create\n  validates :email, presence: true, on: :create\n```\n\nPour les strong_parameters, il faut rajouter dans `application_controller.rb`  (comme indiqué dans la \n[doc de devise](http://devise.plataformatec.com.br/#configuring-controllers) )\n```ruby\n  before_action :configure_permitted_parameters, if: :devise_controller?\n\n  protected\n\n  def configure_permitted_parameters\n    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :email])\n  end\n```\nEt bien-sur il vous faut modifier la migration créée par Devise en rajoutant le nom et la biography  \n```ruby\n# à rajouter dans acme/app/db/migrate/xxxxxxxx_devise_create_user.rb\n      t.string :name, null: false, default: ''\n      t.text :bio, default: '' \n```\net pareil, rajouter ces deux champs dans les vues `views/devise/registrations/new.html.erb` et `edit.html.erb`  \n```html+erb\n  \u003cdiv class=\"field\"\u003e\n    \u003c%= f.label :name %\u003e\u003cbr /\u003e\n    \u003c%= f.text_field :name, autofocus: true, autocomplete: \"name\" %\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"field\"\u003e\n    \u003c%= f.label :email %\u003e\u003cbr /\u003e\n    \u003c%= f.email_field :email, autocomplete: \"email\" %\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"field\"\u003e\n    \u003c%= f.label :biography %\u003e\u003cbr /\u003e\n    \u003c%= f.text_area :bio %\u003e\n  \u003c/div\u003e\n```\nAprès ces opérations, le seed passe et les 2 Users stories sont satisfaites !\n\n## ex02  \n_Pour l'instant (ayant déjà un compte gratuit Cloudinary et ne voulant pas l'épuiser avec les 5000 du seed), je ne travaille\nqu'en local, on verra ça après !_ Mais **promis**, on va le faire !   \nJ'ai fait le choix d'avoir deux uploaders différents, un pour les avatars de Brand, l'autre pour les pict de Product. On \nva demander à Rails de faire (aussi) le café aussi, en scaffoldant comme des bêtes   \n\n```shell script\nrails generate uploader Avatar\nrails generate uploader Pict\nrails generate scaffold Brand name:string avatar:string\nrails generate scaffold Product name:string description:text brand:references pict:string price:decimal\n```\n  \n on monte les uploaders dans les modèles, et on en profite pour écrire les associations\n```ruby\nclass Brand\n  has_many  :products\n  mount_uploader :avatar, AvatarUploader\nend\n```\nComme la maison ne recule devant rien, on va faire des attributs imbriqués, des nested_attributes pour pouvoir éditer, \ndepuis la page edit d'un Product, aussi le nom et l'avatar d'une marque (Brand)   \n```ruby\nclass Product \u003c ActiveRecord::Base\n  belongs_to :brand\n  accepts_nested_attributes_for :brand\n  mount_uploader :pict, PictUploader\nend\n```\nPuisqu'on utilise les `nested_attributes`, il faut modifier les `strong_parameters` dans le contrôleur des produits avec\n`brand_attributes`, et, tant qu'on y est, on rajoute aussi la version cache de pict et d'avatar (qui sert à stocker en \ncache l'image téléchargée en cas de rafraîchissement ou de non validation de la page)    \n```ruby\n  # extrait de acme/app/controllers/products_controller.rb\n  # Never trust parameters from the scary internet, only allow the white list through.\n  def product_params\n    params.require(:product).permit(:name, :description, :brand_id, :pict, :price,\n                                    :brand, # ça je ne sais pas pourquoi rails me l'a demandée\n                                    :pict_cache, #le cache de pict, qui l'eût cru !                              \n                                    brand_attributes: %i[name avatar avatar_cache])\n  end\n```\n_ceci (minimagik et imageMagik) ne concerne que le stockage des images en local_\nPour pouvoir bénéficier de `resize_to_fit` on doit décommenter dans les Uploaders les lignes \n`include CarrierWave::MiniMagick` et `  version :thumb do  \n                                          process resize_to_fit: [50, 50]  \n                                          end`  \nPour que MiniMagik fonctionne, il faut installer `ImageMagik` comme indiqué dans la doc de CarrierWave, avec \n`brew install imagemagick` sur MacOS ou votre gestionnaire de paquets sur Linux (`apt-get install` ou `dnf install` par \nex.) **ET** modifier le Gemfile en rajoutant la gem minimagik  \n```ruby\n# ex01/acme/Gemfile (extrait à  rajouter)\ngem 'mini_magick'\n```                                     \n```ruby\n# ex02/acme/app/uploaders/avatar_uploader.rb\nclass AvatarUploader \u003c CarrierWave::Uploader::Base\n  # Include RMagick or MiniMagick support:\n  # include CarrierWave::RMagick\n  include CarrierWave::MiniMagick\n\n  # Choose what kind of storage to use for this uploader:\n  storage :file\n  # storage :fog\n\n  # Override the directory where uploaded files will be stored.\n  # This is a sensible default for uploaders that are meant to be mounted:\n  def store_dir\n    \"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}\"\n  end\n\n  # J'ai enlevé tout ce qui ne nous concerne pas\n\n  # Create different versions of your uploaded files:\n  version :thumb do\n    process resize_to_fit: [50, 50]\n  end\n\n  # Add a white list of extensions which are allowed to be uploaded.\n  # ...\nend\n```\nPour que le `db/seeds.rb` passe, il faut rajouter un Array nommé price  \n```ruby\n# ex02/acme/db/seeds.rb (extrait)\nprice = [1.29, 4.00, 5.25, 7.75, 9.99, 10.00, 25.5, 49.99, 66.66, 74.99, 99.99]\n50.times do |tm|\n  mk = Brand.create!(name: FFaker::Product.brand,\n                     avatar: open(FFaker::Avatar.image))\n\n  50.times do |tw|\n    Product.create!(name: FFaker::Product.product,\n                    pict: open(FFaker::Avatar.image),\n                    description: FFaker::HipsterIpsum.paragraph,\n                    brand_id: mk.id, price: price.sample)\n  end\nend \n```\n\nDernière modif, pour que l'affichage des Products soit joli, on modifie 2 lignes, l'une pour afficher le nom de la marque\n(Brand) et l'autre pour afficher la photo réduite, la miniature (thumb) de chaque Product  \n```html+erb\n\u003c!-- # ex01/acme/app/views/products.html.erb (extrait) --\u003e\n\u003c!--# avant : --\u003e\n         \u003ctd\u003e\u003c%= product.brand %\u003e\u003c/td\u003e\n         \u003ctd\u003e\u003c%= product.pict %\u003e\u003c/td\u003e\n\u003c!--# après: --\u003e\n        \u003ctd\u003e\u003c%= product.brand.name %\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003c%= image_tag product.pict_url(:thumb) unless product.pict.file.nil? %\u003e\u003c/td\u003e \n```  \n  \n  \n_Maintenant ocuppons-nous de Cloudinary !_  \n  \n  \n1. l'identification  \nchez moi, la méthode avec un fichier YAML, dans `config` n'a jamais voulu fonctionner !\n```yaml\n--- acme/config/cloudinary.yaml\ndevelopment:\n  cloud_name: dl1hwhz99\n  api_key: \u003c%= ENV[\"CLOUDINARY_API_KEY\"] %\u003e\n  api_secret: \u003c%= ENV[\"CLOUDINARY_API_SECRET\"] %\u003e\n  enhance_image_tag: true\n  static_file_support: false\nproduction:\n  cloud_name: dl1hwhz99\n  api_key: \u003c%= ENV[\"CLOUDINARY_API_KEY\"] %\u003e\n  api_secret: \u003c%= ENV[\"CLOUDINARY_API_SECRET\"] %\u003e\n  enhance_image_tag: true\n  static_file_support: true\ntest:\n  cloud_name: dl1hwhz99\n  api_key: \u003c%= ENV[\"CLOUDINARY_API_KEY\"] %\u003e\n  api_secret: \u003c%= ENV[\"CLOUDINARY_API_SECRET\"] %\u003e\n  enhance_image_tag: true\n  static_file_support: false\n```\nEt non, petit malin, je ne vais pas vous montrer mes api_key et api_secret ! J'utilise des variables d'environnement. Pour\nles utiliser, soit vous les mettez dans votre .bashrc ou .zshrc, puis un petit coup de `source ~/.zshrc`\n```shell script\nexport CLOUDINARY_API_SECRET=MaisSiCestMaVeritableCle\nexport CLOUDINARY_API_KEY=12345678913345\n```\nou vous lancez vos commandes ainsi (mais les variables ne seront conservées que durant la session)\n```shell script\nCLOUDINARY_API_SECRET=MaisSiCestMaVeritableCle\nCLOUDINARY_API_KEY=12345678913345\nrake db:migrate\nrails server\n```\nBon, revenons à nos moutons électriques, puisque cette méthode (avec le fichier cloudinary.yml) ne fonctionnait pas, j'ai\nutilisé l'autre façon, en créant un fichier `acme/config/initializer/cloudinary.rb`  \n```ruby\nCloudinary.config do |config|\n  config.cloud_name = 'dl1hwhz99'\n  config.api_key = (ENV['CLOUDINARY_API_KEY']).to_s\n  config.api_secret = (ENV['CLOUDINARY_API_SECRET']).to_s\n  config.enhance_image_tag = true\n  config.secure = true\n  config.cdn_subdomain = true\nend\n```\nSi cette façon fonctionne, on remarquera qu'elle est moins souple quant à la définition de diférents environnements (dev, \nprod, test).  \nPour les utilisateurs de Rubymine, il faut éditer la configuration, dans la **Toolbar**, en principe juste sous la barre\ndes menus, clique sur le petit triangle tête en bas, **Edit Configurations...** et vous aurez alors, dans la boîte de \ndialogues venant de s'ouvrir, une ligne pour les variables d'environnement, avec, tout à fait à droite de cette ligne, une\npetite icône grisée rectangulaire de liste permettant d'entrer les variables sous forem de tableaux  \n\n2. les modifications des uploaders  \n\n\nOn va effacer la grande majorité de ce qu'on avait fait avant, quand on travaillait sans Cloudinary, en local !  \n```ruby\nclass AvatarUploader \u003c CarrierWave::Uploader::Base\n  include Cloudinary::CarrierWave\n\n  def public_id\n    return 'acme/' + model.name\n  end\n\n  # Create different versions of your uploaded files:\n  version :thumb do\n    eager\n    process resize_to_fit: [50, 50]\n  end\nend\n```\nOn inclut bien-sûr le module `Cloudinary::CarrierWave`, on rajoute `eager` dans `version :thumb` (c'est facultatif, ça \npermet de forcer le traitement à l'upload des fichiers, sans ça, la création des miniatures se ferait lors des requêtes \ndu navigateur lors de l'affichage de la page) et on ajoute une méthode `public_id` afin de créer un identifiant unique \npour chaque fichier (le `'acme/' +` est lui aussi facultatif).  \nSi, comme moi, vous avez créer deux uploaders, vous faites bien-sûr la même chose pour l'autre, PictUploader!  \n\n3. Modification des form(ulaires)  \n\nDéjà, dans mes vues `edit.html.erb`, j'ai remplacé le `\u003c%= render 'form' %\u003e` par `\u003c%= render 'form', brand: @brand %\u003e` et \ndonc, dans mes partials `_form.html.erb` je n'ai plus de variables d'instances précédées d'un @, que des variables locales  \n\n```html+erb\n\u003c%= form_for product do |f| %\u003e\n  \u003c% if product.errors.any? %\u003e\n    \u003cdiv id=\"error_explanation\"\u003e\n      \u003ch2\u003e\u003c%= pluralize(product.errors.count, \"error\") %\u003e prohibited this product from being saved:\u003c/h2\u003e\n\n      \u003cul\u003e\n      \u003c% product.errors.full_messages.each do |message| %\u003e\n        \u003cli\u003e\u003c%= message %\u003e\u003c/li\u003e\n      \u003c% end %\u003e\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  \u003c% end %\u003e\n\n  \u003cdiv class=\"form-group field\"\u003e\n    \u003c%= f.label :name %\u003e\u003cbr\u003e\n    \u003c%= f.text_field :name %\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"form-group field\"\u003e\n    \u003c%= f.label :description %\u003e\u003cbr\u003e\n    \u003c%= f.text_area :description %\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"form-group field\"\u003e\n    \u003c%= f.label :brand %\u003e\u003cbr\u003e\n    \u003c%= f.text_field :brand_id %\u003e\n    \u003c%= f.fields_for :brand, product.brand do |ff| %\u003e\n      \u003cdiv class=\"form-group field\"\u003e\n      \u003c%= ff.label :name %\u003e\u003cbr\u003e\n      \u003c%= ff.text_field :name %\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"form-group field\"\u003e\n      \u003c%= ff.label :avatar %\u003e\u003cbr\u003e\n        \u003c%= image_tag(product.brand.avatar_url) unless product.brand.avatar.file.nil? %\u003e\n        \u003c%= ff.hidden_field :avatar_cache %\u003e\u003cbr\u003e\n      \u003c%= ff.file_field :avatar %\u003e\n      \u003c/div\u003e\n    \u003c% end %\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"form-group field\"\u003e\n    \u003c%= f.label :pict %\u003e\u003cbr\u003e\n    \u003c%= image_tag(product.pict_url) unless product.pict.file.nil? %\u003e\n    \u003c%= f.hidden_field :pict_cache %\u003e\u003cbr\u003e\n    \u003c%= f.file_field :pict %\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"form-group field\"\u003e\n    \u003c%= f.label :price %\u003e\u003cbr\u003e\n    \u003c%= f.text_field :price %\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"actions\"\u003e\n    \u003cbutton\u003e\n      \u003c%= f.submit %\u003e\n    \u003c/button\u003e\n\n  \u003c/div\u003e\n\u003c% end %\u003e\n```\n\nPareil pour le form de brand, en plus simple puisque pas de **nested_attributes**  \n\n4. Modification des `index.html.erb`  \n\nexemple avec le products  \n\n```html+erb\n\u003cp id=\"notice\"\u003e\u003c%= notice %\u003e\u003c/p\u003e\n\n\u003ch1\u003eListing Products\u003c/h1\u003e\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth\u003eName\u003c/th\u003e\n      \u003cth\u003eDescription\u003c/th\u003e\n      \u003cth\u003eBrand\u003c/th\u003e\n      \u003cth\u003ePict\u003c/th\u003e\n      \u003cth\u003ePrice\u003c/th\u003e\n      \u003cth colspan=\"3\"\u003e\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n\n  \u003ctbody\u003e\n    \u003c% @products.each do |product| %\u003e\n      \u003ctr\u003e\n        \u003ctd\u003e\u003c%= product.name %\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003c%= product.description %\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003c%= product.brand.name %\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003c%= image_tag(product.pict_url(:thumb)) unless product.pict.file.nil? %\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003c%= product.price %\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003c%= link_to 'Show', product, class: 'btn btn-xs btn-primary' %\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003c%= link_to 'Edit', edit_product_path(product), class: 'btn btn-xs btn-primary' %\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003c%= link_to 'Destroy', product, method: :delete, class: 'btn btn-xs btn-danger', data: { confirm: 'Are you sure?' } %\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n    \u003c% end %\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003cbr\u003e\n\n\u003c%= button_to 'New Product', new_product_path, class: 'btn btn-primary' %\u003e\n\u003c%= link_to 'Brands', brands_path, class: 'btn btn-primary' %\u003e\n\n```\nOn remarquera, pour faire zoli (!), l'utilisation de classes CSS de Bootstrap sur les \n`link_to 'Titre', chemin, , class: 'btn btn-xs btn-primary'` ou `btn-danger` pour l'action delete, `btn-xs` bouton eXtra \nSmall pour avoir de petits boutons !  \nPetit rappel: `link_to` déclenche par défaut un `GET`, alors que `button_to` est associé par défaut à `POST`  \n\n5. Modification des `show.html.erb`  \n\nBon, maintenant, vous pouvez le faire tout seul !  \n\n## ex03\n  \nMes références: le livre `Agile Web Development with Rails` et un site web en français [Hackademy Rails gestion du panier](https://hackademy.io/tutoriel-videos/rails-07-gestion-du-panier)\n, [un article de Medium sur les Single Table Inheritance](https://medium.com/@dcordz/single-table-inheritance-using-rails-5-02-6738bdd5101a)\nou encore, cette série d'articles de blog sur les [Single Table Inheritance (STI)](https://devblast.com/b/single-table-inheritance-with-rails-4-part-1)  \n\nPourquoi de la doc sur les STI ? Car Cart et Order se ressemblent, vous ne trouvez pas ? De même que CartItem et OrderItem...  \n\nOn commence par créer deux migrations pour Cart et CartItem:\n```shell script\nrails g model Cart\nrails g model CartItem product:references cart:belongs_to quantity:integer\nrails g migration add_type_to_cart type:string \nrails g migration add_type_to_cart_items type:string \n```\n\u003eà noter que `belongs_to` est juste un alias de `references`  \n\nOn édite la seconde migration pour rajouter `default: 1` à quantity, et les deux dernières en rajoutant respectivement \n`default: 'Cart'` et `default: 'CartItem'` puis les modèles pour terminer les associations. Dans \nle modèle Cart, on rajoute `has_many :cart_items, dependent: :destroy` et on vérifie les associations pour CartItem:  \n\n```ruby\nclass CartItem \u003c ActiveRecord::Base\n  belongs_to :product\n  belongs_to :cart\nend\n```\nDonc un premier concern pour les contrôleurs, nommé `recordable.rb` :\n```ruby\n# acme/app/controllers/concerns/recordable.rb\nmodule Recordable\n  extend ActiveSupport::Concern\n\n  def add\n    current_cart.add_item @product\n    # puts '*****************************************************************'\n    # puts \"params: #{params}\"\n    # puts \"je suis bien dans add avec (#{@product})\"\n    # puts \"current_cart: #{current_cart.inspect}\"\n    # puts \"cart_items: #{current_cart.cart_items.inspect}\"\n    # puts '*****************************************************************'\n\n    # render partial: 'shared/panier', locals: { cart: current_cart } # , notice: 'Product was successfully added to cart.'\n    redirect_to products_path, notice: 'Product was successfully added to cart.'\n  end\n\n  def remove\n    current_cart.remove_item @product\n    redirect_to products_path, notice: 'Product was successfully deleted from cart.'\n  end\n\n  def remove_all\n    current_cart.remove_all\n    redirect_to products_path, notice: 'Cart was successfully emptied.'\n  end\n\n  def checkout\n    render partial: 'shared/checkout'\n  end\nend\n```  \nun second pour la `current_cart`:  \n```ruby\nmodule CurrentCart\n  private\n\n  def current_cart\n    @cart = Cart.find_by(id: session[:cart_id]) || Cart.create\n    session[:cart_id] ||= @cart.id\n    @cart\n  end\nend\n```\nBien entendu, on n'oublie pas de les inclure là où il faut, par exemple pour les contrôleurs\n```ruby\nclass ProductsController \u003c ApplicationController\n  include Recordable\n  include CurrentCart\n  before_action :set_product, only: %i[show edit update destroy add remove]\n  before_action :current_cart, only: %i[index add remove]\n #\n # etc\nend \n```\nUn autre pour les modèles, à inclure dans le modèle Cart:  \n```ruby\n# acme/app/models/concerns/add_item_concern.rb\nmodule AddItemConcern\n  def add_item(product)\n    new_item = cart_items.find_by(product: product)\n    if new_item\n      new_item.quantity += 1\n    else\n      new_item = CartItem.new(product: product, cart_id: id)\n    end\n    new_item.save\n  end\n\n  def remove_item(product)\n    new_item = cart_items.find_by(product: product)\n    if new_item.quantity \u003e 1\n      new_item.quantity -= 1\n      new_item.save\n    else\n      cart_items.delete new_item\n    end\n    cart_items\n  end\n\n  def remove_all\n    cart_items.each do |cart_item|\n      cart_items.delete cart_item\n    end\n  end\nend\n```\n\nOn n'oublie pas de créer les routes correspondantes :\n```ruby\n# extrait de acme/config/routes.rb\n  resources :products do\n    collection do\n      post 'remove_all', to: 'products#remove_all', as: :remove_all_from_cart\n    end\n\n    member do\n      post 'add', to: 'products#add', as: :add_to_cart\n      post 'remove', to: 'products#remove', as: :remove_to_cart\n    end\n  end\n\n  post 'checkout', to: 'products#checkout', as: '/checkout' \n```\n\nEt on affiche tout ça, avec par exemple un partial, nommé, soyons fou, `_panier.html.erb`\n```html+erb\n\u003cnav class=\"nav nav-nav nav-right\"\u003e\n  \u003cspan\u003e\n    \u003ch3\u003e\n      PANIER DE\n      \u003c%= current_user.name.upcase if current_user %\u003e\n    \u003c/h3\u003e\n  \u003c/span\u003e\n  \u003ctable class=\"container-fluid\"\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth\u003eName\u003c/th\u003e\n      \u003cth\u003eUnit Price\u003c/th\u003e\n      \u003cth\u003eQuantity\u003c/th\u003e\n      \u003cth\u003ePrice\u003c/th\u003e\n      \u003cth colspan=\"2\"\u003e\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n    \u003ctbody\u003e\n        \u003c% unless current_cart.nil? \u0026\u0026 current_cart.cart_items.empty? %\u003e\n          \u003c% current_cart.cart_items.each do |cart_item| %\u003e\n            \u003ctr\u003e\n              \u003ctd\u003e\u003c%= cart_item.product.name %\u003e\u003c/td\u003e\n              \u003ctd\u003e\u003c%= cart_item.product.price %\u003e\u003c/td\u003e\n              \u003ctd\u003e\u003c%= cart_item.quantity %\u003e\u003c/td\u003e\n              \u003ctd\u003e\u003c%= cart_item.quantity * cart_item.product.price %\u003e\u003c/td\u003e\n              \u003ctd\u003e\u003c%= button_to '+', add_to_cart_product_path(cart_item.product), class: 'btn btn-xs btn-primary' %\u003e\u003c/td\u003e\n              \u003ctd\u003e\u003c%= button_to '-', remove_to_cart_product_path(cart_item.product), class: 'btn btn-xs btn-primary' %\u003e\u003c/td\u003e\n            \u003c/tr\u003e\n          \u003c% end %\u003e\n      \u003c% end %\u003e\n    \u003c/tbody\u003e\n  \u003c/table\u003e\n  \u003cdiv class=\"btn-group btn-group-justified\"\u003e\n    \u003ctable\u003e\n      \u003ctr\u003e\n        \u003cth\u003e\n          \u003c%= button_to \"Vider le panier\", remove_all_from_cart_products_path, data: {\n              confirm: \"Êtes-vous sûr ?\" }, class: \"btn btn-danger \" %\u003e\n        \u003c/th\u003e\n        \u003cth\u003e\n          \u003c%= button_to 'Checkout', checkout_path, class: \"btn btn-primary\" %\u003e\n        \u003c/th\u003e\n      \u003c/tr\u003e\n    \u003c/table\u003e\n  \u003c/div\u003e\n\u003c/nav\u003e\n```\n\nSans oublier de rajouter une ligne dans la boucle each d'affichage de l'index des Product:\n```html+erb\n \u003c!-- extrait de acme/app/views/products/index.html.erb --\u003e\n  \u003ctbody\u003e\n    \u003c% @products.each do |product| %\u003e\n      \u003ctr\u003e\n        \u003c!-- etc --\u003e\n        \u003ctd\u003e\u003c%= link_to 'Ajouter au panier', add_to_cart_product_path(product), method: :post, class: 'btn btn-xs btn-primary' %\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n```\n\n## ex04  \n\nBon, ben là, pas grand chose à faire, la gem est déjà dans le Gemfile, il suffit juste de décommenter 4 lignes dans \n`acme/config/initializers/rails_admin.rb`  \n\n```ruby\n# extrait de acme/config/initializers/rails_admin.rb \n  config.authenticate_with do\n    warden.authenticate! scope: :user\n  end\n  config.current_user_method(\u0026:current_user)\n```\net de créer un lien vers le dashboard:  \n```html+erb\n      \u003c!-- extrait de acme/app/views/shared/_navigation.html.erb --\u003e\n      \u003cspan\u003e\n        \u003c%= link_to 'admin', rails_admin_path, class: 'btn btn-xs btn-primary' %\u003e\n      \u003c/span\u003e\n```  \n\nJ'ai rajouté dans config/routes.rb ceci, est-ce vraiment utile, à vous de le tester !\n```ruby\n  # extrait de acme/config/routes.rb\n   mount RailsAdmin::Engine =\u003e '/admin', as: 'rails_admin'\n \n```\n\nEt hop, le tour est joué !  \n  \n## ex05\n[doc sur cancancan et rails_admin](https://github.com/sferik/rails_admin/wiki/CanCanCan)\n\n```ruby\n  # dans application_controller.rb\n  rescue_from CanCan::AccessDenied do |exception|\n    redirect_to main_app.root_url, alert: exception.message\n  end\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquarkgluant%2Fday07_piscine_ruby_42","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fquarkgluant%2Fday07_piscine_ruby_42","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquarkgluant%2Fday07_piscine_ruby_42/lists"}