Edgar J. Suarez
   http://rails.mx
Ruby
1993
Yukihiro Matsumoto
...computer engineers, focus on the machines. They think, "By
    doing this, the machine will run faster. By doing this, the
   machine will run more effectively..." They are focusing on
  machines. But in fact we need to focus on humans, on how
           humans care about doing programming...

          We are the masters. They are the slaves.
"I hope to see Ruby help every programmer
 in the world to be productive, and to enjoy
 programming, and to be happy. That is the
     primary purpose of Ruby language."
Perl + Smalltalk + Eiffel + Lisp
Dynamic Typing
x = 1
x = "hoge"
x = Class
Duck typing
"When I see a bird that walks like a duck
 and swims like a duck and quacks like a
      duck, I call that bird a duck."
Readable
(human friendly)
3.times do |i|
  puts "Now i is #{i}"
end

@user.is_admin?

@transaction.complete!
Flexible syntax
say_hello()
say_hello

multiply(3, 2)
multiply 3, 2
Reflexive
(metaprogramming)
a = "by_name"

eval <<-eos
  def find_#{a}(name)
    puts "finding by name"
  end
eos

find_by_name("edgar") # => "finding by name"
Iterators / blocks
5.times do
  puts "Hello"
end

['ruby', 'rocks'].each do |word|
  puts word
end

1.upto(3) do |i|
  puts i
end
Exception handling
begin
  this_line.could_throw_an_exception!
rescue Exception => e
  puts "Exception rescued: #{e}"
end
Object oriented
person.say_hi

3.to_s

"a string".capitalize

[].empty?

nil.class # => NilClass
Data types
3.class                      #   =>   Fixnum
4.24.class                   #   =>   Float
"Campus".class               #   =>   String
[1, 2, 3, 4].class           #   =>   Array
(10..15).class               #   =>   Range
:name.class                  #   =>   Symbol
{ :name => "Maria" }.class   #   =>   Hash
Symbols
"name".object_id # => 2156968560
"name".object_id # => 2156964240

:name.object_id # => 68828
:name.object_id # => 68828
Hashes
{
    "name" => "John",
    2      => 54,
    true   => "(345) 434-554"
}
{
    :name => "John",
    :age   => 54,
    :phone => "(345) 434-554"
}
Automatic Garbage
    Collection
Boolean coercion
(everything is true except false and nil)
Open classes
class Person
  def walk
    "walking..."
  end
end

juan = Person.new
juan.walk # => "walking..."

class Person
  def eat
    "eating..."
  end
end

juan.eat # => "eating..."
Default arguments
def multiply(a, b = 2)
  a * b
end

multiply 3, 4 # => 12

multiply 4 # => 8
Splat arguments
def menu_list(*args)
  options = args.pop
  args.each do |arg|
    puts "<a href='#' style='#{options[:style]}'>#{arg}</a>"
  end
end

menu_list   :home, :about, :contact, {:style => "color: green;"}
  # => <a   href=’#’ style=’color: green;’>home</a>
  # => <a   href=’#’ style=’color: green;’>about</a>
  # => <a   href=’#’ style=’color: green;’>contact</a>

menu_list :home, :about, {:style => "color: green;"}
  # => <a href=’#’ style=’color: green;’>home</a>
  # => <a href=’#’ style=’color: green;’>about</a>
Attributes
class Person

  def name=(name)
    @name = name
  end

  def name
    @name
  end

end

a = Person.new
a.name = "Pepe"
a.name # => Pepe
class Person

  attr_accessor :name
  attr_reader   :age
  attr_writer   :phone

end
“Class” methods
class Number
  def self.sum(a, b)
    a + b
  end

  def Number.pow(a, b)
    a ** b
  end
end

Number.sum(2, 2) # => 4
Number.pow(4, 2) # => 16
Self
class Number
  def self.base
    10
  end

  def self.parse(str)
    str.to_i(self.base)
  end
end

Number.parse("30") # => 30
class Person
  attr_accessor :first_name, :last_name

  def full_name
    "#{self.first_name} #{self.last_name}"
  end
end

raul = Person.new
raul.first_name = "Raul"
raul.last_name = "Mendez"
raul.full_name # => Raul Mendez
Inheritance
class Shape
  def area
    @a * @b
  end
end

class Square < Shape
end

sq = Square.new
sq.area
class Shape
  def area
    @a * @b
  end
end

class Square < Shape
  def area
    if @a == @b
      @a * @a
    else
      super
    end
  end
end

sq = Square.new
sq.area
Mixins
module Walkable
  def walk
    puts "I'm walking"
  end
end

module Runnable
  def run
    puts "I'm running"
  end
end

class Person
  include Walkable
  include Runnable
end

joe = Person.new
joe.walk # => I'm walking
joe.run # => I'm running
module Summable
  def sum(a, b)
    a + b
  end
end

class Number
  extend Summable
end

Number.sum 2, 4 # => 6
Namespaces
module ActiveRecord
  class Base

  end
end

ActiveRecord::Base.new
Blocks
3.times do
  puts "hey"
end

3.times { puts "hey" }
3.times do
  puts "hey"
end

class Fixnum

  def times
    i = 0
    while i < self
      yield
      i += 1
    end
  end

end
3.times do
  puts "hey"
end

class Fixnum

  def times(&block)
    i = 0
    while i < self
      block.call
      i += 1
    end
  end

end
3.times do           3.times do
  puts "hey"           puts "hey"
end                  end

class Fixnum         class Fixnum

  def times            def times(&block)
    i = 0                i = 0
    while i < self       while i < self
      yield                block.call
      i += 1               i += 1
    end                  end
  end                  end

end                  end
3.times do |i|
  puts i
end

class Fixnum

  def times
    i = 0
    while i < self
      yield(i)
      i += 1
    end
  end

end
Gems
http://rubygems.org/
$ gem install rails
$ gem list
$ gem uninstall bundler
Implementations
• MRI (Matz)
• JRuby (java)
• Rubinius (Ruby)
• IronRuby (.NET)
• MacRuby (Cocoa)
• REE (Enterprise Edition)
Versions
1.8.7
1.9.2
Installation
Windows
Ruby Installer - http://j.mp/rubywindows
Linux / Mac
 RVM - http://j.mp/installrvm
Live CD
Ubuntu - http://j.mp/railsmxlivecd
Ruby on Rails
July 2004
David Heinemeier
    Hansson
     @ 37signals
$ rails new isshoni -d mysql
+-isshoni/
  +-app/
  +-config/
  +-db/
  +-public/
Bundler
$ gem install bundler
Gemfile
source 'http://rubygems.org'

gem 'rails', '3.0.9'
gem 'mysql2', '0.2.7'
$ bundle install
MVC
+-isshoni/
  +-app/
  | +-controllers/
  | +-models/
  | +-views/
  +-config/
  +-db/
  +-public/
Models
ORM
ActiveRecord
Database
+-isshoni/
  +-app/
  +-config/
  | +-database.yml
  +-db/
  +-public/
login: &login
  adapter: mysql2
  encoding: utf8
  username: root
  password:

development:
  <<: *login
  database: isshoni_development

test:
  <<: *login
  database: isshoni_test

production:
 <<: *login
  database: isshoni_production
$ rails g model User name:string email:string
+-isshoni/
  +-app/
  | +-models/
  | | +-user.rb
  +-config/
  +-db/
  | +-migrate/
  | | +-201107211030001_create_users.rb
  +-public/
class User < ActiveRecord::Base
end
class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :name
      t.string :email
      t.timestamps
    end
  end

  def self.down
    drop_table :users
  end
end
$ rails g model Profile user_id:integer about:text
+-isshoni/
  +-app/
  | +-models/
  | | +-user.rb
  | | +-profile.rb
  +-config/
  +-db/
  | +-migrate/
  | | +-201107211030001_create_users.rb
  | | +-201107211035002_create_profiles.rb
  +-public/
class Profile < ActiveRecord::Base
end
class CreateProfiles < ActiveRecord::Migration
  def self.up
    create_table :profiles do |t|
      t.integer :user_id
      t.text :about
      t.timestamps
    end
  end

  def self.down
    drop_table :profiles
  end
end
Migrations
$   rake   db:create
$   rake   db:migrate
$   rake   db:rollback
$   rake   db:drop
Associations
has_one
class User < ActiveRecord::Base
  has_one :profile, :dependent => :destroy
end
@user = User.new
@user.profile = Profile.new
@user.profile.about = “Me”
belongs_to
class Profile < ActiveRecord::Base
  belongs_to :user
end
@profile.user # => #<User id:1>
has_many
class User < ActiveRecord::Base
  has_many :addresses
end
@user.addresses = []
@user.addresses.push(Address.new)
Persistence
@user = User.new
@user.save

@user = User.create
@user = User.new
@user.new_record? # => true

@user.save

@user.new_record? # => false
@user = User.create
@user.new_record? # => false
@user = User.new(:name => “Raj”)
@user.save
@user.name # => Raj

@user = User.create(:name => “Penny”)
@user.name # => Penny
Callbacks
class User < ActiveRecord::Base

  before_create :set_profile

  private

  def set_profile
    self.profile = Profile.new
  end

end
@user = User.new
@user.save

@user.profile # => <#Profile id: 1, about: nil>
Validations
class User < ActiveRecord::Base

  validates_presence_of :email
  validates_uniqueness_of :email

end
@user = User.new
@user.save # => false

@user.valid? # => false

@user.errors # => { :email => [“Can’t be blank”] }

@user.email = “foo@bar.com”
@user.save # => true
Finders / Arel / SQL
@user = User.find(3)
# => SELECT * FROM users WHERE id = 3;

@user = User.find_by_name(“Joe”)
# => SELECT * FROM users WHERE name = ‘Joe’ LIMIT 1;

@user = User.first
# => SELECT * FROM users LIMIT 1;
@users = User.all
# => SELECT * FROM users;

@users = User.where(:name => “John”)
# => SELECT * FROM users WHERE name = ‘John’;

@users = User.where(“name LIKE ?”, “Jo%”)
# => SELECT * FROM users WHERE name LIKE ‘Jo%’;
@users = User.order(“created_at DESC”)
# => SELECT * FROM users ORDER BY created_at DESC;

@users = User.joins(:profile).where(“profiles.about IS NULL”)
# => SELECT * FROM users LEFT OUTER JOIN profiles ON users.id =
profiles.user_id WHERE profiles.about IS NULL;
Scopes
class User < ActiveRecord::Base

  scope :with_name, where(“name IS NOT NULL”)

end
User.with_name
# => SELECT * from users WHERE name
IS NOT NULL;
User.with_name.where(“email = ‘foo@bar.com’”)
# => SELECT * FROM users WHERE name IS NOT NULL AND
email = ‘foo@bar.com’;
Controllers
REST
GET /users

GET /users/2
POST /users

PUT /users/2

DELETE /users/2
Routing
+-isshoni/
  +-app/
  +-config/
  | +-routes.rb
  +-db/
  +-public/
Isshoni::Application.routes.draw do

  get '/users'            =>   'users#index'
  get '/users/:id'        =>   'users#show'
  get '/users/new'        =>   'users#new'
  post '/users'           =>   'users#create'
  get '/users/:id/edit'   =>   'users#edit'
  put '/users/:id'        =>   'users#update'
  delete '/users/:id'     =>   'users#destroy'

end
Isshoni::Application.routes.draw do

  resources :users

end
Isshoni::Application.routes.draw do

  resources :users, :only => [:index, :show]

end
Isshoni::Application.routes.draw do

  resources :users do
    resources :ideas
  end

end

# => /users/:id/ideas
Isshoni::Application.routes.draw do

 resources :users do
   match '/activate' => 'users#activate', :on => :member
 end

end

# => /users/:id/activate
Isshoni::Application.routes.draw do

 resources :users do
   match '/active' => 'users#active', :on => :collection
 end

end

# => /users/active
Actions
$ rails g controller users
+-isshoni/
  +-app/
  | +-controllers/
  | | +-application_controller.rb
  | | +-users_controller.rb
  +-config/
  +-db/
  +-public/
class UsersController < ApplicationController

end
class ApplicationController < ActionController::Base
end
Isshoni::Application.routes.draw do

  get '/users'            =>   'users#index'
  get '/users/:id'        =>   'users#show'
  get '/users/new'        =>   'users#new'
  post '/users'           =>   'users#create'
  get '/users/:id/edit'   =>   'users#edit'
  put '/users/:id'        =>   'users#update'
  delete '/users/:id'     =>   'users#destroy'

end
Isshoni::Application.routes.draw do

  resources :users

end
# /users
users_path

# /users/:id
user_path(@user)

# /users/new
new_user_path

# /users/:id/edit
edit_user_path(@user)
class UsersController < ApplicationController

  def index
  end

  def show
  end

  def new
  end

  def create
  end

  def edit
  end

  def update
  end

  def destroy
  end

end
class UsersController < ApplicationController

  # GET /users
  def index
    @users = User.all
  end

end
class UsersController < ApplicationController

  # GET /users/:id
  def show
    @user = User.find(params[:id])
  end

end
class UsersController < ApplicationController

  # GET /users/new
  def new
    @user = User.new
  end

end
class UsersController < ApplicationController

  # POST /users
  def create
    @user = User.new(params[:user])
    if @user.save
      redirect_to users_path
    else
      render :new
    end
  end

end
class UsersController < ApplicationController

  # GET /users/:id/edit
  def edit
    @user = User.find(params[:id])
  end

end
class UsersController < ApplicationController

  # PUT /users/:id
  def update
    @user = User.find(params[:id])
    if @user.update_attributes(params[:user])
      redirect_to user_path(@user)
    else
      render :edit
    end
  end

end
class UsersController < ApplicationController

  # DELETE /users/:id
  def destroy
    @user = User.find(params[:id])
    @user.destroy
    redirect_to users_path
  end

end
class UsersController < ApplicationController
  # GET /users
  def index
    @users = User.all
  end
                                                  # GET /users/:id/edit
  # GET /users/:id
                                                  def edit
  def show
                                                    @user = User.find(params[:id])
    @user = User.find(params[:id])
                                                  end
  end
                                                  # PUT /users/:id
  # GET /users/new
                                                  def update
  def new
                                                    @user = User.find(params[:id])
    @user = User.new
                                                    if
  end
                                                @user.update_attributes(params[:user])
                                                      redirect_to user_path(@user)
  # POST /users
                                                    else
  def create
                                                      render :edit
    @user = User.new(params[:user])
                                                    end
    if @user.save
                                                  end
      redirect_to users_path
    else
                                                  # DELETE /users/:id
      render :new
                                                  def destroy
    end
                                                    @user = User.find(params[:id])
  end
                                                    @user.destroy
                                                    redirect_to users_path
                                                  end
                                                end
Filters
class UsersController < ApplicationController
  before_filter :get_user, :only => [:show, :edit, :update, :destroy]

  def show
  end

  def edit
  end

  def update
    if @user.update_attributes(params[:user])
      redirect_to user_path(@user)
    else
      render :edit
    end
  end

  def destroy
    @user.destroy
    redirect_to users_path
  end

 private

  def get_user
    @user = User.find(params[:id])
  end
end
#   . #around (code before yield)
#   . . #before
#   . . . execute action
#   . . -
#   . #around (code after yield)
#   #after
Views
ERb
<%= "Hello" %>
<p><%= 3 * 2 %></p>


     <p>6</p>
<% ["Joe", "Mei"].each do |name| %>
  <p>Hello <%= name %></p>
<% end %>
<p>Hello Joe</p>
<p>Hello Mei</p>
+-isshoni/
  +-app/
  | +-views/
  | | +-users/
  | | | +-index.html.erb
  | | | +-new.html.erb
  | | | +-edit.html.erb
  | | | +-show.html.erb
  | | | +-_form.html.erb
  | | +-layouts/
  | | | +-application.html.erb
  +-config/
  +-db/
  +-public/
application.html.erb
<!DOCTYPE html>
<html>
<head>
  <title>Isshoni</title>
  <%= stylesheet_link_tag 'style' %>
  <%= javascript_include_tag :defaults %>
</head>
<body>
  <%= yield %>
</body>
</html>
<%= stylesheet_link_tag ‘style’ %>
# /public/stylesheets/style.css
<%= javascript_include_tag ‘application’ %>
# /public/javascripts/application.js
<%= yield %>
users/index.html.erb
<h1>Users</h1>
<p><%= link_to 'Add user', new_user_path %></p>

<% @users.each do |user| %>
  <div class="user"><%= user.name %></div>
<% end %>
users/show.html.erb
<h1><%= @user.name %></h1>

<p>
  <strong>Email:</strong>
  <%= @user.email %>
</p>

<p>
  <strong>About:</strong>
  <%= @user.profile.about %>
</p>
users/new.html.erb
<h1>Add user</h1>

<%= form_for @user do |f| %>
  <p>
    <%= f.label :name %>
    <%= f.text_field :name %>
  </p>

  <p>
    <%= f.label :email %>
    <%= f.email_field :email %>
  </p>

 <% f.fields_for :profile do |p| %>
   <p>
     <%= p.label :about %>
     <%= p.text_area :about %>
   </p>
 <% end %>

  <p>
    <%= f.submit %>
  </p>
<% end %>
users/edit.html.erb
<h1>Edit user</h1>

<%= form_for @user do |f| %>
  <p>
    <%= f.label :name %>
    <%= f.text_field :name %>
  </p>

  <p>
    <%= f.label :email %>
    <%= f.email_field :email %>
  </p>

 <% f.fields_for :profile do |p| %>
   <p>
     <%= p.label :about %>
     <%= p.text_area :about %>
   </p>
 <% end %>

  <p>
    <%= f.submit %>
  </p>
<% end %>
Partials
<h1>Add user</h1>

<%= form_for @user do |f| %>
  <%= render :partial => 'form', :object => f %>
<% end %>
<h1>Edit user</h1>

<%= form_for @user do |f| %>
  <%= render :partial => 'form', :object => f %>
<% end %>
users/_form.html.erb
<p>
  <%= form.label :name %>
  <%= form.text_field :name %>
</p>

<p>
  <%= form.label :email %>
  <%= form.email_field :email %>
</p>

<% form.fields_for :profile do |p| %>
  <p>
    <%= p.label :about %>
    <%= p.text_area :about %>
  </p>
<% end %>

<p>
  <%= form.submit %>
</p>
Nested attributes
<% form.fields_for :profile do |p| %>
  <p>
    <%= p.label :about %>
    <%= p.text_area :about %>
  </p>
<% end %>
class User < ActiveRecord::Base
  has_one :profile, :dependent => :destroy

  accepts_nested_attributes_for :profile
end
Helpers
<%= link_to 'Add user', new_user_path %>
<a href=”/users/new”>Add user</a>

<%= form_for @user do |f| %>
<% end %>
<form action=”/users” method=”post”>
</form>

<%= text_field_tag :name %>
<input type=”text” name=”name” id=”name” />

<%= javascript_include_tag 'application' %>
<script type=”text/javascript” src=”/javascripts/application.js”></script>


<%= image_tag 'background.png' %>
<img src=”/images/background.png” alt=”background” />
Mailers
class UserMailer < ActionMailer::Base
  default :from => "no-reply@foobar.com"

  def password_reset_instructions(user)
    @user = user
    mail :to => @user.email, :subject => "Password Reset"
  end

end
UserMailer.password_reset_instructions(@user).deliver
Security
Mass-assignment
class UsersController < ApplicationController

  def create
    @user = User.new(params[:user])
    if @user.save
      redirect_to users_path
    else
      render :new
    end
  end

end
<%= form_for @user do |f| %>
  <p>
    <%= f.label :username %>
    <%= f.text_field :username %>
  </p>
  <p>
    <%= f.submit %>
  </p>
<% end %>
<%= form_for @user do |f| %>
  <p>
    <%= f.label :username %>
    <%= f.text_field :username %>
  </p>
  <input type="hidden" name="user[is_admin]" value="1" />
  <p>
    <%= f.submit %>
  </p>
<% end %>
# params[:user] => { :username => "joe", :is_admin => true }
@user = User.new(params[:user])
@user.save

@user.is_admin? # => true
class User < ActiveRecord::Base
  attr_protected :is_admin
end
class User < ActiveRecord::Base
  attr_accessible :username
end
SQL injection
@users = User.where("username LIKE '%#{params[:username]}%'")
# params[:username] = "nick' OR 1 = 1 OR username LIKE '%"
@users = User.where("username LIKE '%#{params[:username]}%'")

# SELECT * FROM users WHERE username LIKE '%nick' OR 1 = 1 OR username LIKE '%';
# params[:username] = "nick' OR 1 = 1 OR username LIKE '%"

@users = User.where("username LIKE ?", "%#{params[:username]}%")

# SELECT * FROM users WHERE username LIKE 'nick' OR 1 = 1 OR username LIKE '%';
XSS
mysite.com                  badsite.com




     POST mysite.com/delete_account
class ApplicationController < ActionController::Base
  protect_from_forgery
end
<head>
  <%= csrf_meta_tag %>
</head>


<head>
  <meta name="csrf-param" content="authenticity_token"/>
  <meta name="csrf-token" content="W8oH32123LgvCYNlVjQXsPAcLigB8CtX8eXYBGyp3yE="/>
</head>
Log Filtering
+-isshoni/
  +-app/
  +-config/
  +-db/
  +-public/
  +-log/
  | +-production.log
Started POST "/sessions" for 127.0.0.1 at Sat Jun 25 19:43:44 -0500 2011
  Processing by SessionsController#create as HTML
  Parameters: {"password"=>"123456", "email"=>"foo@bar.com"}
  SQL (0.5ms) SHOW TABLES
  Account Load (0.2ms) SELECT `accounts`.* FROM `accounts` WHERE
`accounts`.`email` = 'foo@bar.com' LIMIT 1
Completed 200 OK in 274ms (Views: 66.3ms | ActiveRecord: 0.7ms)
+-isshoni/
  +-app/
  +-config/
  | +-application.rb
  +-db/
  +-public/
  +-log/
module Deli
  class Application < Rails::Application

   config.filter_parameters += [:password]

  end
end
Started POST "/sessions" for 127.0.0.1 at Sat Jun 25 19:43:44 -0500 2011
  Processing by SessionsController#create as HTML
  Parameters: {"password"=>"[FILTERED]", "email"=>"foo@bar.com"}
  SQL (0.5ms) SHOW TABLES
  Account Load (0.2ms) SELECT `accounts`.* FROM `accounts` WHERE
`accounts`.`email` = 'foo@bar.com' LIMIT 1
Completed 200 OK in 274ms (Views: 66.3ms | ActiveRecord: 0.7ms)
I18n / L10n
+-isshoni/
  +-app/
  +-config/
  | +-application.rb
  +-db/
  +-public/
  +-log/
module Deli
  class Application < Rails::Application

   config.i18n.default_locale = :es

  end
end
<h1><%= I18n.t('home.title') %></h1>

<h2><%= t('home.subtitle') %></h2>
+-isshoni/
  +-app/
  +-config/
  | +-locales/
  |   | +-es.yml
  +-db/
  +-public/
  +-log/
es:
  home:
    title: "Ruby en la CP"
    subtitle: "Taller de RoR en CP"
<h1>Ruby en la CP</h1>

<h2>Taller de RoR en CP</h2>
+-isshoni/
  +-app/
  +-config/
  | +-locales/
  |   | +-es.yml
  |   | +-en.yml
  +-db/
  +-public/
  +-log/
en:
  home:
    title: "Ruby at CP"
    subtitle: "RoR workshopt at CP"
class ApplicationController < ActionController::Base
  before_filter :set_language

 private

  def set_language
    I18n.locale = params[:lang].to_sym if params[:lang]
  end
end
http://mysite.com/users?lang=en
<h1>Ruby at CP</h1>

<h2>RoR workshop at CP</h2>
Console
$ rails console
Loading development environment (Rails 3.0.9)
>>
Loading development environment (Rails 3.0.7)
>> User.all
=> []
Loading development environment (Rails 3.0.7)
>> User.all
=> []
>> User.create(:username => 'Testing')
=> #<User id: 2, username: "Testing", email: nil>
Best practices
DRY
(Don’t Repeat Yourself)
KISS
(Keep It Simple Stupid)
Convention over
 configuration
Fat models, thin
  controllers
TDD / BDD
Thanks!
Edgar J. Suarez
  @edgarjs

Desarrollando aplicaciones web en minutos

Editor's Notes