sviluppo web con ruby on rails - tanasi · vede persone e fa cose ... ruby on rails - fu? “ruby...

63
Sviluppo web con Ruby on Rails

Upload: vanhanh

Post on 15-Feb-2019

242 views

Category:

Documents


0 download

TRANSCRIPT

Sviluppo web con Ruby on Rails

Chi vi parlaChi vi parla● Alessandro `jekil` Tanasi

● Vede persone e fa cose

● Non sviluppa siti web ma ha delle web-esigenze da soddisfare (chi non le ha?)

● Senza perdere tempo e nel modo più efficace possibile

Ruby on Rails

Ruby on Rails - fu?Ruby on Rails - fu?

“Ruby on Rails is astounding. Using it is like watching a kung-fu movie,where a dozen bad-ass frameworks prepare to beat up the little newcomer only to be handed their asses in a variety of imaginative ways.”(Nathan Torkington, O'Reilly Program Chair for OSCON)

Ruby on RailsRuby on RailsRuby on Rails è un framework per lo sviluppo

di applicativi web

● Basato su Ruby

● Open source

● Pensato per massimizzare la produttività

● Progettato pensando alla felicità del programmatore

● http://rubyonrails.org/screencasts

Perchè Rails...

La forza di RailsLa forza di Rails● Ruby

● Convention over configuration

● Best practices: MVC, DRY, Testing

● Astrazione (SQL, Javascript, ..)

● Integrazione con AJAX e REST

● Metodologie “agile”

InstallazioneInstallazione● Interprete Ruby

– apt-get install ruby– Compilazione dei sorgenti– IstantRails su Windows– Locomotive su OSX

● Ruby on Rails

● DBMS

– SQLite, MySQL, Postgres e altri● Editor di testo

– TextMate, jEdit, Scite, NetBeans, Aptana, vim, emacs

Let's go!

Struttura directoryStruttura directoryapp controllers helpers models views layoutsconfig environment.rb routes.rbdb database.yml migrationsliblogpublicscripttestvendor plugins rails

Migrations

MigrationsMigrations● Evoluzione del database schema nel tempo

● Definite indipendentemente dal DBMS sottostante

● script/generate migration

● Ogni migrazione è numerata e applicata sequenzialmente

● La migrazione può essere reversibile

● I dati evolvono con la migrazione

● rake db:migrate VERSION=X

EsempioEsempioclass CreateUsers < ActiveRecord::Migration def self.up create_table "users", :force => true do |t| t.string :login, :null => false t.string :email, :null => false t.string :salt, :null => false, :limit => 40 t.string :remember_token t.datetime :remember_token_expires_at t.string :password_reset_code, :limit => 40 t.timestamps end add_index :users, :login add_index :users, :enabled end

def self.down drop_table "users" endend

RakeRakeUtility per lo svolgimento di task

● db:migrate

● db:sessions:create

● doc:app

● doc:rails

● log:clear

● rails:freeze:gems

● rails:freeze:edge

● rails:update

● :test (default task)

● :stats

Scaffolding

ScaffoldScaffold● Creazione automatizzata dell'interfaccia

web di un dato modello

● Utile per strumenti che devono essere usabili da subito e con il minimo sforzo

● Possibilità di personalizzazioni

● Rapidità di sviluppo

● ruby script/generate scaffold antani

Modelli

ActiveRecordActiveRecord● Ogni tabella del database è mappata con

una classe

● I nomi delle tabelle sono al plurale mentre le classi dei modelli sono al singolare

● Ogni tabella ha un campo primary key chiamato id

● La mappatura permette di eseguire operazioni trattando i dati come oggetti

● Permette di definire costrutti e condizioni sui dati

FindersFinders● User.find(:all)

● User.find(:all, :limit => 10)

● Dynamic finders

● User.find_all_by_last_name “Hanson”

● User.find_by_age “20”

● User.find_by_first_name_and_last_name “Andreas”, “Kviby”

AssociazioniAssociazionihas_one :credit_card, :dependent => :destroy

has_many :comments, :include => :authorhas_many :people, :class_name => "Person", :conditions => "deleted = 0", :order => "name"has_many :tracks, :order => "position", :dependent => :destroyhas_many :comments, :dependent => :nullifyhas_many :tags, :as => :taggablehas_many :subscribers, :through => :subscriptions, :source => :userhas_many :subscribers, :class_name => "Person", :finder_sql => 'SELECT DISTINCT people.* ' + 'FROM people p, post_subscriptions ps ' + 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' + 'ORDER BY p.first_name'

ValidazioniValidazioni● Regole di validazione del modello che

proteggono l'integrità dei dati

● La validazione viene eseguita al salvattaggio e modifica di dati

● E' fondamentale che ogni dato sia sempre validato

● Es. nomi utente univoci

● Es. campi che devono essere solo numerici

ValidatoriValidatori● validates_acceptance_of

● validate_associated

● validates_confirmation_of

● validates_each

● validates_exclusion_of

● validates_format_of

● validates_inclusion_of

● validates_length_of

● validates_numericality_of

● validates_presence_of

● validates_size_of

● validates_uniqueness_of

EsempioEsempioclass User < ActiveRecord::Base

validates_presence_of :login, :email validates_presence_of :password, :if => :password_required? validates_presence_of :password_confirmation, :if => :password_required?

validates_length_of :login, :within => 3..40 validates_length_of :email, :within => 3..100 validates_uniqueness_of :login, :email, :case_sensitive => false validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message => "Indirizzo email non valido" validates_format_of :login, :with => /^[\w\d\-\_]+$/, :message => "Sono permessi solo lettere" before_save :encrypt_password has_one :country has_many :photos has_one :profile, :dependent => :nullify

Controller

ActiveControllerActiveController● I controller sono classi Ruby in

app/controllers

● Ad ogni suo metodo pubblico corrisponde una vista

● Gestiscono le azioni che si compiono sui dati

● Gestiscono tutte le interazioni logiche

● Es. un utente compra un oggetto

● Es. un utente si iscrive al sito

Flash messagesFlash messages● Permettono di impostare un messaggio da

mostrare all'utente nella gestione di una richiesta e visualizzarlo nella successiva

● Varie priorità flash[:notice], flash[:error]

● Utilizzati per azioni che richiedono un feedback dopo un submit

● Es. Compilazione di un ordine online

● Es. Login di un utente

EsempioEsempioclass LinksController < ApplicationController def index @user = User.find_by_id(params[:user_id]) @links = @user.links end def latest n = 10 @links = Link.find(:all, :order => 'created_at ASC', :limit => n) endend

Views

ActionViewActionView● Si occupa del rendering della risposta al

client

● Rendering basato su template

● Permesso l'uso di codice all'interno dei template

● Il controller sceglie quale template visualizzare

● Le view hanno accesso alle variabili d'istanza (ad es. @pluto)

Tipi di templateTipi di template● Rxml – genera output XML. Tipicamente

usato per generare feed RSS/Atom

● Rhtml – genera output HTML

● Rjs – genera codice Javascript. Utilizzato per AJAX

HTML templateHTML template● <%= ruby code %> valuta l'espressione e

stampa l'output

● <%= ruby code -%> valuta l'espressione e stampa l'output senza un newline a fine riga

● <% ruby code %> valuta l'espressione e non stampa l'output

● <%= h comment.body %> escaping dell'output

PartialsPartials● Parti di una pagina (ad es. footer)

● Possono essere inclusi in una o più pagine

● Funzionano come i template

● Il loro nome inizia con un underscore

● Vengono utilizzate per le parti di codice condivise che devono essere riciclate nell'applicazione

● Es. search box

● Es. header e footer

LayoutLayout● I template del layout si trovano in

/app/views/layout

● Il posizionamento di tag permette la visualizzazione delle parti di codice generate

● <%= yield %> visualizza l'output di una view

● Permettono di uniformare il layout del sito e se desiderato di personalizzarlo in alcune sue zone

EsempioEsempio<div class="titlesection"> <%= link_to '<img src="/images/rss_rotated.jpg" />', :controller => 'rss', :action => 'category', :id => 1 %> Papers</div><%= render :partial => 'table' %><p class="readmore"><%= link_to 'more papers', :controller => 'documents', :action => 'category', :id => 1 %></p>

<div class="titlesection"> <%= link_to '<img src="/images/rss_rotated.jpg" />', :controller => 'rss', :action => 'category', :id => 2 %> Slides</div>

<%= render :partial => 'table' %><p class="readmore"><%= link_to 'more papers', :controller => 'documents', :action => 'category', :id => 2 %></p>

<div class="titlesection"> <%= link_to '<img src="/images/rss_rotated.jpg" />', :controller => 'rss', :action => 'category', :id => 3 %> Videos</div>

Helpers

HelpersHelpers● Moduli Ruby che definiscono metodi

disponibili nei template

● Evitano duplicazione di codice

● Riducono il codice nei template

● Per default ogni controller ha un helper corrispondente

● Usati per definire le funzionalità comuni

● Es. è loggato un utente?

● Es. pagina successiva

EsempioEsempiomodule ApplicationHelper

# Check if a parameter is nil, if it's print - else print the value. NilPrintCheck def npc(var) if var.nil? or var.empty? return '-' else return var end end

Testing

TestingTesting● Test::Unit è una libreria Ruby per unit

testing

● Rails integra tre tipi di tests:

– Uni tests (test sul modello)– Integration tests (test di integrazione)– Functional tests (test sul controller)

● Ogni test deve iniziare per test_

● Prima dell'esecuzione di ogni test viene chiamato il medoto setup e alla sua conclusione il metodo teardown

● Ogni test contiene una o piu assert

Unit TestingUnit Testing● Ogni modello ha il suo unit test in

test/units/test_my_model.rb generato automaticamente alla creazione del modello

● Utilizzato per verifcare coerenza di

– Modello– Validatori– Funzioni del modello

● E' buona norma testare ogni funzione del modello in particolare quelle custom

EsempioEsempiorequire File.dirname(__FILE__) + '/../test_helper'

class UserTest < Test::Unit::TestCase fixtures :users

def test_password_chars assert_difference 'User.count' do u = create_user(:login => '0aaaaaaaA-_') end assert_no_difference 'User.count' do u = create_user(:login => 'aaaaaaaaaaa+') assert u.errors.on(:login) end end def test_should_create_user assert_difference 'User.count' do user = create_user assert !user.new_record?, "#{user.errors.full_messages.to_sentence}" end endend

Helpers & FixturesHelpers & Fixtures● Ogni unit test ha il suo helper

● E' possibile utilizzare una fixture che contiene i dati di test che vengono caricati nel database

● Dati memorizzati in sintassi YAML

● Permette di avere uno storage di dati d'esempio

EsempioEsempioquentin: id: 1 login: quentin email: [email protected] created_at: <%= 5.days.ago.to_s :db %>aaron: id: 2 login: aaron email: [email protected] bio: Aaron is a weird guy created_at: <%= 1.days.ago.to_s :db %>

AssertionsAssertions● assert(actual, comment) # Asserts truth

● assert_equal(expected, actual, comment)

● assert_in_delta(expected_float, actual_float, delta,

● message)

● assert_match(pattern, string, message)

● assert_nil(object, message)/assert_not_nil

● assert_raise(Exception, ..., message) { block ... }

● assert_difference(expressions, difference = 1, &block)

Functional testsFunctional tests● Verificano un'istanza di controller

● Simula una richiesta HTTP e controlla la risposta

● Fatti per verificare il funzionamento della logica applicativa

EsempioEsempiorequire File.dirname(__FILE__) + '/../test_helper'require 'comments_controller'class CommentsController; def rescue_action(e) raise e end; endclass CommentsControllerTest < Test::Unit::TestCase fixtures :users, :comments def setup @controller = CommentsController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new @request.env['HTTP_HOST'] = "localhost" @request.session[:user] = users(:aaron) end def test_rss get :rss, :id => users(:quentin) assert_response :success assert_select "rss > channel" do assert_select "title", /Recent comments/ assert_select "item", 1 assert_select "item > title", Regexp.new(users(:aaron).login) users(:quentin).comments.first.body end end

AssertionsAssertions● assert_response

:success|:redirect|:missing|:error

● assert_redirected_to(:controller => ‘blog’, :action => ‘list’)

● assert_template ‘store/index’

● assert_not_nil assigns(:items)

● assert session[:user]

● assert_not_nil flash[:notice]

Integration testsIntegration tests● Test di integrazione al Rails dispatcher e

tutti i controller

● Simula scenari d'uso reali

● Inludono le stesse funzionalità dei functional tests

● Simulano l'utilizzo dei componenti nella loro globalità

EsempioEsempioclass TracerBulletTest < ActionController::IntegrationTest def test_tracer_bullet get("/mcm/user/login") assert_response :success post("/mcm/user/login", :email => self.mail, :password => self.password) assert_redirected_to :controller => 'mcm/general' follow_redirect! assert_response :success expect_count = contacts(:adam_sandler).jobs.size post("/mcm/contacts/search", :q => 'sandler new york') assert_response :success assert_n_search_results(expect_count) get "/mcm/lists/show/#{list.id}" assert_response :success assert_template 'mcm/lists/show' endend

Running testsRunning tests● rake - runs all tests

● rake test:units

● rake test:functionals

● rake test:integration

● ruby test/unit/user_test.rb

RCOVRCOV● Rcov e` una libreria Ruby per misurare la

copertura data dalle unit tests

● Utilizzata per trovare parti rimaste scoperte dai test

# Installation of rcov:gem install rcovruby script/plugin install http://svn.codahale.com/rails_rcovrake test:test:rcov

Internals

RoutingRouting● Insime di regole che mappano URL e

parametri in componenti Rails

● Le rotte sono definite in config/routes.rb

● Se un URL non trova una rotts corrispondente si ottiene un 404

● Gli oggetti e i controller possono essere mappati sugli URL per creare ad es. /users/photos/1

MVCMVC

Fonte: Manning - Ruby For Rails Ruby Techniques For Railsvelopers

MVC Request CycleMVC Request Cycle1.Richiesta http://localhost:3000/users/new/1

2.Il server Rails:

1.Invoca il dispatcher

2.Cerca la rotta in routes.rb

3.La rotta di default :controller/:action/:id viene usata se non ne vengono trovate altre

4.Il metodo new è chiamato all'interno del controller users che prendo il dato ad id 1 dal modello

3.Viene generato codice HTML dalla vista new.html.erb

4.Rails invia tutto il codice HTML generato al browser

Deployment

DeploymentDeployment● Utilizzo di normali web server e CGI

(fastCGI)

● Utilizzo di cluster (mongrel) e web server di front end in modalita` proxy (apache)

● Utilizzo di tool per il deploy automatico (Capistrano)

● Il deploy e il mantenimento potrebbe risultare un tallone d'Achille se non vengono svolti utilizzando procedure corrette e lungimiranti

Best praticesBest pratices● Usare SQL solo se strettamente necessario

● Mettere meno codice possibile nel controller e cercare di tenere logica nel modello

● Accedere ai dati utilizzando l'utente corrente se possibile (ad es. current_user.visits.recent)

● Dipendere il meno possibile da librerie e plugin esterni (inluderle nell'applicazione)

● Utilizzare ampiamente le unit test

● Automatizzare ogni task possibile

Libri

Libri utiliLibri utili● Agile Web Development with Rails

● Professional Ruby on Rails – Wrox

● Rails Cookbook – O'Reilly

● Deploying Rails Applications - Pragmatic Bookshelf

● Beginning Ruby on Rails - Wrox

Domande

Slides e contattiSlides e contattiQueste slides sono disponibili su:

http://www.lonerunners.net

Scrivetemi pure!

[email protected]