Developing and deploying simple Web services with Ruby, Sinatra and Heroku. This presentation is code-heavy, with examples. All of the talkover detail is in the presenter notes.
@oisin
1 of 27
More Related Content
Simple Web Services With Sinatra and Heroku
1. Simple Web Services with Sinatra and Heroku
ois鱈n hurley
ois.in@nis.io
@oisin
2. Those Pesky Servers
Non-local
Non-debuggable
Non-restartable
All remote and no control
Ive been programming half the night
and now theres no-one to restart the
server. Botheration!
7. Lets Go Corkboard
Subject: xcaken
Date: 8 Feb 2011
Corkin hinder bar wearin achtung
keepin handercloppen keepin gestalt.
Lookinpeepers gewerkin waltz das
yodel auf keepin nine unter buerger.
Wunderbar er, ich heiden frau strudel
kaputt, uber wunderbar morgen,
sightseerin underbite, auf weiner ist.
https://github.com/oisin/corkboard_server_example
Mobile developers have issues - primarily clients, but clients who have servers give that extra frisson of eyeroll, as one expects to have to deal with low-motivation IT staff in a different organization. A mobile developer would much rather have some control over a test server. If you want to make progress on your code at 3am, then you should be able to do so. Creative work is not a schedulable unit. As a pure mobile developer, maybe your server side skills are a little rusty. But you don’t want to commit to lots of learn time to re-learn a skill of which you only really really need a smidgen. This talk is for you.\n\n\n
The solution, as always, is to replace the client’s IT department with a shell script. Or, better still, a Sinatra script. You can consider Sinatra to be Rails’ little brother - he hasn’t got to university yet and discovered beer and is still fairly trim. He doesn’t have the baggage of a lengthy relationship with ActionPack either and is able to focus on one thing. \n
You have a Mac. Ergo you have Ruby and RubyGems installed already. RubyGems is the way you organize various Ruby libraries. It’s far from perfect, but makes life a lot easier for the kind of thing we are trying to deliver here.\n
Once you have the RubyGems package manager installed, getting Sinatra is easy. Everything that Sinatra needs to run will be pulled in automagically.\n\nDSL is Domain-Specific Language - you knew that already, right?\n
“Speed to Hello World” is no indication of the fitness of a particular approach or technology to a large or long-term task. But! We are doing small and short-term tasks, so that’s a WIN then. You can probably infer the rest of how the DSL works just from this teeny-tiny example, however let’s get into it and put together something marginally more complicated.\n
The example I’m going to use is a ‘corkboard’. You have a corkboard (the server) and you can post notes to it (and remove them, and change them, and read them), and notes have a subject, the date they were posted and a piece of text, the note content proper. What is NOT going to be in this example is users, sessions and authentication. We might do them another time.\n
The server is going to be RESTful, meaning I’m just going to provide a set of CRUD operations on a Resource. Each note is a Resource. You have all spotted at this point that these actions map directly to a subset of the standard HTTP verbs. That’s intentional. GET will return the details of a note, given the id in the database. PUT will insert a note in the database and then respond with the id of the new note. DELETE will remove the note from the database. POST will update the note in the database with some new content\n\nEnough! Next up is some Ruby code.\n
Here we get a note with a particular id. \n1. The ‘:’ char indicates a ‘symbol’. Any symbols you put in that path up there get replaced into a dictionary called ‘params’ for use in the body the get. You can be smart about the patterns you use.\n2. ‘puts’ is just a print statement\n3. This loads a note from the database - we’re trying to match the :id to the primary key.\n4. Set the status to be a HTTP 404 code, not found!\n5. Yay, we’re good, set the status to be HTTP 200, ok.\n6. And set the body to be the JSON serialization of the note object.\nThe return just happens on exit.\n
① Deleting a note with the given id, when we receive the HTTP delete verb. This code does a get to see if the note is there in the database so it can give a better error status return. Another option would be to catch the database exception that happens when the delete fails because the key isn’t found. The ② destroy method removes the\nnote from the database.\n
1. Getting a little fancier now. This creates a note. We don’t need an id in the path, and we are reacting to the \nuse of the PUT verb.\n2. Here’s something new - we’re asking the JSON module to parse the content of the request we have just \nreceived into an object called ‘data’. This is a dynamic language, so the type of the ‘data’ is not established\nbefore execution time - in this case it’s going to be a dictionary, or hash.\n3. Checking for missing stuff - the note has to have a subject and content to be valid, otherwise we set a HTTP 400 status, Bad Request.\n4. Actually making the model object given the subject and content and adding some time recording.\n5. Saving the model object to the database.\n6. Returning the primary key of the object we’ve just added to the database so that the client can track it.\n
A post updates the an identified note, maybe changing the subject or the content or both. It will also\nupdate the ‘updated_at’ field of the note. Nothing special to call out here, except perhaps the %w structure\nin the middle of the method. %w(subject content) just gives a collection of two strings “subject” and “content”,\nand the code iterates over these to update the target note with whatever has been part of the post body. The\nscissors is a cut error by me, sorry, there used to be a log message there that got snarfed. Next: DATABASE!\n
Sinatra can use LOTS of different data sources. But that’s not something that concerns you. You do have \nsqlite3 on your mac AND you know how to muck about with it thanks to Core Data, so that’s the right thing\nto pick. We’re going to use DataMapper (another Ruby gem) to interface with it. \n1. Set up sqlite3 to point to a file in the current directory.\n2. Indicate your class is a model by just including DataMapper::Resource (easy!).\n3. You need to have a primary key in there, it will pick one called :id if it’s there, Serial means an increasing integer value!\n4. If you have :required => true, it means this field (column) can’t be null. There’s lots of modifiers like this, including defaults, key status. Check the DataMapper documentation for excruciating details.\n5. This finalize tells DataMapper that all of the models have been defined and now it’s time to nail things down.\n6. This tells DataMapper that if we make changes to the Note model and then run this again using a database that contains the *previous* Note model, then the DataMapper layer is to silently migrate the existing (old) database to put in the new information that has appeared in the changed model. This *won’t* remove stuff that has gone away from the model, so be aware of the potential of wasted columns. Not a problem for this work!\n
A quick note on JSON - your model should provide its own marshalling routines for getting a JSON serialization going. For example, the Note model code creates a simple hash which is then marshalled to json. You can see this code on the next slide.\n
The date is marshalled as an integer (to_i) to give the seconds since epoch approach representation of the date when the note was posted.\n
Application setup and invoking the web service methods is a snap, but sometimes you want to be able to invoke the service by hand to find out wtf is going on. For this, I recommend rest-client...\n
Installing rest-client means you can use the interactive Ruby shell (irb) to send requests to the server without all that tedious messing about with curl (in fact, rest-client is built on curl).\n
\n
So you have worked with you own server on your own machine now. Isn’t it great - built for comfort AND speed. At this point in time you may be considering either writing your own service to backend an app you wish to create, or perhaps you want to test how the app feels with a greater latency on the web communications. You need to deploy it somewhere - preferably somewhere easy and free. Heroku is my choice.\n
All you be needin’ is the http://heroku.com website. You also need to look at git, a source\ncode control system for distributed teams.\n
Of course, if you are deploying to a faraway place, you will need to make sure that the environment has all of the third-party pieces of code and libraries that you are using locally. The first part of getting ready for Heroku (and others) is to codify your dependencies using a tool called Bundler. You create a file called Gemfile which contains the details of the extra software you need, the specific version or version ranges, then run ‘bundle install’. This creates a file called Gemfile.lock, which contains your fully-resolved dependencies\n\n① these adapters get pulled in locally on-demand by other packages, but they need to be included here explicitly so that Heroku knows they are required. Note that Heroku runs PostgreSQL.\n
Heroku is clever about a whole load of stuff, but you still need to tell it how to start up your application. It gets run in a kind of ‘container’ so Heroku can reload it, stop it, etc. The next step is to turn your project into a git project, ready for pushing to Heroku.\n
Create a git repository for your project. What you will do with this is ‘push’ the project code to Heroku - and it will spot it and start it up. This is the basics for turning your project into a git project. Note the .gitignore file - this contains files that git is to ignore when it is attempting to commit and it contains file name patterns. In this one, I just have the line *.sqlite, to make sure that I don’t check in my database from test runs!\n
Creating the webapp on the Heroku infrastructure has two steps - 1) ask heroku to create the webapp space for you; 2) push the webapp code to the repository that got created in (1). Step 2 is on the next slide. Note here that the creation of the Heroku app gives you a new git remote repository name ‘heroku’ that you can use for interacting with its copy of the code.\n
And that’s it - installed and running. You can now visit heroku.com to look for other add-ons, like SSL, exception logging, etc.\n
A quick test to make sure that everything is working properly. If you get a 500, then use ‘heroku logs -n 100’ to get the last 100 lines of logging from the server to see what has going wrong!\n