Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1034 lines (424 sloc) 23.097 kb

Build and deploy a web application

We will build a web application for people to create topics and vote on them. Ultimately, we'll have registration and authentication and a variety of other features. Today we'll focus on building the core features of topics and votes.

Here is the UI design for the application:

Suggestotron_topics_index.jpg

Let's build a web application

*Open the terminal or command line

*Create a folder for our programming stuff

mkdir rubyonrails

*Change into that directory

cd rubyonrails

*Create a new web application!

Mac and Linux users type:

curl -O https://raw.github.com/gist/1143443/f4db3f9aa41ebb0726b98fe783e01f02e0f5ba09/workshop_template.txt rails new suggestotron -m workshop_template.txt Windows users type:

rails new suggestotron -Tm http://ram9.cc/863314.txtIF you don't have curl installed then click on the link, download the txt file, save it locally and then instead of the above command do the following command:

rails new suggestotron -m workshop_template.txt

Everything you need for your application is now in the suggestotron folder. If you decide you don't like it, just delete it and start over with the rails new command.

The -T option (T for Test) is a shortcut to tell the rails command to not use Test::Unit because we will be using RSpec instead.

The -m option (m for teMplate) is a shortcut for a few commands (listed [ http://gist.github.com/609269.txt here]). Mostly it sets up cucumber features and installs the gems you need to run them.

Normally, you probably wouldn't use a template. Eventually though you might create your own template with your frequently used options.

Next step: Go to your new folder:

cd suggestotron

To see what the rails command created for you, type the following:

ls

(That's a lower-case L, for "list.")

rails new created the following files and subdirectories within it:

File/Folder

PurposeGemfile

This file lists the gems that bundler will install in your project.Gemfile.lock

This file lists the gems that were installed for your project.README

This is a brief instruction manual for your application.Rakefile

This file contains batch jobs that can be run from the terminal.app/

Contains the controllers, models, and views for your application. You will do most of your work here.config/

Configure your application’s runtime rules, routes, database, and more.config.ru

This file makes our app rack-friendly (.ru stands for rackup).db/

Shows your current database schema, as well as the database migrations.doc/

You would add documentation for your application herefeatures/

Added by the template. This is from cucumber, which is not part of core Railslib/

Extended modules for your application (not covered today)log/

Application log filespublic/

The only folder seen to the world as-is. This is where your images, JavaScript, stylesheets (CSS), and other static files goscript/

Scripts provided by Rails to do recurring tasks. We'll use some todayspec/

Unit tests, fixtures, and other test apparatustmp/

Temporary filesvendor/

A place for third-party code##Set up your web application

If you haven't already, change to the suggestotron directory by typing:

cd suggestotron

Run the web app server:

rails server

Windows 7 Users:

If you get an Invalid gemspec in c:/...mingw32.gemspec]: invalid date format in specification: "2011-08-20 00:00:00.000000000z" error:

*Open the .gemspec file for editing. The path/filename is reported in the error message.

*Remove the time portion 00:00:00.000000z of the date field in the .gemspec file.

*Save the changes and relaunch the rails server.Point your web browser to http://localhost:3000 [http://localhost:3000]. See your web app actually there!

Make it your own

*Start Komodo Edit

*Project -> New Project...

*Select your new "suggestotron" directory

*Name the project file "suggestotron.komodoproject"If you don't see a list of files and folders to the left, then go to:

*View -> Tabs & Sidebars -> Projects###Edit index page

The page you are seeing at http://localhost:3000 is a static page, located at public/index.html.

Make a change, save, and reload your browser to see it!

Awesome! Let's ship it!

From your new "suggestotron" directory, create a local git repository:

git init

This tells git that we want to start tracking the changes to this directory.

A little note about gitignore

Whenever we commit a project, git tracks every file by default. There are, however, files that we'd rather leave untracked (or, ignored).

There is a hidden file in your app called "gitignore" that has a period as its first character in its filename to hide it from your normal directory view (this is why you didn't see it if you looked at your directory list earlier)

The entries in the "gitignore" file tell git what to avoid when tracking the changes of our project files.

If you open the ".gitignore" file, you'll see this:

.bu ndle db/.sqlite3 log/.log tmp/*/

Making the first commit

After we've done that, we can proceed with our first commit by entering the following lines into the terminal:

git add . git commit -m "basic web application"

This saves a snapshot of the application to git. Let's check to make sure that everything was committed.

git status git log

git status tells you whether you've made changes that haven't been saved in git. Currently it should show you something like this:

On branch master

nothing to commit (working directory clean)

git log shows you a history of all your commits. You should see one entry - the commit you just made.

To exit git log, hit the letter 'q'.

Once you've checked that, it's time to deploy to Heroku!

heroku create git push heroku master

Notice the URL that heroku reports after you typed "heroku create." Type this URL in your browser to see your application on the web. If you don't want to do that, the command heroku open does this for you by opening the browser.

A Closer Look at the Features

**If you are using rails 3.1.0* or greater and you get a postgresql error after pushing to heroku, then add the following to your Gemfile so that Rails can work with heroku:

group :production do gem 'therubyracer-heroku', '0.8.1.pre3' gem 'pg' end

*Then in your terminal, type the following commands:

bundle install --without production git add . git commit -m "heroku postgresql workaround" git push heroku master

**Windows Users! As of December 5, 2010 ', there is a bug in cucumber that prevents the test from running properly. To fix this, open up 'features/support/env.rb* and comment out line 19. (require 'cucumber/rails/capybara_javascript_emulation' # Lets you click links with onclick javascript handlers without using @culerity or @javascript)

*First time project setup in Windows 7, execute these two commands: (this appears to eliminate the bug referenced above) If prompted, select "a" for overwrite "all".bundle install rails generate cucumber:install --rspec --capybara

In the /features directory are several files that end in .feature. These are cucumber features that describe the application we're building.

You can find the first piece of the app that we'll build in features/1_topics.feature:

Feature: Topics In order to see a list of potential topics for meetings people need to be able to create and edit them

Scenario: Getting to the new topic page When I go to the topics page And I follow "New Topic" Then I should see a "Create" button

It's got several "scenarios" in the file. You can run all the scenarios in topics.feature like this:

rake cucumber FEATURE=features/1_topics.feature

NOTE:

*For some OS X users, this may cause a test failure due to a Lib2XML conflict, which can be resolved by commenting out Line 19 in the features/support/env.rb file: # require 'cucumber/rails/capybara_javascript_emulation'

*if you get this error: "no such file to load -- cucumber/rails/active_record (LoadError)", please update your Gemfile, by replacing a line that mentions "cucumber-rails" with:

gem 'cucumber-rails', :git => 'git://github.com/cucumber/cucumber-rails.git'Then re-run "bundle install" and "rails generate cucumber:install --rspec --capybara" (select "a" for overwrite all").

=Once you've run it=

Once you are able to run it, the first thing you'll notice is that it fails! That's good , because we haven't written any code yet.

These features are tests as well as documentation. The typical Rails workflow is:

*run the test

*watch it fail

*implement the feature

*run the test again to see if it passes.This is the workflow we'll be using today.

1_topics.feature relies on some basic elements of a web app, which is what we'll build first. As we get features to pass, we'll look further at the features definitions to see what needs to be built.

First Feature: Adding topics

We can look through the features and screen shots and see how a topic is defined and how people expect to interact with it. We will use rails "scaffolding" to generate some pages and code.

*topic will have a title and a description

*we will enable basic "CRUD" actions:

C reate - enter a new topicR ead - see everyone's topics

U pdate - change the topics you enteredD elete - remove your topics from the system##Scaffolding

*Run this command line script to generate a Topic model along with the application logic and views:

rails generate scaffold topic title:string description:text

*Holy generated files, Batman!

*Open the migration file (yours will have a different number):

db/migrate/20091014021209_create_topics.rbIt contains Ruby code to set up the database table. Migrations can also be used for modifying tables (add/remove/rename columns) and even modifying data.

*Now let's set up our development and test databases by running the migration file:

rake db:migrate

*Run the cucumber feature again (the first scenario should pass, but the second will fail)

rake cucumber FEATURE=features/1_topics.feature

*If you want to just see the passing scenario, type this instead:

rake cucumber FEATURE=features/1_topics.feature:5:8

This command tells cucumber to only run lines 5 through 8 in your story, which is currently passing our tests.Now let's look at the feature we created by running the server locally.

*Start your server:

rails server

*And point your browser to http://localhost:3000/topics [http://localhost:3000/topics]Congratulations! You have built a web application that works with a relational database.

A Quick Look at Your Database

You can access the database directly on your local machine. For this class we’re using SQLite, and we've installed SQLite Manager, a GUI tool that lets you inspect the database. To open it, open Firefox, then select "Tools -> SQLite Manager."

Click the open folder icon Folder.jpg or choose Database -> Connect Database to open suggestotron/db/development.sqlite3

Windows Users: You may have to change the file type selection field to "All Files" to be able to see files with the sqlite3 file extension.

Sqllit.jpg

In SQLite, you can choose “Browse & Search” to interactively explore the database or “Execute SQL” to type SQL commands.

You can also access the database through the command line:

rails dbconsole

Common SQL Commandssqlite

MySql

PosgreSQLlist tables in current db

.tables

show tables;

\dshow SQL for table create list columns

.schema
.schema topics

show create table topics;
describe topics;

\d topicsexit command line tool

.quit

exit

\qshow all rows in table

colspan="3" | select * from topics;show number of rows

colspan="3" | select count(*) from topics;show matching record

colspan="3" | select * from topic where title = "My Topic";##Deploy Your Application

Don’t forget, "commit early, deploy often." Here's how:

git add . git commit -m "topic crud" git push heroku master heroku rake db:migrate

If you run into error, uninitialized constant Rake::DSL then add following to your Rakefile:

require 'rake/dsl_definition' require 'rake'

Congratulations! You have built and deployed a web application that works with a relational database.

Rails implements a very specific notion of the Model-View-Controller pattern, which guides how you build a web application.

Model

*represents what is in the database

ActiveRecord, ActiveModelView*

*the model rendered as HTML

ActionView, erbController*

*receives HTTP actions (GET, POST, PUT, DELETE)

*decides what to do, such as rendering a view

*ActionControllerMvc.jpg

Mvc-stickman.png

When you executed the rails generate scaffold command, Rails generated files that implement a model, some views, and a controller for topics.

The Model

*create app/models/topic.rb

create db/migrate/20090611073227_create_topics.rbFive Views*

*create app/views/topics/_form.html.erb

*create app/views/topics/index.html.erb

*create app/views/topics/show.html.erb

*create app/views/topics/new.html.erb

create app/views/topics/edit.html.erbThe Controller*

*create app/controllers/topics_controller.rb

*route map.resources :topics##A closer look

Rails allows you to easily invoke an interactive console with all of the Rails libraries and your application code loaded:

rails console

Let's add in some logging so that we can see what is happening in the database:

ActiveRecord::Base.logger = Logger.new(STDOUT)

Let’s open the console and look at the model that is defined here: app/models/topic.rb

t = Topic.new => # t.title = "My topic" => "My topic" t.description = "this is really cool" => "this is really cool" t.save

Notice that the Topic class has title and description attributes which you did not need to explicitly declare in the class. This is handled by ActiveRecord which implements ORM (Object Relational Mapping) in Rails.

Routes

Rails routes control how URLs map to code. We can use rake routes to list the relationships between routes and controllers.

$ rake routes topics GET /topics(.:format) {:action=>"index", :controller=>"topics"} topics POST /topics(.:format) {:action=>"create", :controller=>"topics"} new_topic GET /topics/new(.:format) {:action=>"new", :controller=>"topics"} edit_topic GET /topics/:id/edit(.:format) {:action=>"edit", :controller=>"topics"} topic GET /topics/:id(.:format) {:action=>"show", :controller=>"topics"} topic PUT /topics/:id(.:format) {:action=>"update", :controller=>"topics"} topic DELETE /topics/:id(.:format) {:action=>"destroy", :controller=>"topics"}

Each method in the controller will take an HTTP request, usually find some data in the database (via an ActiveRecord model) and render a view or redirect to another action.

See Workshop_Diagrams to help trace the connections between HTTP, REST, routes, controllers, and databases.

How to explore your routes

Open up your rails console:

rails console

Now you can have a look at the paths that are available in your app. Let's try looking at one of the topics routes we just generated.

app.topics_path => "/topics" app.topics_url => " http://www.example.com/topics%22

Remapping /

You can specify what Rails should route "/" to with the root method in routes.rb. You should put the root route near the end of the file, but before the final "end".

root :to => 'topics#index'

After adding the root route, your routes.rb file should look like this:

Suggestotron::Application.routes.draw do

resources :topics root :to => 'topics#index'

end

You also need to delete the index.html file for the root route to work.

git rm public/index.html

Next feature: creating a topic

Let's run the feature again and see what the next scenario is.

rake cucumber FEATURE=features/1_topics.feature

Scenario: Creating a topic Given I go to the topics page And I follow "New Topic" When I fill in "Title" with "Rails Fixtures" And I fill in "Description" with "Introduce how to add test data with fixtures." And I press "Create" Then I should see "Rails Fixtures" And I should be on the topics page

Run the server ( rails server), look at the app ( http://localhost:3000/topics/ [http://localhost:3000/topics]),,) and see how the scaffold template differs from the desired application as we've described it above using cucumber. What we want is the application to show the list of topics after the user creates a topic by completing the form. However, the scaffold displays instead a page showing only the individual topic the user just created.

Controller: adjusting the flow of your application

We looked at the scaffold-generated code a little earlier. To change the behavior and make the feature act as desired, we will look more closely now at the controller, which controls the general flow of your application; for instance, which page is displayed when the user clicks a link or a button.

*Open app/controllers/topics_controller.rb and look at the new and create actions.

*Notice that in create there is a redirect to a single topic, format.html { redirect_to(@topic) }

We want to redirect instead to the list of topics:

format.html { redirect_to(topics_path) } # <-- note no @

*As of December 5, 2010, there is a bug in cucumuber that prevents the test from running properly. To fix this, open up features/support/env.rb and comment out line 19 (if you haven't done that already).

*Now run your feature again with cucumber and it should pass rake cucumber FEATURE=features/1_topics.feature##Commit & Push

Congratulations! This is definitely a checkpoint. Time to commit to git and push your changes out to Heroku.

git add . git commit -m "ZOMG first feature passes!!1!!11" git push heroku master

(Run ‘heroku rake db:migrate --app ’ to bring over changes in the migration files under Feature 1.)

Next feature: the Topics page

Note that for this next set of feature scenarios we have a "Background" set of steps which will be executed before each scenario in the file. Since they depend on behavior that already works, they're rendered in green (pass).

Scenario: Viewing a topic detail page When I go to the topics page And I follow "Rails Fixtures" Then I should see "Introduce how to add test data with fixtures." And I should not see "New Topic"

*Run the scenario and watch it fail: rake cucumber FEATURE=features/2_topics_list_and_details.feature

*In the last scenario we made it so the app redirected to "topics_path" after create. So, we expect that we will need to fix the error in that page. We can use rake routes to find the controller action, then we can see that the default view is rendered. The error is in app/views/topics/index.html.erb:

<%= link_to topic.title, topic %>

*Run the scenario again and see that the "Clicking on the topic title" passes, but we still have one failure which can be addressed in the same file

*Open app/views/topics/index.html.erb and change "Destroy" to "Delete".##Commit & Push

Another checkpoint! Time to commit to git and push your changes out to Heroku.

git add . git commit -m "Topic titles are now links. for srs." git push heroku master

**If you're getting an error* when you go to your Heroku application, run one more commandbundle exec rake assets:precompile

Be sure to commit your changes and push your changes to Heroku

Next feature: allow voting on a topic

Feature: Votes In order to determine which talk to give people need to be able to vote for the ones they like

Background: Make sure that we have a topic Given I go to the topics page And I follow "New Topic" And I fill in "Title" with "Rails Fixtures" And I fill in "Description" with "Introduce how to add test data with fixtures." And I press "Create"

Scenario: viewing votes already cast When I go to the topics page Then I should see "0 votes"

Scenario: voting on a topic When I go to the topics page And I follow "+1" Then I should see "1 vote"

rake cucumber FEATURE=features/3_votes.feature

How will we build this feature?

*Each vote will be an object (row in database table)

*When someone votes on a topic, we'll create a new vote object and save it

*Each vote is associated with a specific topic##Rails associations

  • Topic has_many :votes
  • Vote belongs_to :topic

Rails-associations.jpg

Add votes

We will use the resource generation script to create a model and controller (no views):

rails generate resource vote topic_id:integer

And then we migrate

rake db:migrate

The script creates files with:

*model (including migration, unit, fixture)

*controller (and route) with no codeAdd code to to your models to create the associations:

* /app/models/topic.rb

class Topic

* /app/models/vote.rb

class Vote

Check it out in rails console:

t = Topic.new(:title => "My Topic") => # t.votes => [] t.votes.build => # t.votes => [#] t.save => true t.votes => [#]

Now you can use it in your view ( /app/views/topics/index.html.erb):

<%= pluralize(topic.votes.length, "vote") %>

Allow people to vote

It's good to do one bit at a time and let the test failures drive what you do next.

Check rake routes for figuring out the path TODO: diagram or screenshot of where this info is on the rake output, and edit your view ( /app/views/topics/index.html.erb):

<%= link_to '+1', votes_path(:topic_id => topic.id), :method => :post %>

Now we need to create the controller action. Open the Votes controller ( /app/controllers/votes_controller.rb) to add the create action:

class VotesController

Add flash notice to app/views/layouts/application.html.erb

What Next?

Resources and Next Steps

http://bit.ly/ruby-resources

Jump to Line
Something went wrong with that request. Please try again.