Workshop Topics
From DevchixWiki
Slides
- slides here: http://railsbridge.github.com/workshop
- slides for the intro programming section: http://railsbridge.github.com/workshop/programming_intro.html
- source for slides here: http://github.com/railsbridge/workshop/tree/master
- Contact Sarah Allen (sarah _at_ ultrasaurus _dot_ com) and send your github name if you want to be a collaborator. Or just fork the github project and send a pull request when you've made changes.
Ruby Language (for programmers)
- quick session on Ruby language for the people who already know programming
- basic syntax
- everything is an object
- everything returns something
- open classes
- blocks
- symbols
- http://gist.github.com/190567
- http://betterexplained.com/articles/starting-ruby-on-rails-what-i-wish-i-knew/
- more on TDD/BDD (see Add votes section below. Teaching TDD went really well with the experienced programmers and I definitely want to do that again with a little more TDD overview material -sa )
Total beginner's guide to programming (Novice group only)
- What's a program? Operating system?
- What's a framework?
- something that makes it faster to build an application because it contains most of the things you would commonly write
- Workflow - how do you write a program?
- Learn about customer's requirements -> translate to "stories"
- Pick a story that seems doable and start writing code that does it
- Show your work to the customer, get feedback
- Based on feedback, adjust stories (customer's "up front" requirements vs. changes once they see something working)
- Once story is finished, go back to "pick a story"...keep going until you're done! (This is an example of looping!)
- alternate programming intro
- Basic programming structures - or, how to do the "start writing code" step
- variables - words that hold information
- types of information - text, numbers, collections
- operators - doing stuff with variables
- loops - doing the same action a bunch of times
- printing - to the screen, or to a file
- Writing a simple program
- opening the editor
- opening the command line
- adding two numbers together, storing in a variable
- printing variable to the screen
- save and run
Creating a web application.
- What's a web application?
- Basic structure: web server, code, database
- Programming tools we'll be working with
- Rails
- Rake
- git: for source code control
- a database
- an editor
- hosting services: heroku/github
- Model-View-Controller
- in general
- in Rails
Getting Started with Rails
- creating a folder on the desktop for our programming stuff
- opening the editor, opening the command line
rails suggestotron -m http://gist.github.com/194076.txt
- Everything is in that folder - if you decide you don't like it, just delete it and start over with the rails command.
- note that the -m option is just a shortcut for a few commands listed in that file (click on the link to see). It is optional. Usually you will not use that option (or maybe you will crete your own template with your frequently used options). Later we'll explain them in more detail.
ruby script/server
- http://localhost:3000 - see your web app actually there!
Open the app source code
- Start Komodo Edit
- File -> New -> New Project...
- Open the "suggestotron" directory
- View -> Tabs & Sidebars -> Projects
Edit index page
- note that the page you are seeing at http://localhost:3000 is in public/index.html
- make a change and reload
- Development vs. Production
- When you're working, everything is on your computer. Right now, you've got a server, rails, your code, a database, all on your machine. You can see your app, but nobody else can.
- In practice, your laptop isn't powerful enough to actually have a lot of people connect to it, and you want your app to stay running even when your laptop is off.
- So you "deploy" (copy) your application to a server at your hosting provider - they take care of security, etc.
Awesome! Let's ship it! (Deploy #1)
- saving a copy of the code to git
git init git add . git commit -m 'basic web application'
- deploy to heroku
heroku create git push heroku master heroku rake db:migrate
- go to your URL, see your application on the internet!
What application will we build?
- Screen shots of the application: Workshop Application Design
- Introduction to Behavior Driven Development (BDD)
- We have written up the application behavior using a BDD framework called cucumber
- Describe the web application we want to build
- Basic concepts: topics, votes, users
- These are described in the "features" directory that was created using the template.
Starting to develop the first feature
One of the basic features that we might build first is defined in the topics.feature file in the features directory:
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 topics
And I follow "New topic"
Then I should see a "Create" button
- You can run all of the features in that file with:
cucumber features/topics.feature
- Of course, the feature fails because we haven't written any code yet! These feature descriptions are tests as well as documentation. Typically we run the test, watch it fail, then implement a feature, then run the test again to see if it passes. We'll be doing that today.
- The topics features 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
- To run a single feature scenario, you can provide its name:
cucumber features/topics.feature -n 'Getting to the new topic page'
CRUD and Databases
To develop the first feature, we need to know a bit about writing a web application that accesses a relational database.
- CRUD??
- Create - enter a new topic
- Read - see everyone's topics
- Update - change the topics you entered
- Delete - remove your topics from the system
- REST: conventions, etc.
- Databases
- storage for web apps - even when no one's using the app, even when the app isn't running, the database keeps track of all the meetings that have been entered so far.
- like a storeroom at a restaurant - it keeps all the information, the raw materials of your application, but it's not directly accessible to your users (diners).
- your rails app goes back to the "storeroom", gets the information the user requested, and displays it nicely - like turning raw materials into actual dishes.
- a database is organized into tables - each one is like one worksheet of an excel spreadsheet - columns, rows
- one table per "concept", so we need a topics table
- we can see from our feature specification what fields we need (title, description, ... ?)
- once we have a list, we can go back to the command line to implement it
Scaffolding
- run command line script to generate a Topic model along with the application logic and views
ruby script/generate scaffold topic title:string description:text
- holy generated files, batman
- Open the migration file
- explain what it does
- migrations can also be used for modifying tables (add/remove/rename columns) and even modifying data
- Now let's set up the database for the test
rake db:migrate RAILS_ENV=test
- Run the cucumber feature (and the first one should pass)
cucumber features/topics.feature
- Now let's look at the feature we created. To do so, we want to set up the development environment
- create the topics table
rake db:migrate
- run the server
ruby script/server
- http://localhost:3000/topics - do some topics CRUD
- highlight that documentation for all this code is online at railsapi.com
- Check out SQLite Manager for Firefox
- Now let's take another look at our topics table (check it out using the sqlite plugin)
- You can also look at the database using the command line tool that comes with it. These command line tools are slightly different for each database and rails provides a convenient shortcut for accessing whichever one you are using:
ruby script/dbconsole
- To stop the server
- control-c in the terminal window or git bash where it is running
Deploy #2
- saving a copy of the code to git
git add . git commit -m 'basic topic crud'
- deploy to heroku
git push heroku master heroku rake db:migrate
- go to your URL/topics, see topic CRUD! that you wrote! on the internet!
- Show to customer? Nope! In this case, the customer has already documented the expected features using cucumber. We can see what needs to be done next just by running the features as tests.
- This is a lot to take in the first time, but it was actually not a lot of work to get to this point. Now we'll look at the code we just created and explain what Rails generated for us before we move on to new features
What did we just do?
- TODO: link MVC slide
- How URLs map to code
$ rake routes
topics GET /topics(.:format) {:action=>"index", :controller=>"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"}
PUT /topics/:id(.:format) {:action=>"update", :controller=>"topics"}
DELETE /topics/:id(.:format) {:action=>"destroy", :controller=>"topics"}
/:controller/:action/:id
/:controller/:action/:id(.:format)
Next feature
- Let's run the next feature and see what we need to build next
Scenario: Creating a topic
Given I go to topics
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 see a "New topic" link
- Run the server, look at the app and see how the scaffold template differs from the desired application as we've described it using cucumber
- In the desired behavior, after someone creates a topic, the expected behavior is for the application to display the list of topics; however, scaffold instead displays a page showing the individual topic that we just created
Controller: adjusting the flow of your application
- Earlier we looked at the scaffold generated code a bit. To change this behavior, we will look closely at the controller, which controls the general flow of your application (which page is displayed when someone clicks a link or a button)
- Take a look at: app/controllers/topics_controller.rb
- Walk thru new/create actions
- notice that in create there is a redirect to
format.html { redirect_to(@topic) }
- instead we want to re-direct to the list of topics
format.html { redirect_to(topics_path) }
Next feature: topics page
- Note for this next set of feature scenarios we have a "Background" set of steps which will be executed before each scenario in the file. These rely on behavior that already works, so are green (pass).
cucumber features/topics_list_and_details.feature
Scenario: Clicking on the topic title
When I follow "Rails Fixtures"
Then I should see "Introduce how to add test data with fixtures."
And I should not see "add a topic"
in views/topics/index.html.erb
<td><%= link_to h(topic.title), topic %></td>
- change "Destroy" to "Delete" in views/topics/index.html.erb
Next feature: votes
cucumber features/votes.feature
Add votes
- TODO: this section needs a lot of work and has not yet been edited to match the cucumber feature workflow
- TODO* link to assn images
- use generate resource to create model & controller (no views)
script/generate resource vote topic_id:integer
- creates the following:
- model (including migration, unit, fixture)
- controller (and route)
class Topic < ActiveRecord::Base has_many :votes end
class Vote < ActiveRecord::Base belongs_to :topic end
- check it out in irb
>> t = Topic.new => #<Topic id: nil, title: nil, description: nil, created_at: nil, updated_at: nil> >> t.votes => []
- now you can use it in your view (along with a handy view helper)
<td><%= pluralize(topic.votes.length, "vote") %></td>
Allow people to Vote
This is 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.
<td><%= link_to '+1', votes_path(:topic => topic.id), :method => :post%></td>
now we need to create the controller action
class VotesController < ApplicationController
def create
vote = Vote.new(:topic_id => params[:topic])
vote.save
if vote.save
flash[:notice] = 'Vote was successfully created.'
else
flash[:notice] = 'Sorry we could not count your vote.'
end
redirect_to(topics_path)
end
end
OLD CONTENT
Associations
- write the test
test "Topic has votes" do
t = Topic.new(:title => "my title")
assert t.votes == []
end
- add to topic model:
has_many :votes
- Rails can also support accessing topic from a vote. Write the test
test "Vote has a topic" do
v = Vote.new()
assert v.topic == nil
end
- add to vote model:
class Vote < ActiveRecord::Base belongs_to :topic end
Validations
- TDD (this needs some more description, maybe some slides)
require 'test_helper'
class TopicTest < ActiveSupport::TestCase
test "Require title to not be blank" do
t = Topic.new
assert t.valid? == false, "Expected Topic to not be valid"
end
end
- add validation to the vote model
validates_presence_of :topic
- based on a question from the class we also added the positive test
test "Topic with title is valid" do
t = Topic.new(:title => "my title")
assert t.valid?, "Topic with title should be valid"
end
Nested Routes
What is the test for which this is required?
- edit the routes, from:
map.resources :votes map.resources :topics
- to:
map.resources :topics, :has_many => :votes
- show before and after rake routes
master $ rake routes
(in /Users/sarah/src/topicr)
votes GET /votes(.:format) {:controller=>"votes", :action=>"index"}
POST /votes(.:format) {:controller=>"votes", :action=>"create"}
new_vote GET /votes/new(.:format) {:controller=>"votes", :action=>"new"}
edit_vote GET /votes/:id/edit(.:format) {:controller=>"votes", :action=>"edit"}
vote GET /votes/:id(.:format) {:controller=>"votes", :action=>"show"}
PUT /votes/:id(.:format) {:controller=>"votes", :action=>"update"}
DELETE /votes/:id(.:format) {:controller=>"votes", :action=>"destroy"}
topics GET /topics(.:format) {:controller=>"topics", :action=>"index"}
POST /topics(.:format) {:controller=>"topics", :action=>"create"}
new_topic GET /topics/new(.:format) {:controller=>"topics", :action=>"new"}
edit_topic GET /topics/:id/edit(.:format) {:controller=>"topics", :action=>"edit"}
topic GET /topics/:id(.:format) {:controller=>"topics", :action=>"show"}
PUT /topics/:id(.:format) {:controller=>"topics", :action=>"update"}
DELETE /topics/:id(.:format) {:controller=>"topics", :action=>"destroy"}
/:controller/:action/:id
/:controller/:action/:id(.:format)
master $ rake routes
(in /Users/sarah/src/topicr)
topics GET /topics(.:format) {:controller=>"topics", :action=>"index"}
POST /topics(.:format) {:controller=>"topics", :action=>"create"}
new_topic GET /topics/new(.:format) {:controller=>"topics", :action=>"new"}
edit_topic GET /topics/:id/edit(.:format) {:controller=>"topics", :action=>"edit"}
topic GET /topics/:id(.:format) {:controller=>"topics", :action=>"show"}
PUT /topics/:id(.:format) {:controller=>"topics", :action=>"update"}
DELETE /topics/:id(.:format) {:controller=>"topics", :action=>"destroy"}
topic_votes GET /topics/:topic_id/votes(.:format) {:controller=>"votes", :action=>"index"}
POST /topics/:topic_id/votes(.:format) {:controller=>"votes", :action=>"create"}
new_topic_vote GET /topics/:topic_id/votes/new(.:format) {:controller=>"votes", :action=>"new"}
edit_topic_vote GET /topics/:topic_id/votes/:id/edit(.:format) {:controller=>"votes", :action=>"edit"}
topic_vote GET /topics/:topic_id/votes/:id(.:format) {:controller=>"votes", :action=>"show"}
PUT /topics/:topic_id/votes/:id(.:format) {:controller=>"votes", :action=>"update"}
DELETE /topics/:topic_id/votes/:id(.:format) {:controller=>"votes", :action=>"destroy"}
/:controller/:action/:id
/:controller/:action/:id(.:format)
Edit View
- Add to the topics index (refer to rake routes to see what the path should be)
<td>
<%= button_to "+1", topic_votes_path(topic), :method => :post %>
</td>
- Look the page, click the button, see an error
Controller
- add controller methods
class VotesController < ApplicationController
def create
@topic = Topic.find(params[:topic_id])
@vote = @topic.votes.create!
flash[:notice] = "You voted for #{@vote.topic.title}"
redirect_to(topics_path)
end
# DELETE /topics/votes/1
def destroy
@vote = Topic.find(params[:topic_id]).votes.last
@vote.destroy
redirect_to(topics_url)
end
end
editing views
- edit the index view
- add link to title
<td><%= link_to h(topic.title), topic %></td>
- remove description & show
- add the same vote controls to the show view (explain making a partial: create _vote.erb in /app/views/topics)
<td><%= render :partial => "vote", :locals => {:topic => topic} %></td>
- copy paste same partial code and get this error
- note: need @topic instead, since topic is not a local variable
Next feature: authenticated votes
cucumber features/authenticated_votes.feature
Authentication
- Different options for auth
adding users with restful authentication
script/plugin install git://github.com/technoweenie/restful-authentication.git mv vendor/plugins/restful-authentication/ vendor/plugins/restful_authentication/
- note: renaming to change '-' to '_' is required > Rails 2.1
- using rspec with restful auth
sudo gem install rspec rspec-rails cucumber script/generate rspec script/generate authenticated user sessions --rspec
- or if you don't care about rspec just do
script/generate authenticated user sessions
After running these commands take some time to walk through the generated files.
Move the declaration
include AuthenticatedSystem
from the UsersController to the ApplicationController. Make sure to examine the lib/authenticated_system.rb file to get a sense of what methods your controllers now have available.
Update your local database
rake db:migrate
Now you should be able to use the paths /login, and /users/new. (Note: if you get the error "error: uninitialized constant User::Authentication" either you forgot to rename the plugin with '_' or restart the server note from ruby-forum)
Push the new system to heroku
git add -A git commit -a -m "generated auth system" git push heroku
And update your databases
heroku rake db:migrate
updating views with Login/Sign Up
You may wish to create an application layout and add some links for "Sign Up" and "Login". You will have to remove the existing layouts to make a single application layout work for the Topic and Vote pages:
cp app/views/layouts/topics.html.erb app/views/layouts/application.html.erb rm app/views/layouts/topics.html.erb app/views/layouts/votes.html.erb
You can add a menu to the header of the layout like so:
<% if logged_in? %>
<%= link_to "Logout", logout_path %>
<% else %>
<%= link_to "Sign Up", new_user_path %>
<%= link_to "Login", login_path %>
<% end %>
Requiring authentication for voting
Now it's easy to sign up for your own account. But anyone can still vote, or add a topic. Let's add some protection to our VotesController:
before_filter :authenticated?
def authenticated?
if !logged_in?
flash[:notice] = "You must login to do that"
redirect_to root_path
false
end
end
Now only logged-in users can vote. But it might be nicer to hide the voting buttons from them until they log in. Now you can change app/views/topic/_vote.html.erb to include the following:
<% if logged_in? %> <%= button_to "+1", topic_votes_path(topic) %> <% end %> <%= topic.votes.size %> votes
Add votes to users
- create a migration to add the user_id column to the vote model
script/generate migration user_votes
- Add the following to db/migrate/[...]_user_votes.rb:
class UserVotes < ActiveRecord::Migration def self.up add_column :votes, :user_id, :integer end
def self.down remove_column :votes, :user_id end end
- add to the vote model:
class Vote < ActiveRecord::Base belongs_to :user belongs_to :topic end
- add to user model:
has_many :votes
- and change the VotesController to record this information as well
def create
@vote = @topic.votes.build(params[:vote])
@vote.user = current_user
...
end
- Now you can make it so someone can change their mind, add vote destroy, etc.
Notes
The following stuff should be covered above
- Models, Views, and Controllers
- we're paranoid, so let's save a copy in github.
- go to github.com, create an application
- git remote add origin git@github.com:your_github_username/your_app_name.git
- git push origin master
- heroku/git
- irb and scirpt/console
- Ruby language
- Ruby gems: what are they exactly? how do you install them? what happens when you do?
- TDD
- MVC - the structure of the web app
- what's a relational database?
- migrations, belongs_to
- drag, re-order (this seems too ambitious)

