169
1. FROM THE GROUND UP

Rails for Zombies 2 Slides

Embed Size (px)

Citation preview

1. FROM THE GROUND UP

02 Creating a Rails App

04 Database Migrations

03 The Command Line

TABLE OF CONTENTS

05 Ruby 1.9 Hash Syntax

01 Installing Rails

06 Bundler

FROM THE GROUND UP

07 Database Configuration

WINDOWS ONLY

http://railsinstaller.org

FOR OS X AND LINUX

http://railstutorial.org

CREATING A RAILS APP $ rails

Options: -O, [--skip-active-record] # Skip Active Record files -d, [--database=DATABASE] # Preconfigure database # Default: sqlite3 -j, [--javascript=JAVASCRIPT] # Preconfigure JavaScript library # Default: jqueryRuntime options: -f, [--force] # Overwrite files that already exist -p, [--pretend] # Run but do not make any changes

Example: rails new ~/Code/Ruby/weblog

Usage: rails new APP_PATH [options]

FROM THE GROUND UP

CREATING A RAILS APP

$ rails new TwitterForZombies

create Gemfile create app create app/controllers/application_controller.rb create app/mailers create app/models create app/views/layouts/application.html.erb create config create log create public/index.html create script/rails

Creates a bare Rails app in a new TwitterForZombies directory

run bundle install

create

FROM THE GROUND UP

CREATING A RAILS APP

$ rails new TwitterForZombies

Installs dependenciesrun bundle installFetching source index for http://rubygems.org/Installing rake (0.9.2) Using activesupport (3.1) Using rack (1.3.0) Using actionpack (3.1) Using actionmailer (3.1)Using activerecord (3.1) Using bundler (1.0.15) Installing coffee-script (2.2.0) Installing jquery-rails (1.0.12) Using rails (3.1)Installing sqlite3 (1.3.3) with native extensions Your bundle is complete!

FROM THE GROUND UP

RAILS COMMANDS

$ cd TwitterForZombies

Lists commands

$ rails

Start the Rails server (short-cut alias: "s")server

Usage: rails COMMAND [ARGS]

The most common rails commands are: generate Generate new code (short-cut alias: "g")

Start a console for the db in config/database.yml (short-cut alias: "db")

All commands can be run with -h for more information.

dbconsole

console Start the Rails console (short-cut alias: "c")

FROM THE GROUND UP

STARTING THE SERVERStarts a basic development server $ rails server

=> Booting WEBrick=> Rails 3.1 app in development on http://0.0.0.0:3000=> Call with -d to detach=> Ctrl-C to shutdown server[2011-06-30 16:44:43] INFO WEBrick 1.3.1[2011-06-30 16:44:43] INFO ruby 1.9.2 [2011-06-30 16:44:43] INFO WEBrick::HTTPServer#start: port=3000

To lookup options$ rails server -h

$ rails s

Shortcut

FROM THE GROUND UP

USING GENERATORS

To create template files $ rails generate

$ rails g

Shortcut

Usage: rails generate GENERATOR [args]

Please choose a generator below.

Rails: helper mailer migration modelscaffold

FROM THE GROUND UP

STARTING WITH A SCAFFOLDTo create a starting point $ rails g scaffold

Usage: rails generate scaffold NAME [field:type field:type]

Description: Scaffolds an entire resource, from model and migration to controller and views, along with a full test suite.

Example

string text integer boolean decimal

float binary date time datetime

Variable types

$ rails g scaffold zombie name:string bio:text age:integer

FROM THE GROUND UP

STARTING WITH A SCAFFOLD$ rails g scaffold zombie name:string bio:text age:integer

route

create

create

Model

Routing

Controller

View

PLUS TESTS, HELPERS, AND ASSETS

createinvoke

create app/models/zombie.rbdb/migrate/20110701230207_create_zombies.rbactive_record

resources :zombies

app/controllers/zombies_controller.rb

app/views/zombies app/views/zombies/index.html.erbcreate

createcreatecreate

app/views/zombies/edit.html.erbapp/views/zombies/show.html.erbapp/views/zombies/new.html.erb

FROM THE GROUND UP

INTRODUCING A MIGRATIONMigration

t.datetime :created_att.datetime :updated_at

SAME AS

Date & time automatically set on create and update events

AUTOMATICALLY CREATES A

PRIMARY KEY CALLED ID

db/migrate/20110701230207_create_zombies.rb

create_table :zombies do |t|t.string :namet.text :biot.integer :age

t.timestamps end endend

class CreateZombies < ActiveRecord::Migration def change

FROM THE GROUND UP

WHY WE HAVE MIGRATIONS

Story here

FROM THE GROUND UP

RUNNING MIGRATIONS

$ rails s

FORGOT TO RUN MIGRATIONS!

db/migrate/20110701230207_create_zombies.rb

@zombies = Zombie.all

FROM THE GROUND UP

RUNNING MIGRATIONS

$ rails s

To run all missing migrations$ rake db:migrate

== CreateZombies: migrating ==================================================-- create_table(:zombies) -> 0.0011s== CreateZombies: migrated (0.0012s) =========================================

FROM THE GROUND UP

RAILS CONSOLETo debug our app $ rails console

$ rails cShortcut

Loading development environment (Rails 3.1)> Zombie.create(name: "Eric Allam", age: 27)

SQL (15.2ms) INSERT INTO "zombies" ("age", "bio", "name") VALUES (?, ?, ?) [["age", 27], ["bio", nil], ["name", "Eric Allam"]]

=> #<Zombie id: 1, name: "Eric Allam", bio: nil, age: 27>

> z = Zombie.firstZombie Load (0.2ms) SELECT "zombies".* FROM "zombies" LIMIT 1

=> #<Zombie id: 1, name: "Eric Allam", bio: nil, age: 27>

> z.name = "Caike Souza"=> "Caike Souza"

> z.save(0.5ms) UPDATE "zombies" SET "name" = 'Caike Souza' WHERE "zombies"."id" = 1

=> true

FROM THE GROUND UP

Ruby 1.9{ name: "Eric Allam" }

RUBY 1.9 NEW HASH W/SYMBOL SYNTAX

Ruby 1.9Zombie.create(name: "Eric Allam", age: 27)

Ruby 1.8 & 1.9

Hash is a collection of key value pairs (key can be anything)

{ "first_name" => "Gregg" }

SAME AS

Ruby 1.8 & 1.9

Ruby 1.8 & 1.9

{ 2 => "Pollack" }

{ :name => "Eric Allam" }

FROM THE GROUND UP

RUBY 1.9 NEW HASH W/SYMBOL SYNTAXRuby 1.9Zombie.create(name: "Eric Allam", age: 27)

Ruby 1.8 & 1.9Zombie.create(:name => "Eric Allam", :age => 27)

class ZombiesController < ApplicationController

def index @zombies = Zombie.all

respond_to do |format| format.html format.json { render json: @zombies } end end ...

app/controllers/zombies_controller.rb

SAME AS

render :json => @zombies

FROM THE GROUND UP

ADDING COLUMNS

invoke active_record create db/migrate/201173223_add_email_and_rotting_to_zombies.rb

AddEmailAndRottingToZombies email:stringrotting:boolean

$ rails g migration

class AddEmailAndRottingToZombies < ActiveRecord::Migration def change add_column :zombies, :email, :string add_column :zombies, :rotting, :boolean

default: <value> limit: 30 null: falsefirst: true after: :email unique: true

migration options

POSITION

, default: false endend

Add<Anything>To<Table name> Column Name Type

MIGRATION RAKE TASKSTo run all missing migrations$ rake db:migrate

To rollback the previous migration$ rake db:rollback

Dump the current db state$ rake

CALLdb:schema:dump

CREATES When you start working on an existing app

db/schema.rb

ActiveRecord::Schema.define(:version => 20110703223452) do create_table "zombies", :force => true do |t| t.string "name" t.text "bio" t.integer "age" t.datetime "created_at" t.datetime "updated_at" endend

Authoritative source for db schema

Creates the db, loads schema, & seed$ rake db:setup

MIGRATION COMMANDSRemoveAgeFromZombies age:integer$ rails g migration

rename_column :zombies, :bio, :description rename_table :zombies, :ghouls drop_table :zombies change_column :zombies, :age, :integer, limit: 4 change_column_default :zombies, :is_admin, default: true

migration commands

RUN WITH ROLLBACK

class RemoveAgeFromZombies < ActiveRecord::Migration

remove_column :zombies, :agedef up

end

def downadd_column :zombies, :age, :integer

endend

Remove<Anything>From<Table name>

FROM THE GROUND UP

MIGRATION COMMANDS

DropZombiesTable$ rails g migration

class DropZombiesTable < ActiveRecord::Migration def up drop_table :zombies end

def down create_table :zombies do |t| t.string :name t.text :bio t.integer :age t.timestamps end endend

<Anything>

FROM THE GROUND UP

http://guides.rubyonrails.org/

BUNDLER

source 'http://rubygems.org'

gem 'rails', '3.1'gem 'sqlite3'

# Asset template enginesgem 'sass-rails', "~> 3.1"gem 'coffee-script'gem 'uglifier'

gem 'jquery-rails'

/Gemfile Contains all your application dependencies

$ bundle install Installs all application dependencies

Initially called by ‘rails new’

FROM THE GROUND UP

http://rubyonrails.org/screencasts/rails3

http://gembundler.com

CONFIGURING YOUR DATABASE

development: adapter: sqlite3 database: db/development.sqlite3 pool: 5 timeout: 5000

test: adapter: sqlite3 database: db/test.sqlite3 pool: 5 timeout: 5000

production: adapter: sqlite3 database: db/production.sqlite3 pool: 5 timeout: 5000

/config/database.yml Contains your database configuration

gem 'sqlite3'From Gemfile

database: adapter: mysql2 encoding: utf8 database: tfzombies pool: 5 username: root password: secret socket: /tmp/mysql.sock

gem 'mysql2'

For Mysql

In Gemfile

FROM THE GROUND UP

http://bit.ly/rfz2beta

to get the course....

Do Level 1

2. MODELS STILL TASTE LIKE CHICKEN

02 Callbacks

03 has_one

TABLE OF CONTENTS

01 Named Scope

04 Relationship Options

05 Using Includes

06 has_many :through

MODELS STILL TASTE LIKE CHICKEN

GOOD CODE AND BAD CODE

BAD CODE

GOOD CODE

NOTHING IN PARTICULAR

BRAINS - NO SUGAR ADDEDMODELS STILL TASTE LIKE CHICKEN

NAMED SCOPE

app/controllers/rotting_zombies_controller.rb

WE’RE OFTEN GOING TO QUERY FOR ROTTING ZOMBIES

class RottingZombiesController < ApplicationController

def index @rotting_zombies = Zombie.where(rotting: true) ... end

end

MODELS STILL TASTE LIKE CHICKEN

NAMED SCOPE

app/controllers/rotting_zombies_controller.rb

class RottingZombiesController < ApplicationController

def index @rotting_zombies = Zombie. ... end

end

app/models/zombie.rb

where(rotting: true)scope :rotting,

rotting

end

class Zombie < ActiveRecord::Base

MODELS STILL TASTE LIKE CHICKEN

NAMED SCOPEapp/models/zombie.rb

where(rotting: true)scope :rotting,

end

class Zombie < ActiveRecord::Base

scope :recent, order("created_at desc")scope :fresh, where("age < 20")

.limit(3)

Zombie.rotting.limit(5)

Zombie.recent.rotting

Zombie.recent.fresh.rotting

Use method chaining to create queries

MODELS STILL TASTE LIKE CHICKEN

CALLBACKS

app/controllers/zombies_controller.rb

Objective: When zombie age > 20, set rotting to true

def update @zombie = Zombie.find(params[:id])

if @zombie.age > 20 @zombie.rotting = true end

respond_to do |format| if @zombie.update_attributes(params[:zombie]) ... else ... end endend

MODELS STILL TASTE LIKE CHICKEN

CALLBACKS

app/models/zombie.rb

Objective: When zombie age > 20, set rotting to true

if @zombie.age > 20 @zombie.rotting = true end

def make_rotting

end

end

class Zombie < ActiveRecord::Base

before_save :make_rotting

INVALID VARIABLES

MODELS STILL TASTE LIKE CHICKEN

CALLBACKS

app/models/zombie.rb

Objective: When zombie age > 20, set rotting to true

if age > 20 self.rotting = true end

if @zombie.age > 20 @zombie.rotting = true end

def make_rotting

end

end

class Zombie < ActiveRecord::Base

before_save :make_rotting

reading attributes doesn’t need selfsetting attributes needs “self.”

MODELS STILL TASTE LIKE CHICKEN

REFACTOR

CALLBACKSapp/models/zombie.rb

if age > 20 self.rotting = true end

def make_rotting

end

end

class Zombie < ActiveRecord::Base

before_save :make_rotting

def make_rotting self.rotting = true if age > 20end

MODELS STILL TASTE LIKE CHICKEN

RETURNING FALSE WILL HALTapp/models/brain.rb

def taste return falseend

class Brain < ActiveRecord::Basebefore_save :taste

def taste falseendSAME AS

save stops if callback returns false

> b = Brain.new(:flavor => 'butter')

=> false> b.save

BE CAREFUL OF RETURN VALUES

ALL CALLBACKS1. Can use multiple callbacksbefore_validation

after_validation

before_saveafter_save

before_createafter_create

before_updateafter_update

before_destroyafter_destroy

(even of same type)2. If any return false then everything stops

(with before then save/destroy isn’t done)Examples:

before_save :encrypt_password

before_destroy :set_deleted_flag

after_create :send_welcome_email

MODELS STILL TASTE LIKE CHICKEN

03 has_one

02 Callbacks

04 Relationship Options

TABLE OF CONTENTS

05 Using Includes

01 Named Scope

06 has_many :through

MODELS STILL TASTE LIKE CHICKEN

A Tweet a Zombiebelongs to

tweetsid status zombie_id1 Where can I get a good bite to eat? 12 My left arm is missing, but I don't care. 23 I just ate some delicious brains. 34 OMG, my fingers turned green. #FML 1

zombiesid name graveyard1 Ash Glen Haven Memorial Cemetery2 Bob Chapel Hill Cemetery3 Jim My Father’s Basement

class Tweet < ActiveRecord::Base

end

app/models/tweet.rb

belongs_to :zombie

class Zombie < ActiveRecord::Base

end

app/models/zombie.rb

has_many :tweets

A Zombie Tweetshas many

Singular Plural

HAS_ONEObjective: A zombie may or may not have a (single) brain

MODELS STILL TASTE LIKE CHICKEN

HAS_ONEObjective: A zombie may or may not have a (single) brain

$ rails g model brain zombie_id:integer status:string flavor:string

Model

db/migrate/20110805011138_create_brains.rb

invoke active_recordcreate db/migrate/20110805011138_create_brains.rbcreate app/models/brain.rb

class CreateBrains < ActiveRecord::Migration def change create_table :brains do |t| t.integer :zombie_id t.string :status t.string :flavor end $ rake db:migrate

add a foreign_key index

add_index :brains, :zombie_id endend

MODELS STILL TASTE LIKE CHICKEN

HAS_ONEObjective: A zombie may or may not have a (single) brain

class Brain < ActiveRecord::Base

app/models/brain.rb app/models/zombie.rb

belongs_to :zombie has_one :brain

$ rails console

Loading development environment (Rails 3.1.0)>

=> #<Zombie id: 1, name: "Eric Allam", bio: nil, age: 27>z = Zombie.last

> z.create_brain(status: "Squashed", flavor: "Mud")=> #<Brain id: 1, zombie_id: 1, status: "Squashed", flavor: "Mud">

end end

class Zombie < ActiveRecord::Base

MODELS STILL TASTE LIKE CHICKEN

> z.brain=> #<Brain id: 1, zombie_id: 1, status: "Squashed", flavor: "Mud">

WHAT HAPPENS WHEN WE DESTROY ZOMBIE? $ rails console

Loading development environment (Rails 3.1.0)>

=> #<Zombie id: 1, name: "Eric Allam", bio: nil, age: 27>z = Zombie.find(1)

> Brain.find(1)=> #<Brain id: 1, zombie_id: 1, status: "Squashed", flavor: "Mud">

> z.destroy

=> #<Zombie id: 1, name: "Eric Allam", bio: nil, age: 27>SQL (0.4ms) DELETE FROM "zombies" WHERE "zombies"."id" = 1

> Brain.find(1)=> #<Brain id: 1, zombie_id: 1, status: "Squashed", flavor: "Mud">

ARG! ZOMBIE BRAIN STILL LIVES!MODELS STILL TASTE LIKE CHICKEN

http://railsapi.com

DESTROYING THE ZOMBIE’S BRAIN

class Zombie < ActiveRecord::Base

end

app/models/zombie.rb

class Zombie < ActiveRecord::Base has_one :brain

$ rails console

Loading development environment (Rails 3.1.0)>

=> #<Zombie id: 1, name: "Eric Allam", bio: nil, age: 27>z = Zombie.find(1)

> z.destroy SQL (0.5ms) DELETE FROM "brains" WHERE "brains"."id" = 1 SQL (0.2ms) DELETE FROM "zombies" WHERE "zombies"."id" = 1

, dependent: :destroyend

MODELS STILL TASTE LIKE CHICKEN

RELATIONSHIP OPTIONS

dependent: :destroy will call destroy on associated objects

foreign_key: :undead_id changes the associated key (i.e. zombie_id)

primary_key: :zid changes the primary key (i.e. id)

validate: true when zombie validates brain will too

And many more(read the documentation)

MODELS STILL TASTE LIKE CHICKEN

app/views/zombies/index.html.erb

RELATIONSHIP “INCLUDE” OPTION

<% @zombies.each do |zombie| %> <tr> <td><%= zombie.name %></td> <td><%= zombie.brain.flavor %></td> </tr> <% end %>

Zombie Load (0.1ms) SELECT * FROM "zombies" Brain Load (0.2ms) SELECT * FROM "brains" WHERE "zombie_id" = 4 Brain Load (0.1ms) SELECT * FROM "brains" WHERE "zombie_id" = 5 Brain Load (0.1ms) SELECT * FROM "brains" WHERE "zombie_id" = 6 Brain Load (0.1ms) SELECT * FROM "brains" WHERE "zombie_id" = 7

app/controllers/zombies_controller.rb

.alldef index @zombies = Zombie

Server runs

QUERY FOR EACH BRAIN!N + 1 ISSUE

MODELS STILL TASTE LIKE CHICKEN

app/views/zombies/index.html.erb

RELATIONSHIP “INCLUDE” OPTION

<% @zombies.each do |zombie| %> <tr> <td><%= zombie.name %></td> <td><%= zombie.brain.flavor %></td> </tr> <% end %>

Zombie Load (1.8ms) SELECT * FROM "zombies" Brain Load (0.3ms) SELECT * FROM "brains" WHERE "zombie_id" IN (4, 5, 6, 7)

app/controllers/zombies_controller.rb

.alldef index @zombies = Zombie

In the log files

.includes(:brain)

MODELS STILL TASTE LIKE CHICKEN

HAS_MANY :THROUGHObjective: A zombie may play multiple roles

Captain Scout Soldier Brain Taster

MODELS STILL TASTE LIKE CHICKEN

HAS_MANY :THROUGHObjective: A zombie may play multiple roles

scoutcaptainsoldierbrain taster

Zombie Rolerole_id

belongs_to

ONLY GOING TO WORK

IF A ZOMBIE HAS ONE ROLE!

MODELS STILL TASTE LIKE CHICKEN

HAS_MANY :THROUGHObjective: A zombie may play multiple roles

Zombie Role

belongs_to

Assignments

has_many

has_manybelongs_to

zombie_id

role_id

MODELS STILL TASTE LIKE CHICKEN

HAS_MANY :THROUGHObjective: A zombie may play multiple roles

db/migrate/xxx_create_roles.rb

class CreateRoles < ... def change create_table :roles do |t| t.string :title end endend

db/migrate/xxx_create_assignments.rb

class CreateAssignments < ... def change create_table :assignments do |t|

t.integer :t.integer :

add_index :assignments, :zombie_idadd_index :assignments, :role_id

endend

zombie_idrole_id

Zombie Role

belongs_to

Assignments

has_many

has_manybelongs_to

end

Zombie RoleAssignments

class Assignment < ... belongs_to :zombie belongs_to :roleend

app/models/role.rb

app/models/zombie.rb app/models/assignment.rb

class Zombie < ActiveRecord::Base has_many :assignmentshas_many :roles, through: :assignments

end

class Role < ActiveRecord::Base has_many :assignmentshas_many :zombies, through: :assignments

end

has_many :through

MODELS STILL TASTE LIKE CHICKEN

app/models/zombie.rb

class Zombie < ActiveRecord::Base has_many :assignmentshas_many :roles, through: :assignments

end

HAS_MANY :THROUGH

$ rails console

>=> #<Zombie id: 1, name: "Eric Allam", bio: nil, age: 27>

z = Zombie.last

> z.roles << Role.find_by_title("Captain")Role Load (0.2ms) SELECT * FROM "roles" WHERE "title" = 'Captain' LIMIT 1SQL (0.4ms) INSERT INTO "assignments" ("role_id", "zombie_id") VALUES (2, 1)

> z.rolesRole Load (0.3ms) SELECT "roles".* FROM "roles" INNER JOIN "assignments" ON "roles"."id" = "assignments"."role_id" WHERE "assignments"."zombie_id" = 1

<< is a function on Array it’s the same as z.roles.push

MODELS STILL TASTE LIKE CHICKEN

3. REST IN PIECES

02 Revisit URL Helpers

03 Forms & Input Helpers

TABLE OF CONTENTS

01 Understanding REST

04 Nested Resources

05 View Partials

06 Other View Helpers

REST IN PIECES

WHAT IS REST?Roy Fielding

published a dissertation in 2000 entitled

“Architectural Styles and the Design of Network-based Software Architectures”

Example of REST

HTTP Protocol

1. Resources addressable through URI

(noun)

2. Standard methods (verb)GET, POST, PUT, DELETE

RESTful Web Servicesby Leonard Richardson

& Sam Ruby

FOR MORE INFO

Representational State Transfer

REST IN PIECES

HOW DID RAILS BECOME RESTFUL?app/controllers/users_controller.rb

class UserController < ApplicationController def login_to_website ... end def subscribe_to_mailing_list ... end

def process_credit_card_transaction ... end

end

1. Lends itself to huge controllers2. Multiple models per controller

Bad Because...

REST IN PIECES

RAILS 1 RECOMMENDED RESTapp/controllers/users_controller.rb

class UserController < ApplicationController # GET /users/show/3 def show # POST /users/create def create

# POST /users/update/3 def update

# POST /users/destroy/3 def destroy

end

We’re repeating the VERB!

Bad Because...

REST IN PIECES

USING PROPER REST

Actions

SQL

REST

show create update destroy

select create update delete

get post post post

Actions

SQL

REST

show create update destroy

select create update delete

get post put delete

Rails 2+

Rails 1

USING THE REST VERBS

USING PROPER REST

Rails 1

WITH PROPER VERBS WE CAN SIMPLIFY THE URI

Verb Noun

Rails 2+

Verb Noun

/users/show/3

/users/create

/users/update/3

/users/destroy/3

GET

POST

POST

POST

GET

POST

PUT

DELETE

/users/3

/users

/users/3

/users/3

REST IN PIECES

REST CHALLENGES

REST IN PIECES

Show user

Verb Noun

Activity

? ?

REST CHALLENGES

REST IN PIECES

Show user

Verb Noun

Activity

GET User/users/3

REST CHALLENGES

REST IN PIECES

Create user

Verb Noun

Activity

POST User/users

REST CHALLENGES

REST IN PIECES

Update user

Verb Noun

Activity

PUT User/users/3

REST CHALLENGES

REST IN PIECES

Destroy user

Verb Noun

Activity

DELETE User/users/3

REST CHALLENGES

REST IN PIECES

Play Global Thermonuclear War

Verb Noun

Activity

POST GlobalThermonuclearWar

/global_thermonuclear_wars

REST CHALLENGES

REST IN PIECES

Process credit card transaction

Verb Noun

Activity

POST Transaction/orders/5/credit_card_transactions

REST CHALLENGES

REST IN PIECES

Subscribe to mailing list

Verb Noun

Activity

POST Subscription/mailing_lists/5/subscriptions

REST CHALLENGES

REST IN PIECES

Logout

Verb Noun

Activity

DELETE Session/sessions

class UserController # GET /users/3 def show # POST /users def create

# PUT /users/3 def update

# DELETE /users/3 def destroy

end

REVISED RAILS CONTROLLERS

class UserController # GET /users/show/3 def show # POST /users/create def create

# POST /users/update/3 def update

# POST /users/destroy/3 def destroy

end

Rails 1 Rails 2+

REST IN PIECES

class UserController # GET /users/3 def show # POST /users def create

# PUT /users/3 def update

# DELETE /users/3 def destroy

end

RESTFUL LINKS

<%= link_to 'show', zombie %>

<%= link_to 'create', zombie, method: :post %>

<%= link_to 'delete', zombie, method: :delete %>

<%= link_to 'update', zombie, method: :put %>

zombie = Zombie.find(2)

To link to each action

BUT BROWSERS DON’T SUPPORT

PUT AND DELETE?!?!REST IN PIECES

EXPLAINING PUT AND DELETE

<%= link_to 'delete', zombie, method: :delete %>

<%= link_to 'update', zombie, method: :put %>

<a href="/zombies/4" data-method="put" rel="nofollow">update</a> <a href="/zombies/4" data-method="delete" rel="nofollow">delete</a>

HTML5 data attribute

Creates by unobtrusive JavaScript

<form method="post" action="/zombies/4"> <input name="_method" value="delete" /></form>

_method set as method (verb) by Rails(All under the covers)

REST IN PIECES

app/config/routes.rb

RAKE ROUTES

$ rake routes

resources :zombies

zombies GET /zombies {:action=>"index", :controller=>"zombies"} POST /zombies {:action=>"create", :controller=>"zombies"} new_zombie GET /zombies/new {:action=>"new", :controller=>"zombies"}edit_zombie GET /zombies/:id/edit {:action=>"edit", :controller=>"zombies"} zombie GET /zombies/:id {:action=>"show", :controller=>"zombies"} PUT /zombies/:id {:action=>"update", :controller=>"zombies"} DELETE /zombies/:id {:action=>"destroy", :controller=>"zombies"}

<%= link_to 'All Zombies', zombies_path %>

<%= link_to 'New Zombie', new_zombie_path %>

<%= link_to 'Edit Zombie', edit_zombie_path(@zombie) %>

<%= link_to 'Show Zombie', zombie_path(@zombie) %>

<%= link_to 'Show Zombie', @zombie %>

Creates our RESTful routes

PATH AND URL HELPERS

zombies_path

new_zombie_path

Relative Path/zombies

/zombies/new

zombies_url

new_zombie_url

http://localhost:3000/zombies

http://localhost:3000/zombies/new

Absolute Path

02 Revisit URL Helpers

03 Forms & Input Helpers

TABLE OF CONTENTS

01 Understanding REST

04 Nested Resources

05 View Partials

06 Other View Helpers

REST IN PIECES

CREATING A FORMBoth create and update zombie form

If @zombie isn’t saved to the database yet<form action="/zombies" method="post">

If @zombie is saved to the database<form action="/zombies/8" method="post"> <input name="_method" type="hidden" value="put" />

<%= form_for(@zombie) do |f| %>...

<% end %>

REST IN PIECES

SUBMIT BUTTONBoth create and update zombie form

If @zombie isn’t saved to the database yet

If @zombie is already saved to the database

<%= form_for(@zombie) do |f| %>...

<% end %><%= f.submit %>

<input name="commit" type="submit" value="Update Zombie" />

<input name="commit" type="submit" value="Create Zombie" />

REST IN PIECES

TEXT_FIELD HELPER

If @zombie isn’t saved to the database yet

If @zombie is already saved to the database

<%= form_for(@zombie) do |f| %>...

<% end %><%= f.submit %>

<input name="zombie[name]" size="30" type="text" value="Eric" />

<input name="zombie[name]" size="30" type="text" />

<%= f.text_field :name %>

If @zombie.name has a validation error<div class="field_with_errors"> <input name="zombie[name]" size="30" type="text" value="" /></div>

:params => {:zombie => {:name => "Eric"}}

When submitted, request parameters

Can be viewed from log

REST IN PIECES

LABEL HELPER

Will render out

<%= form_for(@zombie) do |f| %>...

<% end %><%= f.submit %>

<label for="zombie_name">Name</label>

<%= f.text_field :name %>

If @zombie.name has a validation error

<div class="field_with_errors"> <label for="zombie_name">Name</label></div>

<%= f.label :name %><br />

REST IN PIECES

INPUT HELPERS<%= f.text_area :bio %> Renders a multiline text area

List of radio buttons (without their labels)

<%= f.check_box :rotting %> Check box used for booleans

<%= f.radio_button :decomp, 'fresh', checked: true %><%= f.radio_button :decomp, 'rotting' %><%= f.radio_button :decomp, 'stale' %>

Select box with three options<%= f.select :decomp, ['fresh', 'rotting', 'stale'] %>

Select box with three options, each with a numerical value<%= f.select :decomp, [['fresh', 1], ['rotting', 2], ['stale', 3]] %>

REST IN PIECES

ALTERNATE TEXT INPUT HELPERS<%= f.password_field :password %>

<%= f.number_field :price %>

<%= f.range_field :quantity %>

<%= f.email_field :email %>

<%= f.url_field :website %>

<%= f.telephone_field :mobile %>

type=”<field>”

REST IN PIECES

NESTED ROUTESapp/config/routes.rb

/tweets/2

/tweets?zombie_id=4

NOT VERY RESTFUL, TWEETS SHOULDN’T EXIST WITHOUT A ZOMBIE!!

To find a tweet

To find all zombie’s tweet

TwitterForZombies::Application.routes.draw do

end

resources :tweetsresources :zombies

REST IN PIECES

NESTED ROUTESapp/config/routes.rb

/tweets/2

/zombies/4/tweets

To find a tweet

To find all zombie’s tweet

/zombies/4

resources :tweetsresources :zombies

TwitterForZombies::Application.routes.draw do

end

do

end

To make this work, we must:

1. Update the controller2. Update all the links & form_for

REST IN PIECES

UPDATING THE CONTROLLER FOR NESTING/tweets/2 /zombies/4/tweets/zombies/4

params = {:zombie_id => 4, :id => 2} params = {:zombie_id => 4}

app/controller/tweets_controller.rb

@tweet

class TweetsController < ApplicationControllerbefore_filter :get_zombie

@zombie = Zombie.find(params[:zombie_id])def get_zombie

end

def show

end

end

....find(params[:id])@zombie.tweets.=

REST IN PIECES

UPDATING THE CONTROLLER FOR NESTING/zombies/4/tweetsparams = {:zombie_id => 4}

class TweetsController < ApplicationControllerbefore_filter :get_zombie

@zombie = Zombie.find(params[:zombie_id])def get_zombie

end

def index

end

end

...

app/controller/tweets_controller.rb

@tweets @zombie.tweets=

REST IN PIECES

NESTED RAKE ROUTES

zombie_tweets GET /zombies/:zombie_id/tweets POST /zombies/:zombie_id/tweets new_zombie_tweet GET /zombies/:zombie_id/tweetsedit_zombie_tweet GET /zombies/:zombie_id/tweets/:id/edit zombie_tweet GET /zombies/:zombie_id/tweets/:id PUT /zombies/:zombie_id/tweets/:id DELETE /zombies/:zombie_id/tweets/:id

$ rake routes

<%= link_to "#{@zombie.name}’s Tweets", zombie_tweets_path(@zombie) %>

<%= link_to 'New Tweet', new_zombie_tweet_path(@zombie) %>

<%= link_to 'Edit', edit_zombie_tweet_path(@zombie, tweet) %>

<%= link_to 'Show', zombie_path(@zombie, tweet) %>

<%= link_to 'Show', [@zombie, tweet] %>

<%= link_to 'Destroy', [@zombie, tweet], method: :delete %>

REST IN PIECES

UPDATING THE VIEW FOR NESTINGapp/views/tweets/index.html.erb

<% @tweets.each do |tweet| %> <tr> <td><%= tweet.body %></td> <td> </td><td> </td>

<% end %>

<%= link_to 'New Tweet', new_zombie_tweet_path(@zombie) %>

<%= link_to 'Edit', edit_zombie_tweet_path(@zombie, tweet) %><%= link_to 'Show', [@zombie, tweet] %>

<%= link_to 'Destroy', [@zombie, tweet], method: :delete %></tr><td> </td>

app/views/tweets/_form.html.erb

<%= form_for([@zombie, @tweet]) do |f| %>

REST IN PIECES

UPDATING ROUTES IN THE CONTROLLERapp/controller/tweets_controller.rb

... def create @tweet = weets.new(params[:tweet])

respond_to do |format| if @tweet.save format.html { redirect_to @tweet,

notice: 'Tweet was successfully created.' }format.json { render json: @tweet,

status: :created, location: @tweet }

else format.html { render action: "new" } format.json { render json: @tweet.errors, status: :unprocessable_entity } end end end

T

REST IN PIECES

UPDATING ROUTES IN THE CONTROLLER

... def create @tweet = weets.new(params[:tweet])

respond_to do |format| if @tweet.save format.html { redirect_to @tweet ,

notice: 'Tweet was successfully created.' }format.json { render json: @tweet ,

status: :created, location: @tweet }

else format.html { render action: "new" } format.json { render json: @tweet.errors, status: :unprocessable_entity } end end end

@zombie.t

[@zombie, ]

[@zombie, ]

[@zombie, ]

app/controller/tweets_controller.rb

REST IN PIECES

02 Revisit URL Helpers

03 Forms & Input Helpers

TABLE OF CONTENTS

01 Understanding REST

04 Nested Resources

05 View Partials

06 Other View Helpers

REST IN PIECES

PARTIALS - HOW WE REUSE VIEW CODE

app/views/tweets/_form.html.erb

<%= form_for([@zombie, @tweet]) do |f| %>

Partials start with an underscore

app/views/tweets/new.html.erb

<h1>New tweet</h1>

<%= render 'form' %>

<%= link_to 'Back', zombie_tweets_path(@zombie) %>

app/views/tweets/edit.html.erb

<h1>Editing tweet</h1>

<%= render 'form' %>

REST IN PIECES

VIEW HELPERS - MAKING STRINGS SAFE

<%= @tweet.body %>Rails 2

Rails 3

<%= h @tweet.body %>

<%= raw @tweet.body %> <%= @tweet.body %>Rails 3

(unsafe)

(unsafe)

(safe)

(safe)

Rails 2

<script>

&lt;script&gt;

make safe

REST IN PIECES

ADDITIONAL VIEW HELPERS<% @tweets.each do |tweet| %> <div id="tweet_<%= tweet.id %>" class="tweet"> <%= tweet.body %> </div><% end %>

<% @tweets.each do |tweet| %> <%= div_for tweet do %> <%= tweet.body %> <% end %><% end %>

SAME AS

dom_id(@tweet) tweet_2

REST IN PIECES

ADDITIONAL VIEW HELPERS

<%= truncate("I need brains!", :length => 10) %>

I need bra...

I see <%= pluralize(Zombie.count, "zombie") %>

<%= truncate("I need brains!", :length => 10, :separator => ' ') %>

I need...

I see 1 zombieI see 2 zombies

with 1with 2

His name was <%= @zombie.name.titleize %>

His name was Ash Williams

REST IN PIECES

ADDITIONAL VIEW HELPERS

Ash’s zombie roles are <%= @role_names.to_sentence %>

Ash’s zombie roles are Captain, Soldier, and Brain Taster

Price is <%= number_to_currency 13.5 %>

He was buried alive <%= time_ago_in_words @zombie.created_at %> ago

He was buried alive 2 days ago

Price is $13.50

Ash is <%= number_to_human 13234355423 %> years old

Ash is 13.2 billion years old

REST IN PIECES

http://guides.rubyonrails.org/

4. ASSET PACKAGING AND MAILING

02 Sending Attachments in Mail

04 Asset Tags

03 The Asset Pipeline

TABLE OF CONTENTS

05 CoffeeScript

01 Creating and Sending Mail

06 SCSS

ASSET PACKAGING AND MAILING

07 JavaScript Manifest

CREATE A ZOMBIE MAILERObjective: Create emails for decomposition change and lost brain

$ rails g mailer ZombieMailer decomp_change lost_brain

Mailer

app/mailers/zombie_mailer.rb

create app/mailers/zombie_mailer.rbinvoke erbcreate app/views/zombie_mailercreate app/views/zombie_mailer/decomp_change.text.erbcreate app/views/zombie_mailer/lost_brain.text.erb

INSTANCE VARS WE WANT IN OUR VIEWS

class ZombieMailer < ActionMailer::Base default from: "[email protected]"

def decomp_change

end ...end

@greeting = "Hi"

mail to: "[email protected]"

CREATE A ZOMBIE MAILERapp/mailers/zombie_mailer.rbclass ZombieMailer < ActionMailer::Base default from: "[email protected]"

def decomp_change

end ...end

(zombie)@zombie = zombie@last_tweet = @zombie.tweets.last

mail to: @zombie.email, subject: 'Your decomp stage has changed'

from: [email protected]: [email protected]: [email protected]_to: [email protected]

CAN ALSO BE DEFAULTS

attachments['z.pdf'] = File.read("#{Rails.root}/public/zombie.pdf")

ASSET PACKAGING AND MAILING

MAILER VIEWSapp/views/zombie_mailer/decomp_change.text.erb

Greetings <%= @zombie.name %>,

Your decomposition state is now <%= @zombie.decomp %> and your last tweet was: <%= @last_tweet.body %> Good luck!

If you want HTML emails, you just rename text to htmlapp/views/zombie_mailer/decomp_change.html.erb

<h1>Greetings <%= @zombie.name %>,</h1>

<p>Your decomposition state is now <%= @zombie.decomp %> and your last tweet was: <%= @last_tweet.body %></p> <%= link_to "View yourself", zombie_url(@zombie) %>

If you want multipart (both html & text) just make 2 files

ASSET PACKAGING AND MAILING

SENDING MAILapp/models/zombie.rb

after_save :decomp_change_notification, if: :decomp_changed?

private

def decomp_change_notificationZombieMailer.decomp_change(self).deliver

class Zombie < ActiveRecord::Base

endend

ASSET PACKAGING AND MAILING

MAD MIMIMass mailing is best done outside of Rails.

madmimi.com

gem 'madmimi'Gemfile

mimi = MadMimi.new('<email>', '<api_key>')

mimi.add_to_list('[email protected]', 'newsletter')

mimi.remove_from_list('[email protected]', 'newsletter')

mimi.memberships('[email protected]')

then ‘bundle install’

ASSET PACKAGING AND MAILING

madmimi.com

02 Sending Attachments in Mail

04 Asset Tags

03 The Asset Pipeline

TABLE OF CONTENTS

05 CoffeeScript

01 Creating and Sending Mail

06 SCSS

07 JavaScript Manifest

ASSET PACKAGING AND MAILING

ASSET PACKAGING AND MAILING

public

stylesheets

zombie_twitter

javascriptsimages

Rails 3.0

JUNK DRAWERS

ASSET PIPELINE

stylesheetsjavascriptsimages

assets

ASSET PIPELINE

app

zombie_twitter

Rails 3.1

assets

lib

assets

vendor

Specific App Codeapp

My Shared Codelib

3rd Party Codevendor

/assets/custom.jsLooks in all paths

/assets/rails.png

ASSET PACKAGING AND MAILING

ASSET TAG HELPERS

<%= image_tag "rails.png" %>

<%= stylesheet_link_tag "style" %>

<%= javascript_include_tag "custom" %>

/assets/rails.png<img alt="Rails" src=" " />

<link href="/assets/style.css" media="screen" rel="stylesheet" type="text/css" />

/assets/custom.js<script src=" " type="text/javascript"></script>

<img alt="Rails" src="/assets/rails-af27b6a414e6da000035031.png" />

FINGERPRINT

In production

ASSET PACKAGING AND MAILING

ASSET PATHS IN STYLESHEETS<img alt="Rails" src="/assets/rails-af27b6a414e6da000035031.png" />

Allows for better caching

app/assets/stylesheets/zombie.css

form.new_zombie input.submit { background-image: url(/assets/button.png);} NO CACHING

form.new_zombie input.submit { background-image: url(<%= asset_path('button.png') %>);}

/assets/button-af27b6a414e6da000035031.png

app/assets/stylesheets/zombie.css.erb

ASSET PACKAGING AND MAILING

SCSS - SASSY CSSSCSS is an extension to CSS3 adding nested rules, variables, mixins, selector inheritance, and more. You get it by default in Rails 3.1.

app/assets/stylesheets/zombie.css.scss

form.new_zombie {border: 1px dashed gray;

}

}

}

form.new_zombie

form.new_zombie

.field#bio {

input.submit {background-image: url(<%= asset_path('button.png') %>);

display: none;

.erb

ARRG! WE ARE REPEATING OURSELVES!!

Lets do some nesting!

ASSET PACKAGING AND MAILING

SCSS - SASSY CSS

app/assets/stylesheets/zombie.css.scss

form.new_zombie {border: 1px dashed gray;

}

}

form.new_zombieform.new_zombie

.field#bio {

input.submit {background-image: url(<%= asset_path('button.png') %>);

display: none;

.erb

}

Using nested rules

ASSET PACKAGING AND MAILING

COFFEESCRIPT

app/views/zombies/_form.html.erb

CoffeeScript is a programming langugage that compiles into JavaScript. You get it by default in Rails 3.1.

<div class="field" id="bio"> <%= f.label :bio %><br /> <%= f.text_area :bio %></div>

...<% end %>

<%= form_for(@zombie) do |f| %> ...

<a href="#" id="bio-toggle">Show Bio Field</a>

Objective: When Show Bio Field is clicked, make bio field appear, and Show link disappear.

ASSET PACKAGING AND MAILING

COFFEESCRIPTapp/views/zombies/_form.html.erb

<div class="field" id="bio"> <%= f.label :bio %><br /> <%= f.text_area :bio %></div>

<a href="#" id="show-bio">Show Bio Field</a>

First with regular jQuery JavaScript

.field#bio { display: none;}

event.preventDefault();$(this).hide();$('.field#bio').show();

css

app/assets/javascripts/zombies.js

ASSET PACKAGING AND MAILING

$('#show-bio').click(function(event) {

}

$(document).ready(function(){

}

COFFEESCRIPT

Now with CoffeeScript

app/assets/javascripts/zombies.js

app/assets/javascripts/zombies.js.coffee

$('#show-bio').click(function(event) {event.preventDefault();$(this).hide();$('.field#bio').show();

$(document).ready(function(){

$(document).ready ->$('#show-bio').click (event) ->

event.preventDefault()$(this).hide()$('.field#bio').show()

ASSET PACKAGING AND MAILING

}}

REMEMBER THE SCAFFOLD?$ rails g scaffold zombie name:string bio:text age:integer

route resources :zombies

create app/controllers/zombies_controller.rb

create app/views/zombiescreate app/views/zombies/index.html.erbcreate app/views/zombies/edit.html.erbcreate app/views/zombies/show.html.erbcreate app/views/zombies/new.html.erb

Model

Routing

Controller

View

PLUS TESTS, HELPERS, AND ASSETS

createinvoke active_record

create app/models/zombie.rbdb/migrate/20110701230207_create_zombies.rb

ASSET PACKAGING AND MAILING

THE DEFAULT ASSET GENERATION$ rails g scaffold zombie name:string bio:text age:integer

Model

Assets

createinvoke active_record

create app/models/zombie.rbdb/migrate/20110701230207_create_zombies.rb

invoke assetscreate app/assets/javascripts/zombies.js.coffeeinvoke scsscreate app/assets/stylesheets/zombies.css.scss

gem 'sass-rails'

From Gemfile

gem 'coffee-script'

TO REMOVE, TAKE THEM OUTAND RERUN “BUNDLE INSTALL”

ASSET PACKAGING AND MAILING

THE DEFAULT ASSET GENERATIONapp/assets/javascripts/zombies.js.coffee

app/assets/javascripts/tweets.js.coffee

app/assets/javascripts/brains.js.coffee

app/assets/javascripts/roles.js.coffee

<%= javascript_include_tag "zombies", "tweets", "brains", "roles" %>

Do we need to include each of these files?

NO, because of the application.js & Sprockets. <%= javascript_include_tag "application" %>

In here is a Manifest of the JavaScript libraries we use.

ASSET PACKAGING AND MAILING

SPROCKETS

/app/assets/javascripts/application.js

//= require jquery include the jQuery framework JavaScript

include Rails specific unobtrusive JavaScript//= require jquery_ujs

//= require_tree . include all files in this directory

Combines all files<script src="/assets/application.js" type="text/javascript"></script>

In production

<script src="/assets/application-af27b6a414e6da000035031.js" type="text/javascript"></script>

To precompile all files into /public/assets/ (on your server)

$ rake assets:precompile BY DEFAULT IT MINIFIES ALL CODE

In here is a Manifest of the JavaScript libraries we use.

ASSET PACKAGING AND MAILING

REQUIRING OTHER FILESlib/assets/javascripts/shared.js.coffee

vendor/assets/javascripts/friend.js

/app/assets/javascripts/application.js

//= require jquery//= require jquery_ujs

//= require_tree .

//= require shared//= require friend

Can be used to specify order

ASSET PACKAGING AND MAILING

ASSET STYLESHEETS/app/assets/stylesheets/application.css

Specifies where to insert content in this file

form.new_zombie {border: 1px dashed gray;

}INCLUDED BEFORE EVERYTHING ELSE

/* *= require_self *= require_tree . */

ASSET PACKAGING AND MAILING

ASSET STYLESHEETS/app/assets/stylesheets/application.css

form.new_zombie {border: 1px dashed gray;

}

/*

*= require_self *= require_tree . */

*= require reset

<link href="/assets/application.css" media="screen" rel="stylesheet" type="text/css" />

In production also going to be minified<link href="/assets/application-af27b6414e6da0000.css" media="screen" rel="stylesheet" type="text/css" />

Included before the content in this file

ASSET PACKAGING AND MAILING

http://guides.rubyonrails.org/

5. RENDERING EXTREMITIES

02 Custom RESTful Routes

04 Creating AJAX Links

03 Rendering Custom JSON

TABLE OF CONTENTS

05 AJAXified Forms

01 Controller Rendering Options

06 Sending Server JavaScript

RENDERING EXTREMITIES

07 AJAX Using JSON Data

Objective: Respond to HTML and JSON

DEFAULT ACTION RENDERINGapp/controllers/zombies_controller.rb

BY DEFAULT LOOKS FOR

class ZombiesController < ApplicationController

end

app/views/zombies/show.html.erb

end

def show @zombie = Zombie.find(params[:id])

RENDERING EXTREMITIES

Objective: If dead, render ‘/view/zombies/dead_again.html.erb’

Objective: Respond to HTML and JSON

DEFAULT RESPOND_TO BLOCK

class ZombiesController < ApplicationControllerdef show @zombie = Zombie.find(params[:id])

endend

# show.html.erb format.json { render json: @zombie }end

respond_to do |format|format.html

app/controllers/zombies_controller.rb

RENDERING EXTREMITIES

Objective: If dead, render ‘/view/zombies/dead_again.html.erb’

Objective: Remove JSON, we don’t need it.

SPECIFYING A CUSTOM RENDER

class ZombiesController < ApplicationControllerdef show @zombie = Zombie.find(params[:id])

endend

format.json { render json: @zombie }end

respond_to do |format|format.html

if @zombie.decomp == 'Dead (again)' render :dead_againend

do

end IF NOT DEAD, RENDERS SHOW

app/controllers/zombies_controller.rb

RENDERING EXTREMITIES

Objective: Remove JSON, we don’t need it.

RESPONDS_TO JUST ONE THING

class ZombiesController < ApplicationControllerdef show @zombie = Zombie.find(params[:id])

endend

if @zombie.decomp == 'Dead (again)' render :dead_againend

app/controllers/zombies_controller.rb

Objective: We ONLY need JSON

RENDERING EXTREMITIES

Examples

IF WE ONLY NEED JSON

class ZombiesController < ApplicationControllerdef show @zombie = Zombie.find(params[:id])

endend

render json: @zombie

:ok DEFAULTHTTP Status Codes

:unauthorized

render json: @zombie.errors, status: :unprocessable_entity

200201422

401102404

render json: @zombie, status: :created, location: @zombie

URL FOR NEW ZOMBIE

:created:unprocessable_entity

:processing:not_found

app/controllers/zombies_controller.rb

RENDERING EXTREMITIES

CUSTOM ROUTE CHALLENGECreate a new action in the Zombies controller which returns JUST decomp in JSON, and if “dead (again)” give status :unprocessable_entity

2 Create a new action in the ZombiesController1 Add a new member route

RENDERING EXTREMITIES

1 Add a new member routehttp://localhost:3000/zombies/4/decomp

config/routes.rb

match 'zombies/:id/decomp' => 'Zombies#decomp', :as => :decomp_zombie

resources :zombies do resources :tweets

endget :decomp, on: :member

SAME AS

decomp_zombie GET /zombies/:id/decomp {:action=>"decomp", :controller=>"zombies"}

$ rake routes

Creates a custom RESTful Route

<%= link_to 'Get Decomposition Status', decomp_zombie_path(@zombie) %>

Then we can use this route helper:

RENDERING EXTREMITIES

2 TYPES OF CUSTOM ROUTES:member Acts on a single resource:collection Acts on a collection of resources

Route URL Route Helperget :decomp, on: :member /zombies/:id/decomp decomp_zombie_path(@zombie)

put :decay, on: :member /zombies/:id/decay decay_zombie_path(@zombie)

get :fresh, on: :collection /zombies/fresh fresh_zombies_path

post :search, on: :collection /zombies/search search_zombies_path

<%= form_tag(search_zombies_path) do |f| %>

<%= link_to 'Fresh zombies', fresh_zombies_path %>

Examples:

RENDERING EXTREMITIES

CUSTOM ROUTE CHALLENGECreate a new action in the Zombies controller which returns JUST decomp in JSON, and if “dead (again)” give status :unprocessable_entity

2 Create a new action in the ZombiesController

1 Add a new member routeresources :zombies do resources :tweets

endget :decomp, on: :member

RENDERING EXTREMITIES

2 Create a new action in the ZombiesController

/app/controllers/zombies_controller.rb

{"age":25,"bio":"I am zombified","created_at":"2011-08-06T20:22:11Z","decomp":"Fresh","email":"[email protected]","id":6,"name":"Eric","rotting":false,"updated_at":"2011-08-06T20:22:11Z"

}

class ZombiesController < ApplicationController def decomp

@zombie = Zombie.find(params[:id])if @zombie.decomp == 'Dead (again)'render json: @zombie, status: :unprocessable_entity

elserender json: @zombie, status: :ok

end endend

BUT WE WANT

ONLY DECOMP!

RENDERING EXTREMITIES

{ "name":"Eric" }@zombie.to_json(only: :name)

@zombie.to_json(only: [:name, :age]) { "name":"Eric", age:25 }

@zombie.to_json(except: [:created_at, :updated_at, :id, :email, :bio])

{ "age":25, "decomp":"Fresh", "name":"Eric", "rotting":false }

{"age":25,"bio":"I am zombified","decomp":"Fresh","email":"[email protected]","name":"Eric","rotting":false,"brain": {"flavor":"Butter","status":"Smashed","zombie_id":3}

}

CUSTOMIZE JSON RESPONSE

include: :brain, except: [:created_at, :updated_at, :id]

HIDE THAT TOO

@zombie.to_json( )

RENDERING EXTREMITIES

SETTING THE JSON RESPONSE DEFAULT

Set the default JSON Response

/app/models/zombie.rb

{"age":25,"bio":"I am zombified","decomp":"Fresh","email":"[email protected]","name":"Eric","rotting":false"brain": {"flavor":"Butter","status":"Smashed","zombie_id":3}

}

@zombie.to_json

include: :brain, except: [:created_at, :updated_at, :id](options ||{ })

class Zombie < ActiveRecord::Base ... def as_json(options = nil)

super

end end

RENDERING EXTREMITIES

CUSTOM ROUTE CHALLENGECreate a new action in the Zombies controller which returns JUST decomp in JSON, and if “dead (again)” give status :unprocessable_entity

2 Create a new action in the ZombiesController

1 Add a new member route resources :zombies do resources :tweets

endget :decomp, on: :member

app/controllers/zombies_controller.rb

class ZombiesController < ApplicationController def decomp

@zombie = Zombie.find(params[:id])if @zombie.decomp == 'Dead (again)'render json: @zombie.to_json(only: :decomp), status: :unprocessable_entity

else render json: @zombie.to_json(only: :decomp) end endend {"decomp":"Fresh"}

02 Custom RESTful Routes

04 Creating AJAX Links

03 Rendering Custom JSON

TABLE OF CONTENTS

05 AJAXified Forms

01 Controller Rendering Options

06 Sending Server JavaScript

07 AJAX using JSON data

RENDERING EXTREMITIES

ADD AJAXIFIED DELETE LINKWhen Delete is pressed do a fade out, instead of a page refresh.

2 Allow the controller to accept the JavaScript call

1 Make the link a remote call

3 Write the JavaScript to send back to the client

RENDERING EXTREMITIES

1 Make the link a remote call

/app/views/zombies/index.html.erb

<% @zombies.each do |zombie| %><%= div_for zombie do %><%= link_to "Zombie #{zombie.name}", zombie %>

</div> <% end %><% end %>

%><%= link_to 'delete', zombie, method: :delete

<div class="actions"> <%= link_to 'edit', edit_zombie_path(zombie) %>

RENDERING EXTREMITIES

1 Make the link a remote call

/app/views/zombies/index.html.erb

<% @zombies.each do |zombie| %><%= div_for zombie do %><%= link_to "Zombie #{zombie.name}", zombie %>

</div> <% end %><% end %>

%>

<a href="/zombies/5" data-method="delete" data-remote="true" rel="nofollow">delete</a>

, remote: true

GENERATES

Rails will use unobtrusive JavaScript to do AJAX.

<%= link_to 'delete', zombie, method: :delete

<div class="actions"> <%= link_to 'edit', edit_zombie_path(zombie) %>

RENDERING EXTREMITIES

ADD AJAXIFIED DELETE LINKWhen Delete is pressed do a fade out, instead of a page refresh.

2 Allow the controller to accept the JavaScript call

1 Make the link a remote call

3 Write the JavaScript to send back to the client

%><%= link_to 'delete', zombie, method: :delete, remote: true

RENDERING EXTREMITIES

2 Allow the controller to accept the JavaScript call

app/controllers/zombies_controller.rbclass ZombiesController < ApplicationController

format.js

CAN RESPOND WITH JAVASCRIPT

respond_to do |format|format.html { redirect_to zombies_url }format.json { head :ok }

end endend

def destroy @zombie = Zombie.find(params[:id]) @zombie.destroy

RENDERING EXTREMITIES

ADD AJAXIFIED DELETE LINKWhen Delete is pressed do a fade out, instead of a page refresh.

2 Allow the controller to accept the JavaScript call

1 Make the link a remote call

3 Write the JavaScript to send back to the client

%>, remote: true<%= link_to 'delete', zombie, method: :delete

format.jsrespond_to do |format|

end

app/views/zombies/destroy.js.erb

$('#<%= dom_id(@zombie) %>').fadeOut();

RENDERING EXTREMITIES

ADD AJAXIFIED DELETE LINKWhen Delete is pressed do a fade out, instead of a page refresh.

RENDERING EXTREMITIES

ADD AJAXIFIED CREATE ZOMBIE FORMOn the same Zombie index page, create an AJAX form which will create a new zombie and add it to the list.

1 Write format.js in the controller

FROM THE GROUND UP

2 Refactor the view and create the form

3 Create the JavaScript

class ZombiesController < ApplicationController def create @zombie = Zombie.new(params[:zombie])

respond_to do |format| if @zombie.save format.html { ... } format.json { ... } else format.html { ... } format.json { ... } end

app/controllers/zombies_controller.rb

1 Write format.js in the controller

format.js end endend

RENDERING EXTREMITIES

app/views/zombies/index.html.erb

2 Refactor the view and create the form

<%= div_for zombie do %> <%= link_to "Zombie #{zombie.name}", zombie %> <div class="actions"> <%= link_to 'edit', edit_zombie_path(zombie) %> <%= link_to 'delete', zombie, method: :delete, remote: true %> </div><% end %>

<div id="zombies"><% @zombies.each do |zombie| %>

<% end %></div>

Lets refactor into a partial

RENDERING EXTREMITIES

app/views/zombies/_zombie.html.erb

app/views/zombies/index.html.erb

2 Refactor the view and create the form

<%= div_for zombie do %> <%= link_to "Zombie #{zombie.name}", zombie %> <div class="actions"> <%= link_to 'edit', edit_zombie_path(zombie) %> <%= link_to 'delete', zombie, method: :delete, remote: true %> </div><% end %>

<%= render zombie %>

<div id="zombies"><% @zombies.each do |zombie| %>

<% end %></div> LOOKS FOR PARTIAL USING CLASS NAME

RENDERING EXTREMITIES

app/views/zombies/index.html.erb

2 Refactor the view and create the form

<%= render zombie %>

<div id="zombies"><% @zombies.each do |zombie| %>

<% end %></div>

SAME THING<div id="zombies"> <%= render @zombies %></div>

RENDERING EXTREMITIES

app/views/zombies/index.html.erb

2 Refactor the view and create the form

<div id="zombies"> <%= render @zombies %></div>

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

<%= form_for(Zombie.new, remote: true) do |f| %>

RENDERING EXTREMITIES

app/views/zombies/index.html.erb

<div id="zombies"> <%= render @zombies %></div>

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

$('div#zombies').append("<%= escape_javascript(render @zombie) %>");$('div#<%= dom_id(@zombie) %>').effect('highlight');

app/views/create.js.erb

3 Create the JavaScript

BUT WHAT ABOUT WHEN

NAME IS BLANK?

RENDERING EXTREMITIES

<%= form_for(Zombie.new, remote: true) do |f| %>

<% if @zombie.new_record? %>$('input#zombie_name').effect('highlight', { color: 'red' });

<% else %>

<% end %>

3 Create the JavaScript

$('div#zombies').append("<%= escape_javascript(render @zombie) %>");$('div#<%= dom_id(@zombie) %>').effect('highlight');

app/views/zombies/create.js.erb

MUST ADD JQUERY-UI/app/assets/javascripts/application.js

//= require jquery

//= require jquery_ujs

//= require jquery_ui

//= require_tree .

RENDERING EXTREMITIES

ADD AJAXIFIED CREATE ZOMBIE FORM

ADD AJAXIFIED CUSTOM DECOMP FORMCreate a AJAX form that can set a custom decomposition phaseon the zombie show page.

2 Define the form

1 Create new custom route for our action

3 Create action in the controller

4 Write the JavaScript to send back to the client

RENDERING EXTREMITIES

/config/routes.rb

1 Create new custom route for our action

resources :zombies do resources :tweets get :decomp, on: :memberput :custom_decomp, on: :member

end

2 Define the form/views/zombies/show.html.erb

<%= form_for @zombie, url: custom_decomp_zombie_path(@zombie),

<strong>Custom Phase</strong><%= f.text_field :decomp %><%= f.submit "Set" %>

<% end %>

remote: true do |f| %>

<div id="custom_phase">

</div>

<strong>Decomposition Phase:</strong><span id="decomp"%><%= @zombie.decomp %></span>

VALUE WE WANT TO UPDATE

Create a AJAX form that can set a custom decomposition phaseon the zombie show page.

2 Define the form

1 Create new custom route for our action

3 Create action in the controller

4 Write the JavaScript to send back to the client

RENDERING EXTREMITIES

put :custom_decomp, on: :member

<%= form_for @zombie, url: custom_decomp_zombie_path(@weapon),remote: true do |f| %>

ADD AJAXIFIED CUSTOM DECOMP FORM

4 Write the JavaScript to send back to the client

RENDERING EXTREMITIES

/app/views/zombies/advance_decomp.js.erb

$('#decomp').text('<%= @zombie.decomp %>').effect('highlight', {}, 3000);

@zombie = Zombie.find(params[:id])

@zombie.saveend

def custom_decomp

DON’T NEED RESPOND_TO

/app/controllers/zombies_controller.rb

@zombie.decomp = params[:zombie][:decomp]

3 Create action in the controller

<% if @zombie.decomp == "Dead (again)" %> $('div#custom_phase').fadeOut();<% end %>

RENDERING EXTREMITIES

Create a AJAX form that can set a custom decomposition phaseon the zombie show page.

ADD AJAXIFIED CUSTOM DECOMP FORM

DO CUSTOM DECOMPOSITION, USING JSON WITH AN APIDo the same thing, but use JSON with client side JavaScript

2 Modify the custom_decomp action

1 Define the form

3 Write the client side JavaScript using CoffeeScript

RENDERING EXTREMITIES

/views/zombies/show.html.erb

1 Define the form

<%= form_for @zombie, url: custom_decomp_zombie_path(@zombie) do |f| %><strong>Custom Phase via JSON</strong><%= f.text_field :decomp %><%= f.submit "Set" %>

<% end %>

RENDERING EXTREMITIES

<div id="custom_phase2">

</div>

2 Modify the advance_decomp action

RENDERING EXTREMITIES

@zombie = Zombie.find(params[:id])

@zombie.save

end

def custom_decomp

/app/controllers/zombies_controller.rb

@zombie.decomp = params[:zombie][:decomp]

end

respond_to do |format| format.js format.json { render json: @zombie.to_json(only: :decomp) }

3 Write the client side JavaScript using CoffeeScript

/app/assets/javascripts/zombies.js.coffee

$(document).ready ->$('div#custom_phase2 form').submit (event) ->event.preventDefault()

url = $(this).attr('action')custom_decomp = $('div#custom_phase2 #zombie_decomp').val()

$.ajax type: 'put' url: urldata: { zombie: { decomp: custom_decomp } }dataType: 'json'success: (json) -> $('#decomp').text(json.decomp).effect('highlight')

if json.decomp == "Dead (again)"$('div#custom_phase2').fadeOut()

RENDERING EXTREMITIES

RENDERING EXTREMITIES

DO CUSTOM DECOMPOSITION, USING JSON WITH AN APIDo the same thing, but use JSON with client side JavaScript