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 valuta l'espressione e
stampa l'output
valuta l'espressione e stampa l'output senza un newline a fine riga
valuta l'espressione e non stampa l'output
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
visualizza l'output di una view
Permettono di uniformare il layout del sito e se desiderato di personalizzarlo in alcune sue zone
EsempioEsempio
'rss', :action => 'category', :id => 1 %> Papers
'table' %>
'documents', :action => 'category', :id => 1 %>
'rss', :action => 'category', :id => 2 %> Slides
'table' %>
'documents', :action => 'category', :id => 2 %>
'rss', :action => 'category', :id => 3 %> Videos
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: aaron: id: 2 login: aaron email: [email protected].com bio: Aaron is a weird guy created_at:
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!
http://www.lonerunners.net/mailto:[email protected]
Slide 1Slide 2Slide 3Slide 4Slide 5Slide 6Slide 7Slide 8Slide 9Slide 10Slide 11Slide 12Slide 13Slide 14Slide 15Slide 16Slide 17Slide 18Slide 19Slide 20Slide 21Slide 22Slide 23Slide 24Slide 25Slide 26Slide 27Slide 28Slide 29Slide 30Slide 31Slide 32Slide 33Slide 34Slide 35Slide 36Slide 37Slide 38Slide 39Slide 40Slide 41Slide 42Slide 43Slide 44Slide 45Slide 46Slide 47Slide 48Slide 49Slide 50Slide 51Slide 52Slide 53Slide 54Slide 55Slide 56Slide 57Slide 58Slide 59Slide 60Slide 61Slide 62Fine
Recommended