Introduction à Sinatra

3,322 views

Published on

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,322
On SlideShare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
44
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Introduction à Sinatra

  1. 1. Introduction àSinatraRémi Prévost — ConFoo 2011
  2. 2. Rémi Prévost Développeur Web@remi + http://remiprevost.com
  3. 3. Sinatra • Présentation • Installation • Utilisation • Déploiement
  4. 4. Historique et possibilités
  5. 5. Historique 2008 Blake Mizerany & Adam Wiggins
  6. 6. Historique Problème Services Web légers
  7. 7. Historique Solution Un micro-framework
  8. 8. Historique Possibilités infinies
  9. 9. Historique Prototypes d’applications Web
  10. 10. Historique Applications Web complètes
  11. 11. Historique APIs « RESTful »
  12. 12. Avantages Les bons côtés
  13. 13. Avantages Installation rapide
  14. 14. $ gem install sinatra=> Successfully installed rack-1.2.1 Successfully installed tilt-1.2.2 Successfully installed sinatra-1.2.0
  15. 15. Avantages Développement minimaliste
  16. 16. # contenu de hello.rbrequire "sinatra"get "/" do "Hello world."end$ ruby -rubygems hello.rb=> == Sinatra/1.2 has taken the stage on 4567…$ curl http://localhost:4567/=> Hello world.
  17. 17. $ rails new blogue=> create README create Rakefile create config.ru create .gitignore create Gemfile create app …$ du -hd0=> 428K .
  18. 18. Avantages Déploiement facile
  19. 19. Désavantages Les moins bons côtés
  20. 20. Désavantages • Fonctionnalités réduites • Expansion moins guidée • Documentation moins large
  21. 21. RackInterface HTTP
  22. 22. Rack Framework pour frameworks
  23. 23. # contenu de config.ruclass RackApp def call(env) [200, { "Content-type" => "text/html" }, "Hello world."] endendrun RackApp.new$ rackup --env development=> INFO WEBrick::HTTPServer#start: pid=37743 port=9292$ curl http://localhost:9292/=> Hello world.
  24. 24. # contenu de blogue.rbrequire "sinatra"class Blogue < Sinatra::Base # Code l’application Sinatraend# contenu de config.rurequire "blogue"run Blogue.new
  25. 25. # contenu de blogue.rbrequire "sinatra"# Code l’application Sinatra au premier niveau# contenu de config.rurequire "blogue"run Sinatra::Application
  26. 26. RoutesLes directions
  27. 27. Routes REST Basées sur les méthodes HTTP
  28. 28. Routes • GET • POST • PUT • DELETE
  29. 29. methode_http(route) { reponse }
  30. 30. methode_http(route) { reponse }get("/") { "Hello world." }
  31. 31. Routes Statiques Routes fixes
  32. 32. get "/" do "Page principale du blogue"endpost "/admin/article" do "Création d’un nouvel article"end
  33. 33. Routes Paramètres Routes variables
  34. 34. get "/auteur/:username" do "Les billets de #{params[:username]}"enddelete "/articles/:id" do "Suppression de l’article #{params[:id]}"endput "/articles/:id" do |id| "Modification de l’article #{id}"end
  35. 35. Routes Splat Routes avec « wildcards »
  36. 36. get "/fichiers/*.*" do # GET /fichiers/images/2011/03/foo.jpg # params[:splat] => ["/images/2011/03/foo", ".jpg"]endget "/*" do # GET /url/inconnu # params[:splat] => ["url/inconnu"]end
  37. 37. Routes Regex Routes avec expressions
  38. 38. get /^/(d{4})$/ do |annee| "Les articles de l’année #{annee}" # params["captures"] => [annee]endget %r{^(d{4})/(d{2})$} do |annee, mois| "Les articles du mois #{mois} de #{annee}" # params["captures"] => [annee, mois]end# seulement ruby 1.9get %r{^(?<annee>d{4})/(?<mois>d{2})$} do "Les articles du mois #{params[:mois]} de #{params[:annee]}"end
  39. 39. Routes Conditions Routes conditionnelles
  40. 40. get "/", :agent => /msie [w.]+/i do "Page d’accueil pour Internet Explorer"endget "/" do "Page d’accueil pour les autres user-agents"end
  41. 41. set(:secret) do |value| condition { params.include?(:secret) == value }endget "/", :secret => true do "Page d’accueil secrète!"endget "/" do "Page d’accueil régulière."end
  42. 42. Routes • methode_http(route) { reponse } • Paramètres (réguliers, regex, splat) • Conditions
  43. 43. ExécutionPasser, arrêter ou filtrer
  44. 44. Exécution Passer d’une route à la suivante
  45. 45. get "/admin/dashboard" do pass unless authenticate! "Le tableau de bord secret"endget "/admin/*" do "Vous semblez ne pas être identifié."end
  46. 46. Exécution Arrêter l’exécution du code
  47. 47. get "/admin/dashboard" do halt(401, "Vous voulez hacker ce blogue?") unless authenticate! "Le tableau de bord secret"end
  48. 48. Exécution Filtrer avant et après
  49. 49. Exécution before Avant la route
  50. 50. before "/admin/*" do halt(401, "Vous voulez hacker ce blogue?") unless authenticate!endget "/admin/dashboard" do "Tableau de bord de quelqu’un d’authentifié"endpost "/admin/billets" do "Création d’un billet par quelqu’un d’authentifié"end
  51. 51. before "/compte/*" do @user = User.find(session[:user_id])endget "/compte/photo" do "Formulaire de modification de la photo de #{@user}"endget "/compte/motdepasse" do "Formulaire de modification du mot de passe de #{@user}"end
  52. 52. before :agent => /msie 6.0/i do @message = "Vous utilisez un navigateur dépassé…"end
  53. 53. Exécution after Après la route
  54. 54. after "*" do headers "X-Secret-Data" => "LOL"end
  55. 55. Templates Les vues
  56. 56. Templates Réponses compatibles avec Rack
  57. 57. get "/" do "Page principale du blogue"end
  58. 58. get "/" do [200, "Page principale du blogue"]endget "/api/articles.json" do [200, { "Content-type": "application/json" }, "[]"]end
  59. 59. Templates Tilt Templates à la demande
  60. 60. get "/" do haml :indexendget "/css/screen.css" do sass :screenendget "/api/articles.xml" do nokogiri :"api/articles"endget "/api/articles.json" do coffee :"api/articles"end
  61. 61. Templates Options pour chaque engin
  62. 62. get "/" do haml :index, :format => :html4endget "/css/screen.css" do scss :screen, :style => :compressedend
  63. 63. set :haml, :format => :html5, :ugly => trueset :scss, :style => :compressed
  64. 64. Templates Internes Stockés dans le code Ruby
  65. 65. enable :inline_templatesget "/" do haml :indexend__END__@@ layout!!!%html %body =yield@@ index%h1 Bienvenue sur mon blogue.
  66. 66. template :layout do "!!!n%htmln%bodyn=yieldn"endtemplate :index do "%h1 Bienvenue sur mon blogue."endget "/" do haml :indexend
  67. 67. get "/" do haml "!!!n%body Bienvenue sur mon blogue."end
  68. 68. Templates Externes Stockés en tant que fichiers
  69. 69. get "/" do haml :index # /views/index.hamlendget "/css/screen.css" do sass :screen # /views/screen.sassendget "/api/articles.xml" do builder :"api/articles"# /views/api/articles.builderendget "/api/articles.json" do coffee :"api/articles" # /views/api/articles.coffeeend
  70. 70. set :views, Proc.new { File.join(root, "templates") }
  71. 71. Templates Layout Template commun
  72. 72. get "/" do haml :index # template: views/index.haml # layout: views/layout.hamlendget "/article/:id" do @article = Article.find(params[:id]) markdown :article, :layout_engine => :haml # template: views/article.markdown # layout: views/layout.hamlendget "/ajax/article/:id.html" do @article = Article.find(params[:id]) haml :article, :layout => false # template: views/article.haml # layout: n/aend
  73. 73. Templates Données Les utiliser dans les templates
  74. 74. get "/" do @articles = Article.all haml :indexend-# contenu de index.haml.hfeed - @articles.each do |article| .entry %h1= article.titre .entry-content = markdown(article.contenu)
  75. 75. get "/" do articles = Article.all haml :index, :locals => { :articles => articles }end-# contenu de index.haml.hfeed - articles.each do |article| .entry %h1= article.titre .entry-content = markdown(article.contenu)
  76. 76. get "/" do @articles = Article.all haml :indexend-# contenu de index.haml.hfeed - @articles.each do |article| .entry = haml :article, :locals => { :article => article }-# contenu de article.haml.entry %h1= article.titre .entry-content = markdown(article.contenu)
  77. 77. Templates Helpers Utilitaires disponibles partout
  78. 78. helpers do def heading(level, text) "<h#{level}>#{text}</h#{level}>" endend%h1 Derniers articles%ul.articles - @articles.each do |article| %li =heading(2, article.title)
  79. 79. helpers do def link_to(path, text) path = "#{request.host}#{path}" if request.xhr? "<a href="#{path}">#{text}</a>" endenddef other_link_to(path, text) # n’a pas accès à `request` "<a href="#{path}">#{text}</a>"end%h1 Bienvenue%p= link_to "/", "Accueil"%p= other_link_to "/", "Accueil encore"
  80. 80. Templates • Tilt • Options • Internes + Externes • Données • Helpers
  81. 81. Configuration et environnements
  82. 82. Configuration Globale à tous les environnements
  83. 83. configure do DataMapper.setup :default, ENV["DATABASE_URL"] DataMapper::Pagination.defaults[:per_page] = 20 DataMapper::Logger.new $stdout, :debugendget "/" do @articles = Article.all haml :indexend
  84. 84. Configuration Spécifique à un environnement
  85. 85. $ rackup --env development=> INFO WEBrick::HTTPServer#start: pid=37743 port=9292$ rackup --env production=> INFO WEBrick::HTTPServer#start: pid=37743 port=9292$ shotgun --env development== Shotgun/WEBrick on http://127.0.0.1:9393/$ thin start --env production>> Thin web server (v1.2.8 codename Black Keys)>> Listening on 0.0.0.0:3000, CTRL+C to stop
  86. 86. configure :development do set :scss, :style => :expanded set :haml, :ugly => falseendconfigure :production do set :scss, :style => :compressed set :haml, :ugly => trueend
  87. 87. configure :development, :test do set :s3, { :bucket => "blogue-dev", :key => "efg456" }endconfigure :production do set :s3, { :bucket => "blogue", :key => "abc123" }endget "/" do "La valeur de s3/bucket est de #{settings.s3[:bucket]}"end
  88. 88. Erreursgérées comme des routes
  89. 89. Erreurs Routes introuvables
  90. 90. not_found do "Cette page n’a pu être trouvée"end
  91. 91. not_found do haml :erreurend
  92. 92. Erreurs HTTP Codes d’erreurs standards
  93. 93. error 403 do haml :"erreurs/interdit"enderror 405..500 do haml :"erreurs/autre"end
  94. 94. Erreurs Exceptions personnalisées
  95. 95. error UnauthenticatedUser do haml :"erreurs/non_authentifie"endbefore "/admin/*" do raise UnauthenticatedUser unless authenticate!end
  96. 96. Fichierset téléchargements
  97. 97. Fichiers Fichiers publics
  98. 98. $ tree .=> "## blogue.rb "## config.ru "## public    "## css    %   &## screen.css    &## js    &## global.js$ curl http://localhost:9292/css/screen.css=> …$ curl http://localhost:9292/js/global.js=> …
  99. 99. set :public, Proc.new { File.join(root, "fichiers/statiques") }
  100. 100. FichiersTéléchargements de fichiers
  101. 101. get "/live/report.txt" do # Construction dynamique du fichier /tmp/report.txt # … send_file "/tmp/report.txt", :type => :attachmentend
  102. 102. Sessions et cookies
  103. 103. Sessions SessionsDonnées temporaires encryptées
  104. 104. enable :sessionsbefore "/admin/*" do unless session[:admin] halt "Vous devez être <a href="/login">connecté</a>." endendget "/login" do haml :loginendpost "/login" do if params[:username] == "foo" and params[:password] == "bar" session[:admin] = true redirect "/admin" endend
  105. 105. Sessions Cookies Données persistantes
  106. 106. before do unless request.cookies.include?("deja_venu_ici") response.set_cookies("deja_venu_ici", { :value => true, :expires => Time.now + (60*60*24*365) }) @nouveau_visiteur = true endendget "/" do haml :index # peut utiliser @nouveau_visiteurend
  107. 107. TestsVérifier le fonctionnement
  108. 108. Sessions Rack::Test Tests pour applications Rack
  109. 109. $ gem install rack-test=> Successfully installed rack-test-0.5.7
  110. 110. require "blogue"require "test/unit"require "rack/test"class BlogueTest < Test::Unit::TestCase include Rack::Test::Methods def app; Blogue; end def test_page_accueil get "/" assert_equal "Page d’accueil du blogue", last_response.body endend
  111. 111. require "blogue"require "test/unit"require "rack/test"class BlogueTest < Test::Unit::TestCase include Rack::Test::Methods def app; Blogue; end def test_redirection_mauvais_acces_au_tableau_de_bord get "/admin/dashboard" assert_equal "/admin/login", last_request.url assert last_response.ok? end def test_redirection_connexion_au_tableau_de_bord post "/admin/login", :username => "foo", :password => "bar" assert_equal "/admin/dashboard", last_request.url assert last_response.ok? endend
  112. 112. $ ruby test.rb=> Loaded suite test Started .. Finished in 0.009936 seconds. 2 tests, 2 assertions, 0 failures, 0 errors
  113. 113. Déploiement d’une application
  114. 114. Déploiement Bundler Gestionnaire de gems
  115. 115. Déploiement Sans Bundler
  116. 116. # Contenu de config.rurequire "rubygems"require "sinatra"require "haml"require "dm-core"require "blogue"run Blogue.new
  117. 117. $ gem install sinatra dm-core haml=> Successfully installed rack 1.2.1 Successfully installed tilt-1.2.2 Successfully installed sinatra-1.1.3 Successfully installed extlib-0.9.15 Successfully installed dm-core-1.0.2 Successfully installed haml-3.0.25
  118. 118. Déploiement Avec Bundler
  119. 119. $ gem install bundler=> Successfully installed bundler-1.0.10
  120. 120. # Contenu du fichier Gemfilesource "http://rubygems.org"gem "sinatra"gem "haml"gem "dm-core", "~> 1.0"
  121. 121. $ bundle install --path .bundle/gems=> Fetching source index for http://rubygems.org/ Installing addressable (2.2.4) Installing extlib (0.9.15) Installing dm-core (1.0.2) Installing haml (3.0.25) Installing rack (1.2.1) Installing tilt (1.2.2) Installing sinatra (1.1.3) Using bundler (1.0.10) Your bundle is complete! It was installed into ./bundle/gems
  122. 122. # Contenu de config.rurequire "bundler"Bundler.requirerequire "blogue"run Blogue.new
  123. 123. $ bundle exec rackup=> INFO WEBrick::HTTPServer#start: pid=22866 port=9292
  124. 124. Déploiement Heroku Plateforme de déploiement
  125. 125. $ gem install heroku=> Successfully installed configuration-1.2.0 Successfully installed launchy-0.3.7 Successfully installed heroku-1.17.16 3 gems installed
  126. 126. $ git init=> Initialized empty Git repository in /Code/blogue/.git/$ echo ".bundle" > .gitignore
  127. 127. $ heroku create blogue=> Creating blogue.... done http://blogue.heroku.com/ | git@heroku.com:blogue.git Git remote heroku added
  128. 128. $ git add .$ git commit -m "Initial commit"$ git push heroku master=> Counting objects: 14, done. Delta compression using up to 2 threads. Compressing objects: 100% (10/10), done. Writing objects: 100% (14/14), 1.81 KiB, done. Total 14 (delta 0), reused 0 (delta 0) -----> Heroku receiving push -----> Sinatra app detected -----> Gemfile detected, running Bundler version 1.0.7 Unresolved dependencies detected; Installing... … Your bundle is complete! Compiled slug size is 924K -----> Launching... done http://blogue.heroku.com deployed to Heroku To git@heroku.com:blogue.git * [new branch] master -> master
  129. 129. RésuméSinatra en bref
  130. 130. Résumé Rack Compatible avec tout (!)
  131. 131. Résumé Développement minimaliste
  132. 132. Résumé Routes orientées « REST »
  133. 133. Résumé Templates flexibles
  134. 134. Résumé Sessions, cookies, tests, filtres, etc.
  135. 135. Résumé Déploiement facile avec Bundler
  136. 136. RésuméSinatra en bref
  137. 137. Ressources • sinatrarb.com • sinatra-book.gittr.com/ • peepcode.com/products/sinatra • irc.freenode.net/sinatra (IRC) • github.com/remiprev/nid (exemple)
  138. 138. Questions? Commentaires? @remi

×