9780596521424
rails.html

Chapter 2. Rails in a Nutshell

Ruby on Rails is a framework designed for quickly and cleanly writing web applications. Extracted from 37signals' project management tool Basecamp, creator David Heinemeier Hansson wrote Rails with solutions to common real-world problems already in hand. These solutions and decisions are what makes Rails a pleasure to use – the boring, menial chores are taken care of for you, leaving you to concentrate on your own problem instead of the infrastructure around it.

To be specific about the boring parts: we’re talking about deciding on a directory structure, caching layers, data persistence, templating systems, testing frameworks, mailing, routing, and all the other components that you need in almost every web application. Rails comes with the full stack of components you need to get your application online, and they’ve all been hand-written to work together. Gone are the days of hoping the templating language you found works with the complex object relational mapping (ORM) layer. Basic security concerns around SQL injection, XSS and CSRF attacks are all taken care of by default. Support for AJAX calls comes baked-in. Need to expose an API? Rails makes it doable in a few lines. It all just works.

You can also forget about writing reams of tedious XML configuration for components like databases. Rails is all about convention over configuration – keep configuration to a minimum by doing the obvious. If there's a class called Animal that models animals, and there’s a database table called animals, why should you be writing configuration files that specify that they link up? Rails does that for you.

Rails gets out of your way. It doesn’t surprise you. It doesn’t make you repeat yourself. It makes web development fun.

Architecture

Rails applications are developed using the Model View Controller (MVC) pattern. The easiest way to show how Rails uses MVC is by looking at a simplified example of the Rails request lifecycle as seen in Figure 2.1, “MVC in action: the Rails request lifecycle”.

Figure 2.1. MVC in action: the Rails request lifecycle

MVC in action: the Rails request lifecycle

Here’s an example user scenario: let’s imagine that your grandmother wants to purchase a book on tribal tattoos on an online store. Let’s break down the steps of visiting the front page of the store in the context of the MVC pattern:

  1. Your grandmother fires up her web browser and visits the online store. The web browser hits the web application with a request for the front page. This request arrives at a Controller, whose job it is to determine what needs to be done in order to fulfill the request.

  2. The Controller needs to fetch some data from the database about tribal tattoo books, so it asks a book Model, whose job it is to communicate with the database, to get representations of tribal tattoo books.

  3. The book Model fetches data by making SQL calls to the database to fetch records and returns instances of book representations to the Controller. If need be, the book Model is also responsible for data validation.

  4. The Controller needs to package up the response to your grandmother’s web browser in the HTML format that it asked for, so it passes the book Model instances and anything else needed to a View to render into HTML.

  5. The View combines the book Model instances with a template to generate an HTML page and returns it to the Controller.

  6. The Controller gives the rendered HTML page back to the Client as a response.

This approach to organizing application components makes for readable and maintainable projects as it’s predictable where each piece of code should go and can be found: Models deal with interactions with the database and business logic (typically the bulk of your application), View templates are where HTML and the like goes, while Controllers are the thin layer coordinating the two. Without this separation of concerns, you end up with SQL in your HTML, caching code all over, and a general mess on your hands, making writing new features an exercise in masochism.

Another pattern that permeates Rails’ design is Representational State Transfer (REST), an architectural idea of how a well-designed web application should behave. It defines a web application as a collection of resources accessed through uniform, predictable URIs and HTTP verbs like GET, PUT, POST, and DELETE, and returning a representation of a resource that transfers the client (a user’s web browser) into a certain state. This approach frees you from having to make architectural decisions about the layout and composition of your application and its API. We’ll see later about how this idea is what lets Active Resource work so well.

Note

A great tutorial on REST is http://www.xfront.com/REST-Web-Services.html

Model

A model contains business logic – whether or not to calculate something, send a happy birthday notification, or launch a space ship. To make those business decisions and take those actions, it often depends on accessing a database, so a model is also responsible for creating, retrieving, updating, and deleting the data it represents. It’s also tasked with ensuring data integrity by preventing invalid entries from being entered into the database.

Rails makes describing all that logic easy with a component called Active Record that lets you work in Ruby instead of the typical mess of database configuration/connection and SQL voodoo required for creation, retrieval, updating, and deletion (CRUD). Here are some examples of what your code could look like.

# create a record
book_author = Person.create(:name => "Edward", :nationality => "Canadian")

# retrieve records
canadian_authors = Person.where(:nationality => "Canadian")

# update a record
book_author.name = "Edward Ocampo-Gooding"
book_author.save

# delete a record
book_author.destroy

Because of how Rails is set up, you can do this sort of thing anywhere in your application – in any model, controller, or view – your models are always accessible like this.

Instead of making you manually specify which classes map to which database tables, Active Record just looks at the names used in the schema – it makes sense that a table called animals should map to a class Animal, so Active Record does that for you. It also handles pluralizations like people mapping to a class Person.

Setting up associations detailing what belongs to what and has many or one of something else is also dead-simple:

class Person < ActiveRecord::Base
  belongs_to :family
  has_one :free_time_activity
  has_many :interests
end

After setting up these associations, writing code like this becomes natural:

edward = Person.where(:name => 'Edward Ocampo-Gooding')
edward.interests.create(:name => 'Open Data')

Active Record also takes the intuitive step of providing methods on model objects that match the table’s columns. If the people table has a column called gray_hairs, then you automatically get accessors for that column by the same name:

john = Person.where(:nationality => 'American').first

puts john.gray_hairs  # The Person#gray_hairs method appears automatically
john.gray_hairs = 9   # The Person#gray_hairs= method also appears automatically
john.save

Active Record is also flexible enough to bend when you need it – if automatic table-class mapping doesn’t work for a situation, you can specify the details manually. If a complex search written in Ruby isn’t optimized enough, dropping to SQL is painless.

There’s more beyond that – Active Record comes with common validations to ensure data integrity and let you skip having to write the same code to check if a field is numeric, of a certain size or format, just plain required, or other common situations. In other words, you can do this:

class Animal < ActiveRecord::Base
  validates :known_population, :numericality => true
  validates :biological_name, :uniqueness => true, :presence => true
end

and the model won’t save unless those validations pass, with the added bonus of automatically generating error messages. More validations are available out of the box, and writing your own is easy.

There’s lots more to Active Record that makes your work as a developer quick and to the point: it has rich support for callback hooks throughout the CRUD object lifecycle, and observers to simultaneously listen for callback events on multiple models. Transactions are built-in and straightforward. Database schema migrations to help keep everyone on the team on the same schema are concise and easy to read.

Active Record supports a variety of database backends, including SQLite, MySQL, Postgres, Oracle, Microsoft SQL Server, Sybase, and DB2.

Check out Chapter 4, Active Record for more on how Active Record can make your life easier.

View

The View layer is what the user of your application sees and touches. It usually entails rendering model data and web forms, but could also be rendering XML for an RSS reader or JSON for some custom API.

Rails delivers this presentation level of the stack with a component called Action View. It comes bundled with a templating system called embedded Ruby (ERB) that works great for HTML output – if you’ve used PHP, ASP, or JSP, you’re already familiar with the syntax:

<h1>All People</h1>
<ul>
<% Person.all.each do |person| %>
  <li><%= person.name %> is having a birthday 
    in <%= distance_of_time_in_words_to_now(person.birthday) %></li>
<% end %>
</ul>

Any Ruby in <% … %> tags is evaluated but not displayed in the output. Ruby inside <%= … %> is evaluated and is also rendered in the output. The above would render HTML looking something like this:

<h1>All People</h1>
<ul>
  <li>Edward is having a birthday in 11 days</li>
  <li>James is having a birthday in 11 days</li>
  <li>John is having a birthday in 156 days</li>
  <li>Cody is having a birthday in 90 days</li>
</ul>

The distance_of_time_in_words_to_now method comes with Action View – it’s one of the many convenience methods you’ll find making interfaces easy and quick to write. Other similar methods exist for time, dates, links, forms, text, and produce clean HTML5. You’ll also find that the output has been made easy to hook into via unobtrusive JavaScript techniques often used by Rails developers to make their applications feel snappier and look snazzier.

Action View also comes bundled with Builder, a tool for easily producing hierarchial output like XML. We’ll see it in action later on in this chapter, producing an Atom/RSS output.

See ??? for more on Action View.

Controller

Action Controller orchestrates the Rails request lifecycle: it responds to HTTP requests, gets data and decisions from models, then renders a view. To make running the show easier, Action Controller provides automatic logging, benchmarking, caching, session and cookie management, as well as other interesting features like filters, which run code before or after actions, often acting as ways to easily introduce things like authentication.

Action Controller also intelligently knits together endpoints, or actions with the corresponding view of the same name. Here’s an excerpt from a typical controller to show you what that means:

class PeopleController < ApplicationController
  before_filter :ensure_person_is_authenticated
  
  def index
    @people = Person.all
    
    respond_to do |format|
      format.html # index.html.erb
      format.json  { render :json => @people }
    end
  end

  # ...
  
end

When handling a request for a URI like http://some-app.com/people/index.html, the PeopleController#index method (also called an action because it handles a request) will look for a view template by the same name and request format: index.html.erb

What’s even more cool is that Action Controller can also respond to different request formats with the same action: a URI like http://some-app.com/people/index.json will be responded to by the above example code with a JSON representation of the same thing the HTML view rendered. That means writing an API for your application doesn’t have to involve a whole bunch of other special controllers and logic. Just reuse your existing code by adding “.json” to the end of your URIs, render the same object you’d otherwise pass to your HTML view in JSON, and you’re done.

For more on Action Controller, see Chapter 5, Action Controller.

Note

Action View and Action Controller are together known as Action Pack.

Mailer

Action Mailer makes sending templated email a breeze. Because Rails treats sending email very similarly to rendering a regular HTML/ERB view, you’ll find yourself already knowing how to use it by the time you need it.

Action Mailer also lets you to easily process incoming email, turning typically complex jobs like turning email sent by users into comments, inventory updates, or new profile pictures into a straightforward and simple procedure.

Read more about Action Mailer in Chapter 11, Action Mailer.

Web Services

Rails includes a component called Active Resource which lets you integrate your application with other web services like those offered by Shopify, Twitter, Pivotal Tracker and Lighthouse via a RESTful API. Instead of having to figure out various HTTP calls and how to build and parse JSON and XMLrequests and responses, Active Resource takes care of all that for you, letting you work with other web services like they were just another model in your application.

Find out how to work with Active Resource in Chapter 12, Active Resource.

Plugins

It’s easy to modify Rails to get it to do what you want if it doesn’t already. For that reason, there are a ton of well-maintained plugins and Ruby gems available written by the community making everyday tasks like file uploads and image resizing as easy as a one-line install.

If installing one of the many out there already doesn’t work for you, we’ll show you how to write one in Chapter 15, Plugins.

Getting Started

To give you a feel of how quickly and elegantly you can develop web applications with Rails, let’s build a video jukebox.

This application is going to

  • Accept videos from around the web that our friends think are hilarious, touching, or otherwise awesome

  • Shield against vandalism with data sanitization

  • Validate video submissions so none have missing fields

  • Offer syndication through an Atom feed to let RSS readers subscribe to the list of submitted videos

  • Accept comments

  • Feel snappy with some AJAX

To keep it simple, we’re going to assume the video is already up on the web somewhere, so its HTML embed code will be used instead of actually storing the video.

If you don’t have Rails installed, see the Appendix for instructions.

Generate a Rails application

Begin by creating a skeleton Rails application:

$ rails new video_jukebox

Take a look at the application’s directory tree and the typical files and configuration needed in every Rails application:

app/

All application-specific code lives in this directory.

assets/

This is where your assets go: images, stylesheets, and javascripts are the usual suspects.

controllers/

This is where your controllers go. We’ll put videos_controller.rb and comments_controller.rb here later.

helpers/

Holds view helpers: collections of methods used to simplify your views. Consider encapsulating any complex logic used in a view into a helper method. Helpers we’ll create later are videos_helper.rb and comments_helper.rb

mailers/

Classes used for email generation go here.

models/

Where your business logic goes. video.rb and comment.rb will appear here.

views/

View templates that render controller actions like videos/index.html.erb go here.

layouts/

Layouts render view templates inside of them like a loving embrace. This tutorial will use just the one application.html.erb

config/

Configuration for your application, database, routing, and other components.

db/

The database schema of your application, schema.rb, is here.

migrate/

Migrations (incremental database schema changes) are placed here. We’ll write a few during this tutorial.

doc/

Documentation for your application generated by the rake doc:app command is placed here.

lib/

Code that doesn’t belong in a model, controller, or helper belongs in a library module or class. This directory is in the load path so anything placed here can be included or required in the rest of the application.

tasks/

Automate monotonous or complex tasks that should be automated by creating rake task recipes here. If you’re used to a ball of shellscripts used to periodically collect statistics from your database, bill customers, back up data, etc., then stop and use a custom rake task instead.

log/

Logs for your application are written here. Each environment (typically development, testing, and production) will have its own logfile.

public/

Static files like 404.html and 500.html go here.

script/

Executable scripts that don’t make sense as rake tasks go here. Rails creates several by default like server which we’ll use in a moment to start the application server.

test/

Tests and associated files for asserting the behaviour of your models, controllers, and the application in general belong here.

tmp/

Temporary files for tracking caches, pids, sessions, and sockets are kept here.

vendor/

External libraries that the application depends on. Like lib/, this directory and its children are in the load path.

assets/

Assets (stylesheets, etc.) belonging to plugins go in here.

plugins/

Plugins installed to extend the functionality of your app go here.

config.ru

Used to start the Rails app – you typically won’t be having to mess with this file.

Gemfile

Used to list the gem dependencies of your app. Gemfile.lock is used in conjunction to keep track of which gems are currently installed. You really want to make sure both of these are checked into version control and kept updated.

Rakefile

Used to load tasks you define in lib/tasks/*.rake – these tasks are really handy for running Cron jobs with to run regular operations on your app, like nightly mail or culling of the database. Note that you typically won’t have to modify this file.

README

Be kind to others and your future self – delete what’s there and fill it in meaningfully with sections that tell you what this app is, who’s responsible, how to get started, and how/where to contribute and ask questions.

Start up your app

Take a look at what your application looks like by starting up its server:

$ cd video_jukebox
$ rails server
=> Booting WEBrick
=> Rails 3.1.0 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2011-09-18 02:57:11] INFO  WEBrick 1.3.1
[2011-09-18 02:57:11] INFO  ruby 1.8.7 (2011-06-30) [i686-darwin11.1.0]
[2011-09-18 02:57:11] INFO  WEBrick::HTTPServer#start: pid=61839 port=3000

With the server running in a terminal, visit http://localhost:3000 in a web browser. If everything is working correctly, you should be looking at a web page with some suggestions on how to start writing your application as seen in Figure 2.2, “If you can see this, Rails is correctly installed.”.

Figure 2.2. If you can see this, Rails is correctly installed.

If you can see this, Rails is correctly installed.

Generate a scaffolded resource

Follow the steps suggested in the browser and start by changing directories into the project and creating a video resource with a title and HTML embed code attributes:

$ rails generate scaffold Video title:string embed_code:text

Running just this one command generates all we need for a working video resource. Let’s take a look at some of the files just created:

db/migrate/20100129040614_create_videos.rb

A script to alter the database schema to accommodate the new Video model. Scripts like these are called migrations. Don’t worry if your filename is slightly different – that’s just a UTC timestamp in the name to ensure the migrations are named differently.

app/models/video.rb

The new Video model.

test/unit/video_test.rb

A suite of unit tests for the Video model.

test/fixtures/videos.yml

A configuration file to keep test data used to populate the videos table for use in testing.

app/controllers/videos_controller.rb

The controller for your video resource.

app/views/videos/index.html.erb

The view used for listing all videos. The VideoController uses it when responding to http://localhost:3000/videos/ and http://localhost:3000/videos/index

app/views/videos/edit.html.erb

The view used for editing a single video. The VideoController uses it when responding to a request like http://localhost:3000/videos/1/edit

app/views/videos/show.html.erb

The view used for displaying a single video. The VideoController uses it when responding to a request like http://localhost:3000/videos/1

app/views/videos/new.html.erb

The view used for displaying a form used to create a new video. The VideoController uses it when responding to a request like http://localhost:3000/videos/new

app/views/videos/_form.html.erb

A view partial used in other views. It encapsulates some template content that other views like app/views/videos/new.html.erb and app/views/videos/edit.html.erb both use.

test/functional/videos_controller_test.rb

A test suite for the VideoController that asserts expected behavior. For example, one of the default tests checks that when a HTTP GET is made for the /index path on the VideoController, videos are fetched from the database and the response is successful.

app/helpers/videos_helper.rb

A place to put methods that encapsulate code used in your video resource views. Any code in the VideosHelper module found within videos_helper.rb is available in all video views.

test/unit/helpers/videos_helper_test.rb

A test suite for the code in the VideosHelper module.

app/assets/javascripts/videos.js.coffee

CoffeeScript specific to the videos resource. CoffeeScript is “an attempt to expose the good parts of JavaScript in a simple way” (http://jashkenas.github.com/coffee-script/) – it’s a language that compiles down to JavaScript and is nice to write in, just like Ruby. Rails automatically pre-processes it for you, sending compiled JavaScript on the way out.

app/assets/stylesheets/videos.css.scss

SCSS specific to the videos resource. SCSS adds nested rules, variables, mixins, selector inheritance, and other niceties to CSS, making it easier to work with. Rails automatically pre-processes it for you, sending compiled CSS on the way out.

app/assets/stylesheets/scaffolds.css.scss

A stylesheet of default styles. This and videos.css.scss is processed and rendered in app/views/layouts/application.html.erb by way of the stylesheet_link_tag method call.

Running database migrations

The scaffold generator also created a database migration to update the database schema with the new table and fields the video resource will use. Take a look at it in db/migrate/20100131231323_create_videos.rb :

class CreateVideos < ActiveRecord::Migration
  def change
    create_table :videos do |t|
      t.string :title
      t.text :embed_code

      t.timestamps
    end
  end
end

Instead of editing the routes as recommended in step 2 of the “Welcome Aboard!” screen at http://localhost:3000, skip to step 3 and run the migration the scaffold generator created by typing rake db:migrate

The results should look like this:

$ rake db:migrate
==  CreateVideos: migrating ===================================================
-- create_table(:videos)
   -> 0.0022s
==  CreateVideos: migrated (0.0023s) ==========================================

Running the migration creates a SQLite3 database for you in db/development.sqlite3 and creates a table called "videos" with the schema we specified earlier.

Let’s take a deeper look at the CreateVideos migration and at what just happened.

Active Record migrations define a change method that specifies some changes to the schema, or some code to run to put the local database schema into a certain version. By versioning your database schema via these migrations, you gain the same benefits that you get from using a source control system:

  • A history of how the database got into the shape it’s in now

  • Your colleagues can run the migrations you check into version control and wind up with the same schema and database state as you. (No more passing around giant balls of SQL and hoping that it works.)

  • Rollbacks are automated, assuming simple, non-destructive operations

  • As long as you’re not dropping down to SQL and use functionality specific to a database engine, the operations are database agnostic, so swapping out SQLite3 for MySQL later on is not a big deal

The typical thing you’ll be doing is migrating up a version, which is what just happened when you ran rake db:migrate. In this case, it created a new table videos with a column title of type string (usually 255 chars, but the specifics depend on the database being used) and a column embed_code of type text. Timestamp columns created_at and updated_at are also created in the videos table – these column names are special and when detected by Rails, are set accordingly when a model that maps to that table is created or updated.

You can also specify self.up and self.down methods in a migration instead of change to instruct Rails what to do when migrating up or down specifically. Migrations are also great for flexible bulk data inserts, deletes and updates. There’s also a built-in mechanism in Rails for cleanly seeding your database with initial data. Learn more about this sort of stuff in Chapter 4, Active Record.

Note

By default, Rails uses a built-in SQLite3 database adapter, so installing a database engine to use Rails and Active Record is not strictly required. Rails also has support for databases like Postgres, MySQL, Oracle, DB2, but requires the installation of Ruby bindings. See the Active Record chapter for more on configuring database adapters and bindings.

Check out your working application

At this point, we can start playing around with the application. Believe it or not, we’re done with the hard parts of writing this application.

Make sure your application server is up by running

$ rails server

then visit http://localhost:3000/videos to see a page like Figure 2.3, “Rendered output of the VideosController#index action”.

Figure 2.3. Rendered output of the VideosController#index action

Rendered output of the VideosController#index action


Click on the New video link and start playing around with the app to see it working already.

Make sure to watch the URIs in your browser’s location bar and the log messages coming from the terminal you started the application server in, or by tailing tail -f log/development.log – you should be seeing messages like POST "/videos" followed by Processing by VideosController#create as HTML

Note

If your application doesn’t work, the fastest way to debug it is by looking at the application log in log/development.log – Rails reports its inner workings in detail here and in the terminal the application server is run in with colorized output. See the Appendix for more on the logging services built into Rails.

Setting up a default route

With that said, let’s finish up this app. A good next step would be to make it so that when you visit http://localhost:3000/, the application routes to the VideosController#index action and you see the same view when you visit http://localhost:3000/videos

Set this up by adding the following under the resources :videos line in config/routes.rb:

root :to => "videos#index"

Next, delete public/index.html:

$ rm public/index.html

After you’ve done that, reload http://localhost:3000 and you should be looking at your videos/index page.

If you’re still looking at the same “Welcome aboard” page, make sure you’ve deleted public/index.html – everybody makes this same mistake.

Curious about how that public/index.html file works and why if you don’t delete it, it’ll always be served up instead of anything you’ve set a default route for? Any path that resolves to a file under public/ will be served up before Rails checks its routes. This is also how pages like http://localhost:3000/404 and http://localhost:3000/500 render without having to depend on a working Rails setup – just take a look at public/404.html and public/500.html.

Note

Rails is able to skip the usual request handling lifecycle when requests come in for static files in public/ by using something called middleware, which inspects requests as they come in. Take a look at Chapter 5, Action Controller for more on the request lifecycle and middleware.

XSS protection

Let’s add a video via its HTML embed code and have it render correctly.

Visit http://localhost:3000/ and create a new video record. Set its title to “First thing I could think of” and grab the HTML embed code from the first video you see from http://vimeo.com or your favourite online video site. (There should be a button somewhere on the video site that says “embed”.) The embed code should look like this:

<iframe width="425" height="349" src="http://www.youtube.com/embed/QH2-TGUlwu4"
   frameborder="0" allowfullscreen></iframe>

After creating the video, you can see that it was created, but the video embed code isn’t rendering correctly. This is because Rails escapes all dynamically rendered text in views to prevent cross-site scripting (XSS) attacks where some external JavaScript does something nasty to your users.

We want to display this embed code, but don’t want to open up a XSS exploit vector. We can do that by just making sure only a few kinds of HTML tags can be used when we render a video’s embed code, like <iframe> – anything else like a script tag that could contain malicious JavaScript should be HTML-escaped so that it won’t be evaluated by the browser as executable code.

This functionality is built-into Rails in the form of Action View’s sanitize helper – it lets you specify certain HTML elements and attributes to pass through and disables escaping on the rendered output.

Open up and edit the view used to render single videos, also known as the video show page: app/views/videos/show.html.erb

Change

<p>
  <b>Embed code:</b>
  <%= @video.embed_code %>
</p>

to

<p>
  <b>Embed code:</b>
  <%= sanitize @video.embed_code, :tags => ['iframe'] %>
</p>

Refresh the video show page you’re looking at (e.g., http://localhost:3000/videos/1), to see your app safely rendering the video embed code like in Figure 2.4, “Baked-in XSS protection thanks to data-sanitization by default is nifty”.

Figure 2.4. Baked-in XSS protection thanks to data-sanitization by default is nifty

Baked-in XSS protection thanks to data-sanitization by default is nifty

Nice work! It looks like this application is almost done. Before we move on to validation, comments, RSS, and AJAX, let’s poke around our application to make sure everything is working correctly.

Try visiting videos/index at http://localhost:3000/

Hmmm… it looks like the video isn’t rendering correctly. Open the view used to render this URI (app/views/videos/index.html.erb) and look at this line:

<td><%= video.embed_code %></td>

While it would be easy to just change it to the sanitized version we used earlier, having to use the same view code in two places doesn’t feel right. If we realized some element in the sanitization call was missing, we might forget to change it in both places and cause a bug to slip in. We need to encapsulate the video embed code sanitization.

View helpers

This sort of functionality fits nicely in a helper: a method used in views to encapsulate complex logic and clarify what’s happening in the view. Open up app/helpers/videos_helper.rb and change it to look like this:

module VideosHelper
  def sanitize_embed_code(video)
    sanitize video.embed_code, :tags => ['iframe']
  end
end

Now we can use it in both app/views/videos/show.html.erb and in app/views/videos/index.html.erb

Change app/views/videos/show.html.erb to look like this:

<p>
  <b>Embed code:</b>
  <%= sanitize @video.embed_code, :tags => ['iframe'] %>
</p>

and app/views/videos/index.html.erb to look like this:

<p>
  <b>Embed code:</b>
  <%= sanitize_embed_code @video %>
</p>

Note

By default, the VideosHelper module is included in all views rendered by the VideosController – there’s no extra code or configuration needed. Rails makes this inclusion based on the module name, so if you create a WhatevsHelper, it will be included in views rendered by a WhatevsController. This is a great example of how Rails conventions save you from extra configuration. For more on helpers and Action View, see ???.

Refresh your browser and make sure everything is working. If you’ve got a bug, look at the end of the application log in log/development.log for clues, or tail -f it as you run it.

Model validations

While playing with submitting videos, you might have noticed that it’s possible to create a video without a title or embed code. That’s not good. We need some validation to prevent creating video entries in the database with missing titles and/or embed code.

The good news is that Active Record models come with all sorts of validation code to handle common situations like this one. In fact, we can handle this requirement by adding just this one line to the top of the Video model in app/models/video.rb:

class Video < ActiveRecord::Base
  validates :title, :embed_code, :presence => true
end

Try submitting a video without a title or embed code and you can see the validation we just added in action, showing an error message and a red outline around the missing elements that need to be fixed. Not bad for just a single line of validation!

We should also validate the embed code. For now, let’s just check that the code contains the beginning of an embed tag.

Change the Video model in app/models/video.rb so that it looks like this:

class Video < ActiveRecord::Base
  validates :title, :embed_code, :presence => true
  validate :must_have_valid_embed_code
  
  def must_have_valid_embed_code
    unless embed_code.include?("<iframe")
      errors.add(:embed_code, "Must include an iframe tag")
    end
  end
end

The validate method takes the name of an instance method and runs it during validation, making it really easy to write custom validation. In this case, the method must_have_valid_embed_code adds an error to the instance of the model on the embed_code attribute with a message about it needing to include an embed tag.

Try creating a new video with obviously invalid embed code with this custom validation in place and see what happens.

Rails also comes with other common validators like length_of, numericality, format which ensures some text follows a certain format or pattern like an email or phone number field, confirmation for when you want a password to be entered twice, and acceptance for when you need a terms of service to be accepted. Find out more about validations in Chapter 4, Active Record.

RSS feeds & request formats

We’re almost done writing our application. The next step is to open up the videos resource to RSS readers.

An RSS reader is just like a regular web browser, except instead of asking for HTML, it asks for a special blend of XML called Atom. So in order to support RSS readers asking for videos, we just need to modify the VideoController to respond to requests for the RSS format and content-type.

Fortunately for us, this is easy to do in Rails. In fact, your application already supports asking for JSON representations of resources like videos/index.

Give it a shot by visiting http://localhost:3000/videos.json and looking at the source. It should look like this:

[{
    "embed_code": "<iframe width=\"425\" height=\"349\"
      src=\"http://www.youtube.com/embed/QH2-TGUlwu4\" 
      frameborder=\"0\" allowfullscreen></iframe>",
    "created_at": "2011-10-05T19:16:59Z",
    "title": "The first thing I could think of",
    "updated_at": "2011-10-10T01:04:55Z",
    "id": 1
}]

There’s no magic here; the respond_to block in the index method tells the action to respond to various formats in different ways. When presented with a request for the videos/index resource in HTML format, it uses the format.html method, which by default renders the index.html.erb view template.

When a request for videos/index in JSON format appears, it uses the format.json method, which in this case has a block passed to it. Inside this block is a call to render the @videos instance variable in JSON, which happens internally by calling @videos.to_json

Note

The Rails scaffold generator adds an JSON format of views by default in order to give you an RESTful API for your application out of the box. Find out more about how you can use Active Resource to hook up to APIs like this in Chapter 12, Active Resource.

With that in mind, in order to add an RSS feed to the videos/index resource, all we have to do is add another method like format.html or format.json. An easy format for handling RSS feeds is Atom – it’s the simplest specification for RSS feeds to implement and works for what we need to do.

Change the VideosController#index method in app/controllers/videos_controller.rb to look like this:

def index
  @videos = Video.all
  
  respond_to do |format|
    format.html # index.html.erb
    format.json { render :json => @videos }
    format.atom
  end
end

The format.atom method tells the controller to render the matching view template for the action and format requested.

While we could create a view template index.atom.erb and use ERB to generate the output we want, it’s easier to write the view template using Builder, a templating system like ERB, but better suited to forming XML-like documents like Atom. To specify that the view template uses the Builder system, just name the file with a final postfix of .builder instead of .erb

Create the view template app/views/videos/index.atom.builder with the following:

atom_feed do |feed|
  feed.title("Awesome Videos")
  feed.updated(@videos.first.created_at)
              
  @videos.each do |video|
    feed.entry(video) do |entry|
      entry.title(video.title)
      entry.content(sanitize_embed_code(video), :type => 'html')
      
      entry.author do |author|
        author.name("The Awesome Video Group")
      end
    end
  end
end

Visit http://localhost:3000/videos.atom with an RSS reader to see the new video index feed. Figure 2.5, “An RSS reader happily reading videos/index” is an example of what it could look like.

Figure 2.5. An RSS reader happily reading videos/index

An RSS reader happily reading videos/index


View layouts

Having an RSS feed is great, but it’d be better if it was auto-discoverable by web browsers, giving the user that little “here be RSS” glowing button in the URL like in Figure 2.6, “Safari showing an RSS feed waiting for the taking”.

Figure 2.6. Safari showing an RSS feed waiting for the taking

Safari showing an RSS feed waiting for the taking

Making RSS feeds be auto-discoverable is done by adding

<link href="/videos.atom" rel="alternate" title="ATOM" type="application/atom+xml" />

inside the <head> section of each HTML page served up by this application. Rails makes remembering and using these sort of links even easier by providing a view helper that lets you write

<%= auto_discovery_link_tag :atom, videos_path(:atom) %>

but where does it go in order to be in the <head> of each HTML page?

All view templates we’ve looked at have only dealt with content specific to the controller action they were rendering, but where’s the bigger template where the rest get rendered inside of? That bigger template is called a layout template, and in this case, it’s in app/views/layouts/videos.html.erb and looks like this:

<!DOCTYPE html>
<html>
<head>
  <title>VideoJukebox</title>
  <%= stylesheet_link_tag    "application" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body>

<%= yield %>

</body>
</html>

The Action View component covered in ??? goes into greater detail about each method used here, but the general idea is that <= yield %> is where rendered HTML view template code appears, and that each controller uses the layout it’s named after (in this case, videos.html.erb) by default.

We want an auto-discoverable RSS link to appear in the <head>, so it’s as simple as inserting the auto-discovery link at the bottom of the <head> section so that app/views/layouts/videos.html.erb looks like:

<!DOCTYPE html>
<html>
<head>

  ...

  <%= auto_discovery_link_tag :atom, videos_path(:atom) %>
</head>

...

Refresh http://localhost:3000/videos and if your browser supports it, you can see a syndication icon appear like in Figure 2.6, “Safari showing an RSS feed waiting for the taking”.

Routes

Curious about how all this is working under the hood?

To find out which paths are handled by which controllers’ methods, run rake routes

$ rake routes

    videos GET    /videos(.:format)          {:controller=>"videos", :action=>"index"}
           POST   /videos(.:format)          {:controller=>"videos", :action=>"create"}
 new_video GET    /videos/new(.:format)      {:controller=>"videos", :action=>"new"}
edit_video GET    /videos/:id/edit(.:format) {:controller=>"videos", :action=>"edit"}
     video GET    /videos/:id(.:format)      {:controller=>"videos", :action=>"show"}
           PUT    /videos/:id(.:format)      {:controller=>"videos", :action=>"update"}
           DELETE /videos/:id(.:format)      {:controller=>"videos", :action=>"destroy"}

Note how HTTP verbs are a big deal here – the same /videos path routes to a different action depending if it’s a GET or a POST. This is part of the idea behind RESTful routing. The other important thing to see here is that the paths are predictable and obvious. For example, a HTTP DELETE sent to /videos/1 is a request to delete the video record with id 1.

Note

Public controller methods routed to handle requests are called actions. The connection between a path and a controller’s action is called a route.

The :id parts in the paths are placeholders for a record id. Try creating a video and you’ll see that you can visit http://localhost:3000/videos/1 to see it again.

Your application’s routes are configured in config/routes.rb – open it up and take a look at how much code we needed to set up these routes. Actually… just look at this one line the scaffold generator added, because that’s all it takes:

resources :videos

This line is a method call that sets up RESTful routing for the videos resource, where a resource is a term borrowed from REST literature to describe a source of information with a universal identifier and standard interface like the videos URIs in the routing table. REST terminology also dictates that the standard interface be able to exchange representations of the resource. In other words, when you ask for /videos/1, you should get back video 1 in some representation.

Model associations

The last major piece of functionality we have to implement is the ability to comment on a video. If we treat comments as just another RESTful resource, we can use a generator to fill in a working skeleton of code to fill in and be done quickly.

Instead of using the scaffold generator we used for the videos resource, this time we’re going to use the resource generator – it’s the same as the scaffold generator but doesn’t create view code. Since comments are to be displayed in the video views, we don’t need comment views, so a resource generator is exactly what we need:

rails generate resource Comment body:text

You should see it create a migration, a model, a unit test suite, a fixture, a controller, a functional test suite for the controller, a helper, a test suite for the helper, and an entry in the config/routes.rb file (i.e. a route).

Each video needs to know which comments it has so they can be easily displayed in its view. Likewise, it might be handy for each comment to know which video it belongs to, so let’s set up those associations.

Associations between resources are defined at a model level, so start by editing app/models/comment.rb to look like this:

class Comment < ActiveRecord::Base
  belongs_to :video
end

and change app/models/video.rb to

class Video < ActiveRecord::Base
  has_many :comments
  
  validates :title, :embed_code, :presence => true
  validate :must_have_valid_embed_code
  
  def must_have_valid_embed_code
    unless embed_code.include?("<iframe")
      errors.add(:embed_code, "must include an iframe tag")
    end
  end
end

Before we run the migration the generator wrote for us, we need to edit it so that each comment has a reference to the video it belongs to. In other words, we need to add a foreign key field to hold a video id.

Open up the newly created migration db/migrate/20100313161619_create_comments.rb and edit it so that it looks like this:

class CreateComments < ActiveRecord::Migration
  def change
    create_table :comments do |t|
      t.text :body
      t.references :video

      t.timestamps
    end
  end
end

That t.references :video line adds the foreign key field video_id to the comments database table.

Run the migration to add the comments table with the columns we specified:

rake db:migrate

Now we need to reflect that association in the routes.

Nested resources

If you look at config/routes.rb you can see the resources :comments line the generator inserted at the top, which routes URIs like http://localhost:3000/comments/5. Check this by running:

$ rake routes

Because each comment belongs to a video, each time a comment is created with that URI, the associated video id would have to be passed in as a parameter. A more elegant way of dealing with hierarchial resource URIs is to use nested resource routes, which produce URIs like http://localhost:3000/videos/1/comments/5 where the parent resource ids (in this case, the video id) are in the path itself.

To nest the comments resource routes within the videos resource change the routes so that the resources :videos line looks like this:

resources :videos do
  resources :comments
end

Check the routes after you make this change by running:

$ rake routes

This should display routes like:

video_comments GET    /videos/:video_id/comments(.:format)
    {:action=>"index", :controller=>"comments"}

The next step is to write the CommentsController#create action. Open up app/controllers/comments_controller.rb and add a public method create so that the class looks like this:

class CommentsController < ApplicationController
  def create
    @video = Video.find params[:video_id]
    @comment = @video.comments.create params[:comment]

    redirect_to @video
  end
end

The last step to make comments work is the view code. Let’s modify the video/show view to allow comment submission and list existing comments for that video. Open up apps/views/videos/show.html.erb and add this around the bottom of the file:

<h2>Please leave a comment:</h2>

<%= form_for([@video, Comment.new], :remote => true) do |f| %>
  <div class="field">
    <%= f.text_area :body, :cols => 40, :rows => 2 %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

<h2>What others felt about this:</h2>
<div id="comments">
  <% @video.comments.reverse.each do |comment| %>
    <div class="comment">
      <p><%= comment.body %></p>
    </div>
  <% end %>
</div>

Visit http://localhost:3000/, click on a video, then try leaving a comment. Upon refresh, it should appear below the comment form.

Partials

While it’s great that VideoHelpers#sanitize_embed_code wraps up data sanitization, what do you do when you need to encapsulate a chunk of a page, like the form used when editing a video in http://localhost:3000/videos/1/edit or when making a new one in http://localhost:3000/videos/new? The answer is to use a partial template, better known as a partial.

Our app renders the same partial in two places, in app/views/videos/new.html.erb:

<h1>New video</h1>
            
<%= render 'form' %>
            
<%= link_to 'Back', videos_path %>

and in app/views/videos/edit.html.erb

<h1>Editing video</h1>
            
<%= render 'form' %>
            
<%= link_to 'Show', @video %> |
<%= link_to 'Back', videos_path %>

That render 'form' line is what renders the partial. All partials have their filename start with an underscore, so this render call is using app/views/videos/_form.html.erb which looks like this:

<%= form_for(@video) do |f| %>
  <% if @video.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@video.errors.count, "error") %> prohibited this video from being saved:</h2>

      <ul>
      <% @video.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :embed_code %><br />
    <%= f.text_area :embed_code %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

There’s a lot going on here, but the important parts to notice are:

  • Instance variables like @video assigned in VideosController#new can be used in partials that the videos/new view renders.

  • This partial generates a form used to gather a video title and embed code and fills in existing values from @video.

Partials are not only a great fit for displaying the same content in more than one view, but they’re also great for rendering the same bit many times on the same page. See how that works in the Action View chapter, as well as how to use form helpers like form_for, label, text_field, text_area, and submit.

Note

VideosController#new is a common Ruby shorthand notation for “the VideosController instance method new

Let’s make a change to the partial to show how making a change in one place will appear in both the videos/new and videos/edit views.

Add a helpful little suggestion for the title field by making this change to app/views/videos/_form.html.erb:

<%= form_for(@video) do |f| %>

  # ...

  <%= f.label :title %> <small>(Be clever!)</small><br />

  # ...

Now take a look at http://localhost:3000/videos/new and http://localhost:3000/videos/1/edit and you should see the single change made in the partial appear in both views.

AJAX

Let’s make adding comments feel more responsive by adding a sprinkle of AJAX so that newly submitted comments appear on the page right away, without a page refresh.

Note

AJAX, or Asynchronous JavaScript and XML has evolved in usage since the technique was named. These days, JSON is usually used in new applications instead of XML as a data format. Even more confusing is that sometimes, like this time, no data is sent back; sometimes it’s just a little JavaScript and rendered HTML. Be cool – in the end, all it means is that the page doesn’t refresh and JavaScript is sent back to the browser to be interpreted.

Rails makes doing this sort of thing super easy. Start by changing the form tag for comments in app/views/videos/show.html.erb to look like this:

<h2>Please leave a comment:</h2>

<%= form_for([@video, Comment.new], :remote => true) do |f| %>
  <div class="field">
  
  ...

The key thing to notice is the addition of the :remote => true option to the form_for call. What that does is it adds a data-remote="true" attribute to the rendered form tag, which is noticed by code in jquery_ujs.js, which unobtrusively turns that form’s submit actions into asynchronous (or async) requests. It’s not important to understand everything that’s going on here; just know that we’re going to have an async call to our app from JavaScript.

Let’s respond to that async JavaScript call from the CommentsController by changing the #create action to respond to requests from JavaScript:

def create
  @video = Video.find params[:video_id]
  @comment = @video.comments.create params[:comment]

  respond_to do |format|
    format.html { redirect_to @video }
    format.js # create.js.erb
  end
end

Just like the .html.erb templates used for the videos, we’re going to create a template to be rendered and passed back as the response to the CommentsController#create action. Create that file now: app/views/comments/create.js.erb

$("#new_comment")[0].reset()
$("#comments").prepend("<%= escape_javascript(@comment.body) %>");

These few lines of jQuery are all we need. The first line resets the form used to create new comments – by default, Rails gives it an HTML id of #new_comment. The second line prepends the comment body to the top of the #comments div. Note that it also escapes the content so it’s safe to embed in JavaScript that’s being sent back as the response to the #create request.

With that added, comments submitted now appear almost instantaneously due to the browser making an async call and rendering the JavaScript received as a response. That’s pretty much all there is to “making things AJAX” in Rails.

For more complex things, just play around with jQuery – there’s tons of great resources on the web and in books. As to what else has the :remote => true option: it also works on form_tag, link_to, and button_to – take a look at ??? for more.

Note

“How did I just get jQuery?” As of Rails 3, jQuery comes standard with all new Rails applications. The inclusion happens via the javascript_include_tag "application" line in app/views/layouts/application.html.erb which is in turn provided by app/assets/javascripts/application.js which by default has those two lines that bring in jQuery and jQuery UJS for unobtrusively letting you do AJAX stuff.

The Console

The Rails console is a super handy tool. It loads up your entire application and gives you an irb shell so you can manually play with your application and its data like you were standing inside of it. Here’s an example:

$ rails console
Loading development environment

v = Video.first
=> #<Video id: 1, title: "First thing... >
v.title = "My sweet derpina"
=> "My sweet derpina"
v.save
=> true

During the early stages of app development, the console makes for a really handy admin. It also happens to be super useful for trying out ideas as you can type out one-liners without having to interact with your application from the web, running temporary hacks in the source. Apart from giving you an interactive window into your application, the Rails console also exposes a few interesting variables:

app is an instance of ActionDispatch::Integration::Session which simulates a controller in your application. Here’s an example of playing around with it:

$ rails console
Loading development environment

>> app.url_for(Video.first)
=> "http://www.example.com/videos/1"
>> app.get '/videos/1'
=> 200
>> app.assigns(:video)
=> #<Video id: 1, ... >
>> app.path
=> "/videos/1" 
>> app.reset!
=> nil
>> app.get '/hi/hello/whatever'
=> 404
>> app.post '/videos/1/comments', { :body => 'Trees are great' }
=> 200

The Rails console also gives you access to a local variable called helper that lets you run helper methods. Here are some examples:

$ rails console
Loading development environment

>> helper.pluralize 2, "video"
=> "2 videos"
>> helper.submit_tag
=> "<input name=\"commit\" type=\"submit\" value=\"Save changes\" />"

The console also provides a super handy --sandbox option. Turning this on will cause any database operations to be rolled back upon exiting the console. It’s great for playing around with destructive or mutative operations in ActiveRecord without making permanent changes to the database. Here’s how to turn it on:

$ rails console --sandbox

Note

For more on playing around with the console, check out http://errtheblog.com/posts/24-irb-mix-tape where some of these examples were referenced from.

Need to drop down to the database and tired of remembering the differences of how to do it between SQLite, MySQL and Postgres? Look no further than dbconsole:

$ rails dbconsole
SQLite version 3.7.5
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> SELECT * FROM comments;
1|Nice video!|1|2011-10-05 19:17:11.192907|2011-10-05 19:17:11.192907

Summary

Nice work! You’ve written a typical Rails application from scratch, complete with data sanitization, model validations, an RSS feed, and comments. Not bad.

Think of how long it would take if you had to spend time configuring the guts of an ORM layer, hooking it up to a view templating system, hacking together a routing system, and finding all the rest of the components you might need. Thanks to Rails being a full-stack framework, you didn't have to do any of that.

The rest of this book goes into deeper detail into each part of the Rails stack of components, like Active Record, Action View, Action Controller, and all the other bits and pieces we’ve talked about so far. We’ll be covering the most useful methods and concepts from each and steer you clear of common issues and problems.

Site last updated on: December 11, 2012 at 10:21:28 PM PST
Cover for Rails 3 in a Nutshell

View 4 comments

  1. juliehache – Posted Oct. 13, 2010

    "are taken care for " should be "are taken care of for"

  2. bteller – Posted Nov. 26, 2010

    additionally the "leaving just you to concentrate" should probably be revised to "leaving just your own problems for you to concentrate on", or something like that.

  3. Fred Flinstone – Posted July 13, 2011

    These solutions and decisions are what makes Rails a pleasure to use – the boring, menial chores are taken care ~of~ for you, leaving just ~the tasks specific to your application for you to concentrate on~.

  4. Edward Ocampo-Gooding – Posted Nov. 17, 2011

    @Fred – changed. Thanks!

Add a comment

View 1 comment

  1. DataCatalyst – Posted July 20, 2010

    Might be useful to introduce the phrase 'convention over configuration' here as it's pretty standard in most rails texts.

Add a comment

View 2 comments

  1. Marcio Torres – Posted Sept. 30, 2011

    Maybe we can say that Rails follows Push MVC way.

  2. Edward Ocampo-Gooding – Posted Oct. 3, 2011

    Marcio: can you clarify what you mean by “Push MVC””

Add a comment

View 3 comments

  1. pran – Posted June 23, 2010

    As a suggestion, I would start with a paragraph explaining a typical scenario a grandma would do, instead of just diving in directly into the MVC pattern. For instance:

    "Imagine your grandmother want to purchase a book on baking. She finds the one she wants to obtain, she then logs in, clicks the purchase button and gets redirected to the 'thanks you' screen. Under the MVC pattern, this would equate to the following steps..."

  2. pran – Posted June 23, 2010

    Missing step number ( 1.) at the beginning of the paragraph.

  3. Edward Ocampo-Gooding – Posted Nov. 17, 2011

    @pran – I’ve incorporated your ideas. Thanks!

Add a comment

View 2 comments

  1. bafilius – Posted Feb. 8, 2011

    I believe this should be 'an HTML page'.

  2. Edward Ocampo-Gooding – Posted Nov. 17, 2011

    @bafilius – done. Thanks!

Add a comment

View 2 comments

  1. bteller – Posted Nov. 26, 2010

    lower-level architectural (i think you meant decisions) about the layout

    also, let's

  2. Fred Flinstone – Posted July 13, 2011

    This ~[deleted]~ approach frees you from having to make lower-level architectural ~decisions~ about the layout and composition of your application~[deleted]~.

    Edited on July 13, 2011, 3:15 a.m. PDT

Add a comment

View 2 comments

  1. lakshmi – Posted July 25, 2010

    the above mentioned link is giving the following error... 502 bad gateway... nginx/0.8.36

  2. bafilius – Posted Feb. 8, 2011

    worked ok for me

Add a comment

View 1 comment

  1. Fred Flinstone – Posted July 13, 2011

    It’s also tasked with ensuring data integrity by preventing invalid entries ~from being~ entered into the database.

    Edited on July 13, 2011, 3:26 a.m. PDT

Add a comment

View 3 comments

  1. itsgg – Posted Nov. 25, 2010

    book_author = Person.create(:create => "Edward", :nationality => "Canadian")

    should be

    book_author = Person.create(:name => "Edward", :nationality => "Canadian")

  2. Marcio Torres – Posted Sept. 30, 2011

    Remember that Active Record is also an Architectural Pattern (see. Fowler, Patterns of Enterprise Application Architecture)

  3. Edward Ocampo-Gooding – Posted Oct. 4, 2011

    Thanks for the typo catch @itsgg!

    @Marcio agreed that it should be mentioned, but I’m trying to limit what I dump on the newbie reader here. I’ll ask James to add it to the Active Record chapter.

Add a comment

View 1 comment

  1. Fred Flinstone – Posted July 13, 2011

    Setting up associations ~[deleted]~ is also dead-simple:

    I think a side bar relating belongs_to, has_one and has_many to RDBMS relations might be warranted here.

    Edited on July 13, 2011, 3:29 a.m. PDT

Add a comment

View 2 comments

  1. primerano – Posted June 7, 2010

    Seems like we need some better CSS for handling line wraps. Someone might thing automatically is a function instead of being from the comment above. maybe overflow:auto to handle these cases?

  2. amartynov – Posted Oct. 25, 2010

    I'd suggest simply to split the long second comment into two lines

Add a comment

View 1 comment

  1. bteller – Posted Nov. 26, 2010

    should probably be listed as Microsoft SQL Server.

Add a comment

View 1 comment

  1. yoonchulkoh – Posted Jan. 5, 2011

    Isn't it possible to write like this?

    What perhaps is not recommended?

Add a comment

View 1 comment

  1. bteller – Posted Nov. 26, 2010

    clean HTML5 mark-up

Add a comment

View 3 comments

  1. steve – Posted Sept. 6, 2010

    Replace ??? with correct link

  2. amartynov – Posted Oct. 25, 2010

    Would be nice to mention alternative view engines. Like Haml.

  3. sharath chander – Posted Feb. 13, 2012

    May provide a link to "how to access all methods in Action View" - will help newbies

Add a comment

View 1 comment

  1. jon – Posted July 23, 2010

    The last sentence of the paragraph should probably read "often acting as ways to easily introduce things like authentication."

Add a comment

View 1 comment

  1. ayayalar – Posted Sept. 28, 2011

    The default is now json instead of xml.

    format.json

Add a comment

View 1 comment

  1. bteller – Posted Nov. 26, 2010

    together or collectively?

Add a comment

View 1 comment

  1. jon – Posted July 23, 2010

    As of Rails 3 Beta 4, the command "rails video_jukebox" would be entered as "rails new video_jukebox".

Add a comment

View 2 comments

  1. jeyb – Posted June 21, 2010

    The updated syntax is rails new video_jukebox

  2. chrisle – Posted Jan. 6, 2011

    Should there be a note regarding use of lowercase letters and an underscore for the application name?

Add a comment

View 1 comment

  1. Telefon – Posted Feb. 3, 2012

    Although I don't agree with all your observations they provide food for thought. This was a stimulating read and I thoroughly enjoyed what you said. Telefon Sex

Add a comment

View 1 comment

  1. jeyb – Posted June 21, 2010

    The test directory can be excluded through the option --skip-testunit. Often this is substituted by the spec directory. Worth noting test is optional and can be substituted by spec for RSpec.

Add a comment

View 4 comments

  1. muescha – Posted July 15, 2010

    if you get an error with this command - try to do this to install all missing gems before starting the server:

    bundle install

  2. muescha – Posted July 15, 2010

    do it inside the project folder:

    cd video_jukebox
    bundle install
    
  3. stanislavf – Posted July 24, 2010

    Really, this is supposed to work? "rails server" generates a new project called "server" on my machine.

    "ruby script/server" seems to work better

  4. Shripad – Posted Aug. 23, 2010

    @Stanislav Fritz: Upgrade to Rails 3.0.0.rc, To create a new project its "rails new project" and to launch the server "rails server"

Add a comment

View 1 comment

  1. e4c5 – Posted April 18, 2012

    It would be far better to spend a paragraph explaining what this command does than spend several paragaphs explaining what new files are created in each and every directory. Most people are just going to skim through it at best.

Add a comment

View 1 comment

  1. chrisle – Posted Jan. 6, 2011

    Should there be something about choosing singular or plural names when using the code generator?

Add a comment

View 1 comment

  1. Guzart – Posted Feb. 18, 2011

    The layout view videos.html.erb wasn't generated. Rails v.3.0.3 Only application.html.erb exists in app/views/layouts/

Add a comment

View 1 comment

  1. e4c5 – Posted April 18, 2012

    Wouldn't a summary instead of this lengthy description be better?

Add a comment

View 1 comment

  1. ayayalar – Posted Sept. 28, 2011

    rake db:migrate won't work here... it needs to be executed as "bundle exec rake db:migrate" or run "bundle install --binstubs" to create the bin directory then run ./bin/rake db:migrate

Add a comment

View 1 comment

  1. Fred Flinstone – Posted July 13, 2011

    As long as you’re not dropping down to SQL and ~using~ functionality specific to a database engine, the operations are database agnostic, so swapping out SQLite3 for MySQL later on is not a big deal

Add a comment

View 1 comment

  1. primerano – Posted June 9, 2010

    we’re done the hard parts should be "we're done with the hard parts" or "we've done the hard parts"

Add a comment

View 1 comment

  1. ayayalar – Posted Sept. 28, 2011

    or simply rails s

Add a comment

View 2 comments

  1. ayayalar – Posted Sept. 28, 2011

    perhaps tail -f log/development.log

  2. Edward Ocampo-Gooding – Posted Sept. 30, 2011

    Thanks! Fixed in the next version.

Add a comment

View 1 comment

  1. cnk – Posted April 8, 2011

    You will probably want to follow the log (-f flag): tail -f log/development.log

Add a comment

View 2 comments

  1. Shripad – Posted Aug. 23, 2010

    This "the application routed to the VideosController#index action and saw you see when you now go to http://localhost:3000/videos" should be "the application routed to the VideosController#index action and you see the same view as that when you go to http://localhost:3000/videos"

  2. Fred Flinstone – Posted July 13, 2011

    A good next step would be to make it so that when you ~visit~ http://localhost:3000/, the application ~routes~ to the VideosController#index action and you see ~see the same view as~ when you now go to http://localhost:3000/videos.

    Edited on July 13, 2011, 5 a.m. PDT

Add a comment

View 1 comment

  1. Shripad – Posted Aug. 23, 2010

    An addition to this text: "If you’re surprised that it doesn’t work, don’t be. The problem is that the file you’re looking at (the page welcoming you aboard to riding Rails) is in public/index.html, and any path that resolves to a file under public/ will be served up before Rails checks its routes. If even after this, it still doesn't work, then clear your browser cache."

Add a comment

View 1 comment

  1. Bonnie Cheung – Posted Nov. 26, 2011

    Missing colon after "The embed code should look like this"

Add a comment

View 1 comment

  1. tallman – Posted April 19, 2011

    Unfortunately, most of popular video sites now uses iframes for embeding, so XSS protection will cut out your videos. Adding 'iframe' to allowed tags will solve problem but without domain check it will cause serious security issue.

Add a comment

View 1 comment

  1. Fred Flinstone – Posted July 15, 2011

    Refresh the video show page you’re looking at (e.g., http://localhost:3000/videos/1), ~to see~ your app safely rendering the video . . .

Add a comment

View 1 comment

  1. garyc40 – Posted Sept. 3, 2010

    visiting videos/index gives this error:

    ActiveRecord::RecordNotFound in VideosController#show

    Couldn't find Video with ID=index

    I guess just "videos/" is sufficient.

Add a comment

View 2 comments

  1. nzcub3y – Posted Feb. 9, 2012

    You want to change app/views/videos/show.html.erb to look like this:

    <p>
        <b>Embed code:</b>
        <%= sanitize_embed_code @video %>
    </p>
    

    And app/views/videos/index.html.erb to look like this:

    <td><%= sanitize_embed_code video %></td>
    

    I thought it wasn't 100% clear for a beginner what to do at this step.

    Edited on February 9, 2012, 5 p.m. PST

  2. e4c5 – Posted April 18, 2012

    @nzcub3y +1

Add a comment

View 1 comment

  1. thierry cuisinier – Posted Jan. 23, 2012

    Maybe it will be a better idea to use the new syntax (>=3.1) for validation:

    validates :attribute, [...], :attribute, { :built_in_feature => value }

    class Video < ActiveRecord::Base validates :title, :embed_code, :presence => true end

    and using the built in validates :format => /regex/ instead of creating a custom validation method for the video markup validation

    validates :embed_code, :format => { :with => /A.*</iframe>z/i }

Add a comment

View 1 comment

  1. PAL – Posted Dec. 14, 2010

    In rails 3.0.3 & Ruby 1.9.2-p0 working in NetBeans 6.91 I receive the following message after entering the previous instruction in IDE: validates_presence_of is deprecated in Rails, use validates...:presence => true

Add a comment

View 1 comment

  1. e4c5 – Posted April 18, 2012

    are you suggesting that the line

    attr_accessible :embed_code, :title

    is not needed?

Add a comment

View 1 comment

  1. primerano – Posted Aug. 22, 2010

    In Rails 3 this respond_to block goes away. Update to show respond_with and respond_to.

Add a comment

View 1 comment

  1. Fred Flinstone – Posted July 16, 2011

    ...which in this case has ~a~ block passed to it.

Add a comment

View 1 comment

  1. nzcub3y – Posted Feb. 9, 2012

    I may be wrong but isnt the default syntax in Rails 3.2 like this:

    format.json { render json: @videos }
    

    Instead of this:

    format.json { render :json => @videos }
    

    Edited on February 9, 2012, 5:05 p.m. PST

Add a comment

View 1 comment

  1. garyc40 – Posted Sept. 3, 2010

    It seems like "videos.html.erb" is not created by default. But as soon as I create the file and copy paste the code from applications.html.erb, and modify it a little bit, it works. I'm using Rails 3, the official release.

Add a comment

View 2 comments

  1. gb26 – Posted July 21, 2010

    typo! should be <%= yield %>

  2. nzcub3y – Posted Feb. 9, 2012

    also

    app/views/layouts/videos.html.erb
    

    should be

    apps/views/layouts/application.html.erb
    

Add a comment

View 1 comment

  1. primerano – Posted Aug. 22, 2010

    "We’ll learn more about the (.:format) part later when we implement the Atom/RSS feed."

    We have already implemented the Atom/RSS feed

Add a comment

View 1 comment

  1. Shripad – Posted Aug. 23, 2010

    rails generate resource comment body:text video:references

Add a comment

View 2 comments

  1. pithyless – Posted June 6, 2010

    It is not clear if this is a typo that is supposed to read 'app/models/comment.rb', or if we are supposed to create a new file 'app/models/video/comment.rb'. (I presume it is the former)

  2. aks_sba – Posted April 19, 2011

    The above was a typo; the correct path is "app/models/comment.rb".

Add a comment

View 2 comments

  1. garyc40 – Posted Sept. 3, 2010

    "rails generate resource comment body:text video:references" achieves the same effect without having to manually edit the migration file.

  2. e4c5 – Posted April 18, 2012

    and shouldn't that be app/models/comment.rb ?

Add a comment

View 4 comments

  1. pithyless – Posted June 6, 2010

    The comment posting form (see next paragraph) was not working for me. Debugging in the console, I figured out that the CommentsController.create was being called, but no INSERT was actually hitting the database. So, I added a save:

    def create @video = Video.find params[:video_id] @comment = @video.comments.build params[:comment] @comment.save

    redirect_to @video end

    I'm just getting my feet wet with Rails, so I have no idea if this was the intention of the authors. YMMV

  2. pithyless – Posted June 6, 2010

    My last comment was not good enough, because it ignores form errors (eg. if we add validation to not allow empty comments). It would be nice if the authors could give an example of the correct way of writing the save code; I'm particularly having trouble with rendering on error, since comment/new does not exist under its own url but is a child of /video/id/:

    def create
        @video = Video.find params[:video_id]
        @comment = @video.comments.build params[:comment]
    
        if @comment.save
            redirect_to @video, :notice => 'Comment was added.'
        else
            # FIXME: not sure how to re-render the current comment (with errors)
            redirect_to @video 
        end
    end
    
  3. jon – Posted July 27, 2010

    I believe that instead of calling @video.comments.build, the create method should actually be calling @video.comments.create.

  4. jayfiveshin – Posted March 28, 2011

    jon is correct. By changing @video.comments.build to @video.comments.create, this solved my problem.

Add a comment

View 4 comments

  1. pithyless – Posted June 6, 2010

    Probably should be mentioned. DEPRECATION WARNING: f.error_messages was removed from Rails and is now available as a plugin. Please install it with rails plugin install git://github.com/rails/dynamic_form.git

  2. pithyless – Posted June 6, 2010

    DEPRECATION WARNING: <% %> style block helpers are deprecated. Please use <%= %>.

    Changed it to: <%= form_for([@video, Comment.new]) do |f| %>

  3. lf – Posted July 19, 2010

    DEPRECATION WARNING: f.error_messages was removed from Rails DEPRECATION WARNING: <% %> style block helpers are deprecated. Please use <%= %>

    The right way i think should be :

  4. tag – Posted April 14, 2011

    <% if @comment.errors.any? %> <%= pluralize(@comment.errors.count, "error") %> any error message

      <ul>
      <% @comment.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
    

    <% end %>

Add a comment

View 1 comment

  1. tsysoev – Posted March 9, 2012

    In show.html.erb file below there is :remote => true option in form_for. This may be a mistake, since you ask to add this option in AJAX section.

Add a comment

View 1 comment

  1. Shripad – Posted Aug. 23, 2010

Add a comment

View 1 comment

  1. kirka121 – Posted July 15, 2012

    :remote => true was already present from typing in previous example. "start by changing" does not make sense here as there is nothing to change :(

Add a comment

View 1 comment

  1. e4c5 – Posted April 18, 2012

    This chapter tries to do far too many things at once. Not enough information is included in the chapter itself so someone who really wants to understand what's going on will need to do too much additional research.,

Add a comment