Lets Build a Sinatra App
Featured in issue #156 of Ruby Weekly.
Lets talk a little bit about my motivation for this article. Frustration… Frustration with what you ask? Apple TV, AirPlay & watching videos from my library. Normally when I think apple, I think: beauty, simplicity, it just works… You know, the same things that come to mind when anyone thinks of apple. I also love watching Seinfeld before I hit the sack. Its how I switch gears from mad coder to heavy sleeper.
My setup is pretty simple, all my videos are stored on my time capsule. I have a Mac mini with iTunes. I connect via my iPad that sits on my nightstand. Everything has been amazing, that is, until a few months ago. I don’t know what changed but one night I fired up the videos app on my iPad, and waited for it to find the Mac mini library. It didnt… So I get up, go to the Mac mini and make sure it is connected to the network and to the shared drive. Try again, it works. I’d keep running into the same issue every night though. I mean, what good is it if it doesn’t just work? amirite?
So at this point you might be saying “I thought this was a post about Sinatra?” or maybe I already lost a few of you. So before I lose any more of you, “Let’s get the show on the road!”.
I have to make a few assumptions here in order to keep this a blog post. One is that you know what Sinatra is. If you don’t I’ll borrow this brief quote from the Sinatra site: “Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort”. The next assumption I am going to make is that you are already a developer and just want a quick jumpstart in building your first sinatra app. So you should already have ruby installed on your machine.
I’ll start by modeling the app a bit. For now just the basics. I am going to have a video object with fields like title, length, description, image & genre. I’ll need a storage location. I’ll need a way to add this info about the video, upload the video, and watch it, especially on my iPad.
A few things to note, I am developing and running this from my Mac. I believe everything should work just the same on Linux, though I’m not so sure about Windows. I will be using haml for the markup. So instead of having to type out this:
we can just type this:
Time to kick things off, we need to open terminal and install a few things. If you’re not comfortable on the commandline I’d suggest you read up on basics. You should still be able to follow along, but if your goal is to be a developer you should really have a basic understanding. If you have a few hours and about $24, a great resource is the “Meet the Command Line” videos over at PeepCode.
https://peepcode.com/products/meet-the-command-line
https://peepcode.com/products/advanced-command-line
Now we’re going to put the app structure and basic setup in place. From a directory that makes sense (in my case I have a /Users/mweppler/developer/projects directory that I keep all my projects in) run the following:
mkdir -p the-video-store the-video-store/public the-video-store/public/images the-video-store/public/javascripts the-video-store/public/media/image the-video-store/public/media/video the-video-store/public/stylesheets the-video-store/views the-video-store/media/image the-video-store/media/video
touch the-video-store/config.ru the-video-store/config.yml the-video-store/Gemfile the-video-store/video_store.rb the-video-store/views/index.haml the-video-store/views/layout.haml
You should now have the following structure:
.
├── media/
│ ├── image/
│ └── video/
├── public/
│ ├── images/
│ ├── javascripts/
│ ├── media/
│ │ ├── image/
│ │ └── video/
│ └── stylesheets/
├── views/
│ ├── index.haml
│ └── layout.haml
├── Gemfile
├── config.ru
├── config.yml
└── video_store.rb
11 directories, 6 files
Raise your hand if you know what a “Gemfile” is. Ok now put your hand down. Since I couldn’t see if you had your hand raised or not I’ll just assume you did and proceed with adding some gems to our Gemfile. Note: If you don’t know what a gem file is you’ve got some more reading to do pal. Start by checking out the Bundler site.
…and from the commandline, run bundler:
bundle install
I want to get this basic app up and running quickly so we can iterate through the functionality. Normally I’d suggest you follow TDD/BDD practices but for something this basic I want to focus on sinatra. Add the following to your config.ru file:
I am going to add code to the config.yml file to store things like the media location, and supported file types:
Before we can use some of these gems we’ll have to require them. In our main app file video_store.rb add the following:
What is that “Hash” stuff above? Well the short answer is: It enables us to easily turn the config file settings into Objects. That allows us to use syntax like this:
Instead of this:
Note: this is just a preference of mine.
Continuing in the video_store.rb file, we add the database and DataMapper. Now lets add the sqlite database:
Our video class is going to need access to some DataMapper methods so well need to include it in our models:
That was easy! Our attachment class (which will also go in video_store.rb) will have a bit more logic in it since it will handle the file uploads:
Now that we’ve declared our models, we have to “finalize” them and create the tables. We do that with the block of code below:
We need to set the content type header to “text/html” in order for the page to render correctly in the browser. So add the following:
Up to this point, we’ve been adding a bunch of “backend” code. Now we are going to start write code that we can actually see in the web browser. This is an initial route, which can be added to the video_store.rb file:
This tells sinatra that if the user visits our site http://localhost:9393/ we want to render the index view. Lets add some text to our index view. Open views/index.haml and add the following:
I want to finish off this first sprint by building out our layout (in the layout.haml file):
All of our views will be inserted inside this layout with the yield statement. That was a lot of work! Where is the payoff? Just run the following:
bundle exec shotgun
…and open http://localhost:9393/
This step is optional, but I think we should put the project under version control:
touch .gitignore
In this .gitignore I add the following lines:
development.db
/media
/public/media
…and commit
git init
git add .
git commit -m "Initial commit"
Yay! Take one hand and put it way up in the air. Now with your other hand give yourself a big high five!
Ok, lets not marvel at the wonder of the h1 welcoming us. No, lets push ourselves a bit further. Lets add the ability to actually upload a movie. So lets create another route:
Now lets create the view which is basically a form. Within the views directory create a file ‘new.haml’ and add the following:
Create another view to show our success message ‘create.haml’ and add a simple:
Fire it up with good ol ‘bundle exec shotgun’ and test. Works for me! Lets commit.
Before I go any further I’d like to add a touch of style to the video store. So lets add our application stylesheet:
touch public/stylesheets/application.css
I also was looking to try Yahoo! Pure so lets do that now. So I am going to refactor the layout.haml file a bit:
I added the links to both our application.css file and the pruecss file, also a nav and main section. Looks pretty good, but I want to center the page in a 960px container. So in application.css add the following:
Now lets clean up the upload form. Update the new.haml file with:
Now that that’s out of the way lets add a way to view videos that we’ve uploaded. In other words lets add a /video/list route in video_store.rb:
…and list view:
touch views/list.haml
…and lets add a link to it in our layout.haml:
Make this list.haml view pretty:
Ok, one last step. Lets add a way to get all the video details as well as an option to watch them! So lets add the route in video_store.rb:
…and we’ll add a link in the list view list.haml. We’ll make the title the link element so update the code:
to:
…and we’ll need the show.haml view:
touch views/show.haml
For now we’re just going to add the same elements from the list view, only we’ll remove the each iterator. So open views/show.haml:
…and add a watch link. Wait a watch link? Yeah a watch link:
So does this mean we’re going to need a route and view as well? Umm, yeah sure. Can you handle that?
So thats the link. What about the route:
…and the view:
touch views/watch.haml
…and we make it look nice (but not really…):
Wow that was a lot of work. Its time for another high five and commit! It’s also time for me to cut the cord, or kick you out of the nest. Get some copy/paste action going on, and add in a touch of google. I think you can take it from here and continue building on top of this project.
You can double check your work with the project I have hosted on github