Swing when you're winning - an introduction to Ruby and Sinatra

5,999
-1

Published on

Session originally given at Scotch on the Rocks 2013 in Edinburgh.

In this session we will explore how to build a RESTful-based application using the Sinatra framework, built on top of the Ruby programming language. We will explore installing Ruby, creating our first Sinatra application, the use of route definitions to handle multiple METHOD request types, including GET and POST requests, data persistence in a SQLite database, and how to return data in multiple formats including JSON and HTML. The RESTful approach and ease of use offered by Sinatra make it a great choice for underlying API requests which you can implement and call from any programming language of your choice.

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

No Downloads
Views
Total Views
5,999
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
0
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Swing when you're winning - an introduction to Ruby and Sinatra

  1. 1. == Sinatra has taken to the stage with backup fromMatt Gifford@coldfumonkehwww.monkehworks.com
  2. 2. “SWING WHEN YOU’RE WINNING”AN INTRODUCTION TOSINATRA & RUBY
  3. 3. I build stuffand I write about building stuff too
  4. 4. WHAT WE’LL COVER:Getting your first Sinatra project up and runningWhat is Sinatra?Brief overview of a Sinatra application structureHandling routes and formatsDeploying to Heroku ** other options do exist
  5. 5. LIVE CODE DEMOS?WHAT COULD POSSIBLYGO WRONG?
  6. 6. NOT SURE IF I SHOULD BE HEREOR WATCHING SOMEONE ELSE
  7. 7. WHAT ELSE IS ON?Using personas in service design - continuouslyMura 6 for developersTuüli Aalto-NyyssonenSteve Withington
  8. 8. WAIT!You’re a ColdFusion developer, aren’t you?And this is a ColdFusion conference, isn’t it?
  9. 9. YES I AM!(and proud of it)
  10. 10. BACK TO SINATRA
  11. 11. WHY NOT RAILS?It is awesome though...Rails is quite large and sometimes too muchmonkeh$ rails create [stuff]Ships with ORM, routes, test suites etcEasily installed with RubyInstaller(.org)
  12. 12. SO WHY SINATRA?Doesn’t force any frameworkExtremely lightweightA Domain Specific Language (DSL)Incredibly quick and simple(but plays well with others if you want to)
  13. 13. SO WHY SINATRA?Runs on RackCan be a single file web applicationCan use a number of JavaScript librariesCan use a number of template enginesIt also means I can show pictures like this...
  14. 14. MORE BOUNCE TO THE OUNCE
  15. 15. DEVELOPMENT IS QUICK!
  16. 16. “simplicityistheultimatesophistication”Leonardo da Vinci
  17. 17. SINATRA IS GREAT FORREST APIsSmall appsuseful for AJAX calls to dataKeeping your simple application simpleIf it gets too big, consider using Rails (or CF)
  18. 18. HOW SIMPLE?
  19. 19. application.rbrequire rubygemsrequire sinatraget /hi do"Hello World!"endTHIS SIMPLE!
  20. 20. monkeh$ ruby application.rb== Sinatra/1.4.2 has taken to the stage on 4567 for development withbackup from Thin>> Thin web server (v1.5.1 codename Straight Razor)>> Maximum connections set to 1024>> Listening on localhost:4567, CTRL+C to stopRUNNING THE APP
  21. 21. IMPRESSIVE, RIGHT?
  22. 22. require rubygemsrequire sinatraget /hi do"Hello World!"endWHAT’S GOING ON?
  23. 23. require rubygemsrequire sinatraget /hi do"Hello World!"endWHAT’S GOING ON?<--- Ruby package manager
  24. 24. require rubygemsrequire sinatraget /hi do"Hello World!"endWHAT’S GOING ON?<--- Ruby package manager<--- Sinatra gem
  25. 25. require rubygemsrequire sinatraget /hi do"Hello World!"endWHAT’S GOING ON?<--- Ruby package manager<--- Sinatra gem<--- GET request
  26. 26. require rubygemsrequire sinatraget /hi do"Hello World!"endWHAT’S GOING ON?<--- Ruby package manager<--- Sinatra gem<--- GET request<--- response
  27. 27. require rubygemsrequire sinatraget /hi do"Hello World!"endNO RETURN?<--- the return is implied
  28. 28. get /hi do"Hello World!"endROUTE BLOCKS<--- this is a route blockA route block is an HTTP method paired witha URL-matching pattern.Processing takes place within the block (database calls etc)and the result is sent to the browser.
  29. 29. RESTUniform InterfaceClient-ServerCacheableHATEOASLayered SystemStatelessHypermedia as the Engineof Application State
  30. 30. SINATRA CAN DO THAT!CachingMultiple RoutesContent TypesAll HTTP verbs supportedGood times!
  31. 31. RESTget / do.. show something ..endpost / do.. create something ..endput / do.. replace something ..endpatch / do.. modify something ..enddelete / do.. annihilate something ..endoptions / do.. appease something ..endlink / do.. affiliate something ..endunlink / do.. separate something ..end
  32. 32. ROUTESget / do"Home page"endget /hello do"Hello!"endget /hello/:name do"Hello! #{params[:name]}"end
  33. 33. ROUTESget /say/*/to/* do# matches /say/hello/to/worldparams[:splat] # => ["hello", "world"]endget /download/*.* do# matches /download/path/to/file.xmlparams[:splat] # => ["hello", "world"]endget /download*.* do |path, ext|# matches /download/path/to/file.xml[path, ext] # => ["path/to/file", "xml"]end
  34. 34. ROUTESget %r{/hello/([w]+)} do"Hello, #{params[:captures].first}!"endget /posts.?:format? do# matches "/posts " and any extension# eg "GET /posts.xml" or "GET /posts.json"end
  35. 35. ROUTESget /hi, :agent => /Mozilla/(d.d)sw?/ do"You’re using Mozilla version #{params[:agent][0]}"endget /hi do# matches all non-Mozilla browsersend
  36. 36. GETTING STARTEDInstall RubyBathe in Ruby deliciousnessCreate your application fileInstall the Sinatra gem
  37. 37. http://rvm.io
  38. 38. monkeh$ rvm install 1.9.3monkeh$ rvm use --default 1.9.3monkeh$ rvm use jrubyhttp://cheat.errtheblog.com/s/rvm
  39. 39. GEMS?
  40. 40. GEMS?RubyGem distributes librariesapt-getSimilar in functionality to:A library is self-contained in a gemportageyum
  41. 41. GEMS!monkeh$ gem install [gem]monkeh$ gem uninstall [gem]monkeh$ gem listmonkeh$ gem fetch [gem]
  42. 42. BACK TO SINATRA
  43. 43. INSTALLINGmonkeh$ gem install sinatramonkeh$ gem install shotgun
  44. 44. DEMO TIME
  45. 45. DIRECTORY STRUCTURE| -- application.rb
  46. 46. SINGLE PAGE APPapplication.rbrequire rubygemsrequire sinatraget / dohtml = <form method="post">html += <input type="text" placeholder="Add the URL to shorten here..."html += name="url" id="url" />html += <input type="submit" value="Shorten!" />html += </form>htmlendpost / dohtml = <p>Thanks for submitting a URL.</p>htmlend
  47. 47. VIEWSMultiple template languages available, including:buildererbhamlsassliquidmarkdown
  48. 48. USING VIEWSapplication.rbrequire rubygemsrequire sinatraget / doerb :indexendpost / doerb :indexendviews/index.erb<form method="post"><input type="text" placeholder="Add the URL to shorten here..."name="url" id="url" /><input type="submit" value="Shorten!" /></form>
  49. 49. DIRECTORY STRUCTURE| -- application.rb| -- views-- index.erb
  50. 50. LAYOUTSA template that calls yield to draw in view dataCan also be managed through route blocksA template called “layout” will be used by default
  51. 51. USING LAYOUTSviews/layout.erb<!DOCTYPE html><html><head><title>Sinatra Intro</title></head><body><%= yield %></body></html>views/index.erb<form method="post"><input type="text" placeholder="Add the URL to shorten here..."name="url" id="url" /><input type="submit" value="Shorten!" /></form>
  52. 52. USING LAYOUTSviews/layout.erb<!DOCTYPE html><html><head><title>Sinatra Intro</title></head><body><%= yield %></body></html>views/index.erb<form method="post"><input type="text" placeholder="Add the URL to shorten here..."name="url" id="url" /><input type="submit" value="Shorten!" /></form>
  53. 53. DIRECTORY STRUCTURE| -- application.rb| -- views-- index.erb-- layout.erb
  54. 54. STATIC CONTENTAll static content is stored within a new directory“public”This is primarily forJavascriptCSSImages
  55. 55. DIRECTORY STRUCTURE| -- application.rb| -- views-- index.erb-- layout.erb| -- public-- css-- style.css-- javascript
  56. 56. STATIC CONTENT?
  57. 57. HELPERSapplication.rbhelpers dodef random_string(length)rand(36**length).to_s(36)enddef get_site_url(short_url)http:// + request.host + / + short_urlenddef generate_short_url(long_url@shortcode = random_string 5get_site_url(@shortcode)endend
  58. 58. FILTERSapplication.rb# This code will run before each event# Very useful for debugging parameters sent via the consolebefore doputs [Params]p paramsend# This code will run after each eventafter doputs response.statusend
  59. 59. CONFIGURATIONWill run once at startupCan be used toset application-wide values and optionsperform certain processes per environment
  60. 60. CONFIGURATIONconfigure do# All environmentsendconfigure :production do# Production onlyendconfigure :development, :test do# Development and Testend
  61. 61. CONFIGURATIONconfigure doset :variable, foo# multiple optionsset :variable1 => Hello, :variable2 => world# same as set :option, trueenable :option# same as set :option, falsedisable :optionendget / dosettings.variable? # => truesettings.variable # => fooend
  62. 62. CONFIGURATIONconfigure :development do# very useful for debugging parameters sent via the consolebefore doputs [Params]p paramsendend
  63. 63. DATAMAPPERSame API can talk to multiple datastoresUses adapters to achieve thissqlitemysqlpostgresql
  64. 64. DATAMAPPERDefine mappings in your modelComes bundled with tools to assist withmigrationconstraintstransactionstimestampsvalidations...and more!
  65. 65. INCLUDE THE LIBRARYapplication.rbrequire rubygemsrequire sinatrarequire data_mapper <--- DataMapper gem
  66. 66. CONFIGURATIONapplication.rbconfigure do# load models$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")Dir.glob("#{File.dirname(__FILE__)}/lib/*.rb") { |lib|require File.basename(lib, .*)}DataMapper::setup(:default, "sqlite3://#{Dir.pwd}/myDatabase.db")DataMapper.finalizeDataMapper.auto_upgrade!end
  67. 67. MODELlib/short_url.rbclass ShortURLinclude DataMapper::Resourceproperty :short_url, String, key: true, unique_index: true, required: trueproperty :url, Text, required: trueproperty :created_at, DateTimeproperty :updated_at, DateTimeend
  68. 68. SAVING DATAapplication.rbdef generate_short_url(long_url@shortcode = random_string 5su = ShortURL.first_or_create({ :url => long_url },{:short_url => @shortcode,:created_at => Time.now,:updated_at => Time.now})get_site_url(su.short_url)end
  69. 69. GETTING DATAapplication.rb# root pageget / do# get the current object of all links stored@urls = ShortURL.all;erb :indexendviews/index.erb<h1>Serving <%= @urls.count %> links</h1>
  70. 70. GETTING DATAapplication.rbget /:short_url do@URLData = ShortURL.get(params[:short_url])end
  71. 71. DIRECTORY STRUCTURE| -- application.rb| -- views-- index.erb-- layout.erb| -- public-- css-- style.css-- javascript| -- lib-- shorturl.rb
  72. 72. CONTENT TYPESReturn content in a number of formats, includingJSONXMLHTMLmonkeh$ gem install jsonMay need some more gems to process
  73. 73. CONTENT TYPEapplication.rbget / doif params[:url] and not params[:url].empty?@shortURL = generate_short_url(params[:url])content_type :json{:original_url => params[:url],:short_url => @shortURL}.to_jsonelse# get the current count of all links stored@urls = ShortURL.all;erb :indexendend
  74. 74. TESTING
  75. 75. WHY?
  76. 76. BECAUSE!
  77. 77. TESTING GEMSmonkeh$ gem install rspecmonkeh$ gem install rackmonkeh$ gem install rack/test
  78. 78. TESTING WITH RSPECspec/application_rspec.rbrequire_relative ../application.rbrequire rack/testset :environment, :testdef appSinatra::Applicationenddescribe URL Shortening Service doinclude Rack::Test::Methodsit "should load the home page" doget /last_response.should be_okendend
  79. 79. AUTO TESTINGmonkeh$ gem install ZenTest
  80. 80. AUTO TESTING NOTIFICATIONSmonkeh$ gem install autotest-growlmonkeh$ gem install autotest-fsevent
  81. 81. DIRECTORY STRUCTURE| -- application.rb| -- views-- index.erb-- layout.erb| -- public-- css-- style.css-- javascript| -- lib-- shorturl.rb| -- spec-- application_rspec.rb
  82. 82. http://gembundler.com/v1.3/gemfile.htmlmonkeh$ gem install bundler
  83. 83. Gemfilesource https://rubygems.orggem sinatragem jsongem dm-coregem dm-migrationsgem dm-postgres-adaptergroup :development dogem shotgunendgroup :production dogem thinendGEMFILE
  84. 84. monkeh$ bundle installmonkeh$ git add Gemfile Gemfile.lockBUNDLE TIME
  85. 85. CONFIG.RUMany reasons to use, but especially if..you are deploying to a different Rack handlerHerokuPassenger
  86. 86. config.rurequire ./application.rbrun Sinatra::ApplicationCONFIG.RU
  87. 87. Procfileweb: bundle exec thin -R config.ru start -p $PORT -e $RACK_ENVPROCFILE
  88. 88. HEROKU DEPLOYMENTmonkeh$ git initmonkeh$ git add .monkeh$ git commit -m "Initial commit"
  89. 89. monkeh$ heroku create urlshrinkappCreating urlshrinkapp... done, stack is cedarhttp://urlshrinkapp.herokuapp.com/ | git@heroku.com:urlshrinkapp.gitGit remote heroku addedHEROKU
  90. 90. monkeh$ git push heroku masterTotal 19 (delta 2), reused 0 (delta 0)-----> Ruby/Rack app detected-----> Installing dependencies using Bundler version 1.3.2Running: bundle install --without development:test --path vendor/bundle --binstubsvendor/bundle/bin --deploymentFetching gem metadata from https://rubygems.org/.........Fetching gem metadata from https://rubygems.org/..Installing data_objects (0.10.13)Installing dm-core (1.2.0)Installing dm-do-adapter (1.2.0)Installing dm-migrations (1.2.0)Installing do_postgres (0.10.13)Installing dm-postgres-adapter (1.2.0)Installing eventmachine (1.0.3)Installing json (1.8.0)Installing rack (1.5.2)Installing rack-protection (1.5.0)Installing tilt (1.4.1)Installing sinatra (1.4.2)Using bundler (1.3.2)Your bundle is complete! It was installed into ./vendor/bundleCleaning up the bundler cache.-----> Discovering process typesProcfile declares types -> webDefault types for Ruby/Rack -> console, rake-----> Compiled slug size: 3.0MB-----> Launching... done, v4http://urlshrinkapp.herokuapp.com deployed to HerokuTo git@heroku.com:urlshrinkapp.git* [new branch] master -> masterHEROKU
  91. 91. monkeh$ heroku addons:add heroku-postgresql:devAdding heroku-postgresql:dev on urlshrinkapp... done, v5 (free)Attached as HEROKU_POSTGRESQL_BROWN_URLDatabase has been created and is availableheroku pg:promote HEROKU_POSTGRESQL_BROWN_URLPromoting HEROKU_POSTGRESQL_BROWN_URL to DATABASE_URL... doneHEROKU
  92. 92. monkeh$ heroku run consoleRunning `console` attached to terminal... up, run.2858irb(main):001:0> require ./application.rb=> trueirb(main):002:0> DataMapper.auto_upgrade!=> #<DataMapper::DescendantSet:0x000000035aacf0@descendants=#<DataMapper::SubjectSet:0x000000035aaca0@entries=#<DataMapper::OrderedSet:0x000000035aac78@cache=#<DataMapper::SubjectSet::NameCache:0x000000035aac50@cache={"ShortURL"=>0}>, @entries=[ShortURL]>>>irb(main):003:0> exitHEROKU
  93. 93. monkeh$ heroku openOpening urlshrinkapp... doneHEROKU
  94. 94. DIRECTORY STRUCTURE| -- application.rb| -- views-- index.erb-- layout.erb| -- public-- css-- style.css-- javascript| -- Gemfile| -- lib| -- spec| -- config.ru| -- Procfile
  95. 95. BACK TO SINATRA
  96. 96. USEFUL LINKShttp://sinatrarb.comhttps://github.com/sinatra/sinatrahttp://rvm.io
  97. 97. == Sinatra has ended his set (crowd applauds)

×