ruby on rails supinfo 2011. a propos martin catty, fondateur de synbioz rubyiste depuis 2006 _fuse...
Post on 26-Mar-2015
215 Views
Preview:
TRANSCRIPT
Ruby on RailsSupinfo 2011
A propos
•Martin Catty, fondateur de Synbioz
•Rubyiste depuis 2006
_fuse mcatty@synbioz.com
Ruby avant Rails
•Ne partez pas sans vos bagages !
•Rails a aidé à l’essor de Ruby
•Mais pas de framework sans langage
RubyOn ne va pas (ou peu) parler de:
I/O
Réseau
ThreadTest
Debugger
Proc / lambda Exceptions
Garbage
VM
Implémentations
L’Histoire
•Créé par Yukihiro Matsumoto (Matz)
•Release publique en 2005
Un langage
•Plus objet que python
•Plus puissant que perl
•Fun
Les versions
•Stable actuelle: 1.9.2
•1.8.7 dans la branche 1.8
•1.8.7 (p250) fonctionne avec Rails 2 et 3
•< 1.8.7 = Rails 2
•> 1.8.7 = Rails 3
Le fameux hello world
classclass Hello { Hello {public static void main(String [] public static void main(String [] args) {args) {
system.out.println(‘Hello’);system.out.println(‘Hello’);}}
}}
classclass Hello { Hello {public static void main(String [] public static void main(String [] args) {args) {
system.out.println(‘Hello’);system.out.println(‘Hello’);}}
}}
Java Ruby
putsputs ‘Hello’ ‘Hello’putsputs ‘Hello’ ‘Hello’
</troll>
Tout est objet2.2.timestimes { puts "Bonjour { puts "Bonjour Supinfo." }Supinfo." }2.2.timestimes { puts "Bonjour { puts "Bonjour Supinfo." }Supinfo." }
=> Bonjour Supinfo
=> Bonjour Supinfo
3.3.uptoupto(5) { |i| puts i }(5) { |i| puts i }3.3.uptoupto(5) { |i| puts i }(5) { |i| puts i }
=> 3
=> 4
=> 5
p 1.p 1.zero?zero?p 1.p 1.zero?zero?
=> false
39 + 339 + 339 + 339 + 3
=> 39.+(3)
Les variablesclass Aclass A
MAXMAX = 42 = 42@@instances@@instances = 0 = 0def initialize(name)def initialize(name)
@name@name = = namename@@instances@@instances += 1 += 1
endend
def self.instancesdef self.instances@@instances@@instances
endendendend
class Aclass AMAXMAX = 42 = 42@@instances@@instances = 0 = 0def initialize(name)def initialize(name)
@name@name = = namename@@instances@@instances += 1 += 1
endend
def self.instancesdef self.instances@@instances@@instances
endendendend
A.new("a")A.new("b")A.new("c")A.new("d")A.new("e")
p A.instances #=> 5
Itérationsa = 1..9a = 1..9forfor i in a i in a
putsputs i iendend
a = 1..9a = 1..9forfor i in a i in a
putsputs i iendend
a = 1..9a = 1..9a.a.eacheach { |i| { |i| putsputs i } i }a = 1..9a = 1..9a.a.eacheach { |i| { |i| putsputs i } i }
=> 1…9
ou
i = 0i = 0looploop do i += 1 puts i do i += 1 puts i breakbreak if 10 == iend if 10 == iendi = 0i = 0looploop do i += 1 puts i do i += 1 puts i breakbreak if 10 == iend if 10 == iend
1.1.uptoupto(10) do |i| (10) do |i| nextnext if i.odd? if i.odd? # pas d'impair en ruby puts # pas d'impair en ruby puts iendiend
1.1.uptoupto(10) do |i| (10) do |i| nextnext if i.odd? if i.odd? # pas d'impair en ruby puts # pas d'impair en ruby puts iendiend
=> 2, 4, 6, 8, 10
=> 1…9
=> 1…9
1.upto(2) do |i| v = rand(2) 1.upto(2) do |i| v = rand(2) retryretry if v.zero?end if v.zero?end1.upto(2) do |i| v = rand(2) 1.upto(2) do |i| v = rand(2) retryretry if v.zero?end if v.zero?end
Conditionsifif index == 1 index == 1elseelseendend
ifif index == 1 index == 1elseelseendend
puts ‘0’ puts ‘0’ ifif index.zero?index.zero?puts ‘0’ puts ‘0’ ifif index.zero?index.zero?
puts ‘not 0’ puts ‘not 0’ unlessunless index.zero?index.zero?puts ‘not 0’ puts ‘not 0’ unlessunless index.zero?index.zero?
def what_is_it?(a) def what_is_it?(a) casecase a a whenwhen 1..2 1..2 puts "1 or 2" puts "1 or 2" whenwhen 3 puts "3" 3 puts "3" whenwhen /4.*/ # Regexp puts "something /4.*/ # Regexp puts "something starting with 4." starting with 4." whenwhen "foo" puts "foo" puts "foo" "foo" elseelse puts "I don't know." puts "I don't know." endendendend
def what_is_it?(a) def what_is_it?(a) casecase a a whenwhen 1..2 1..2 puts "1 or 2" puts "1 or 2" whenwhen 3 puts "3" 3 puts "3" whenwhen /4.*/ # Regexp puts "something /4.*/ # Regexp puts "something starting with 4." starting with 4." whenwhen "foo" puts "foo" puts "foo" "foo" elseelse puts "I don't know." puts "I don't know." endendendend
what_is_it?(1)# 1 or 2what_is_it?(2)# 1 or 2what_is_it?(3)# 3what_is_it?("4004")# something starting with 4.what_is_it?("foo")# foowhat_is_it?(5)# Don't know.
Tableaux 1/2lost = [8, 15, 16, lost = [8, 15, 16, 23]23]lost lost <<<< 42 # 42 # pushpushlost.lost.unshiftunshift(4)(4)# [4, 8, 15, 16, # [4, 8, 15, 16, 23, 42]23, 42]
lost = [8, 15, 16, lost = [8, 15, 16, 23]23]lost lost <<<< 42 # 42 # pushpushlost.lost.unshiftunshift(4)(4)# [4, 8, 15, 16, # [4, 8, 15, 16, 23, 42]23, 42]
lost lost <<<< nil nil <<<< nilnil
# [4, 8, 15, 16, # [4, 8, 15, 16, 23, 42, nil, nil]23, 42, nil, nil]
lost lost <<<< nil nil <<<< nilnil
# [4, 8, 15, 16, # [4, 8, 15, 16, 23, 42, nil, nil]23, 42, nil, nil]
lost.lost.compact!compact!
# [4, 8, 15, 16, # [4, 8, 15, 16, 23, 42]23, 42]
lost.lost.compact!compact!
# [4, 8, 15, 16, # [4, 8, 15, 16, 23, 42]23, 42]
lost lost <<<< [4, 8] [4, 8]
# [4, 8, 15, 16, # [4, 8, 15, 16, 23, 42, [4, 8]]23, 42, [4, 8]]
lost lost <<<< [4, 8] [4, 8]
# [4, 8, 15, 16, # [4, 8, 15, 16, 23, 42, [4, 8]]23, 42, [4, 8]]
lost.lost.flatten!flatten!..uniq!uniq!
# [4, 8, 15, 16, # [4, 8, 15, 16, 23, 42]23, 42]
lost.lost.flatten!flatten!..uniq!uniq!
# [4, 8, 15, 16, # [4, 8, 15, 16, 23, 42]23, 42]
lost.lost.indexindex(23)(23)
# 4# 4
lost.lost.indexindex(23)(23)
# 4# 4
lost.lost.shuffleshuffle# [16, 23, 42, 4, # [16, 23, 42, 4, 15, 8]15, 8][5, 3, 7, 39, 1, [5, 3, 7, 39, 1, 15].15].sortsort# [1, 3, 5, 7, 15, # [1, 3, 5, 7, 15, 39]39]
lost.lost.shuffleshuffle# [16, 23, 42, 4, # [16, 23, 42, 4, 15, 8]15, 8][5, 3, 7, 39, 1, [5, 3, 7, 39, 1, 15].15].sortsort# [1, 3, 5, 7, 15, # [1, 3, 5, 7, 15, 39]39]
('a'..'z').('a'..'z').to_ato_a["a", "b", "c", ["a", "b", "c", "d"…]"d"…]
('a'..'z').('a'..'z').to_ato_a["a", "b", "c", ["a", "b", "c", "d"…]"d"…]
lost.lost.atat(0)(0)# 4# 4lost[-1]lost[-1]# 42# 42
lost.lost.atat(0)(0)# 4# 4lost[-1]lost[-1]# 42# 42
1111
Tableaux 2/2double_lost = lost.double_lost = lost.mapmap { |v| v * { |v| v * 2 }2 }# => [8, 16, 30, 32, 46, 84]# => [8, 16, 30, 32, 46, 84]
double_lost = lost.double_lost = lost.mapmap { |v| v * { |v| v * 2 }2 }# => [8, 16, 30, 32, 46, 84]# => [8, 16, 30, 32, 46, 84]
# lost: [4, 8, 15, 16, 23, 42]# lost: [4, 8, 15, 16, 23, 42]double_lost double_lost -- lost lost# => [30, 32, 46, 84]# => [30, 32, 46, 84]
# lost: [4, 8, 15, 16, 23, 42]# lost: [4, 8, 15, 16, 23, 42]double_lost double_lost -- lost lost# => [30, 32, 46, 84]# => [30, 32, 46, 84]
# intersection# intersectiondouble_lost double_lost && lost lost# [8, 16]# [8, 16]
# intersection# intersectiondouble_lost double_lost && lost lost# [8, 16]# [8, 16]
# jointure# jointure(double_lost (double_lost || lost).sort lost).sort# [4, 8, 15, 16, 23, 30, 32, 42, # [4, 8, 15, 16, 23, 30, 32, 42, 46, 84]46, 84]
# jointure# jointure(double_lost (double_lost || lost).sort lost).sort# [4, 8, 15, 16, 23, 30, 32, 42, # [4, 8, 15, 16, 23, 30, 32, 42, 46, 84]46, 84]
String
str = str = "a"str."a"str.succsucc# => # => "b""b"
str = str = "a"str."a"str.succsucc# => # => "b""b"
# # InterpolationInterpolationputsputs "foo "foo #{#{strstr}}""# # => foo bar=> foo bar
# # InterpolationInterpolationputsputs "foo "foo #{#{strstr}}""# # => foo bar=> foo bar
# attention, en 1.8# attention, en 1.8str = "éhé"str = "éhé"str.sizestr.size# => 5 et non # => 5 et non 33str[0]str[0]# 195 et non é # 195 et non é (code ascii)(code ascii)
# patché dans rails# patché dans railsstr.str.mb_charsmb_chars[0][0]# é # é str.mb_chars.sizestr.mb_chars.size# 3# 3
# attention, en 1.8# attention, en 1.8str = "éhé"str = "éhé"str.sizestr.size# => 5 et non # => 5 et non 33str[0]str[0]# 195 et non é # 195 et non é (code ascii)(code ascii)
# patché dans rails# patché dans railsstr.str.mb_charsmb_chars[0][0]# é # é str.mb_chars.sizestr.mb_chars.size# 3# 3
str = "bar"str = "bar"putsputs 'foo 'foo #{str}'#{str}'# => foo # => foo #{str}#{str}
str = "bar"str = "bar"putsputs 'foo 'foo #{str}'#{str}'# => foo # => foo #{str}#{str}
str = "foo\str = "foo\n"str.n"str.chompchomp# # => "foo"=> "foo"
str = "foo\str = "foo\n"str.n"str.chompchomp# # => "foo"=> "foo"
str = str = "foo"str."foo"str.chopchop# # => "fo"=> "fo"
str = str = "foo"str."foo"str.chopchop# # => "fo"=> "fo"
"supinfo"."supinfo".capitalicapitalizeze# => "Supinfo"# => "Supinfo""supinfo"."supinfo".upcaseupcase# => "SUPINFO"# => "SUPINFO"
"supinfo"."supinfo".capitalicapitalizeze# => "Supinfo"# => "Supinfo""supinfo"."supinfo".upcaseupcase# => "SUPINFO"# => "SUPINFO"
Hash
h = { :a => 'a', :b => 42, :c => { h = { :a => 'a', :b => 42, :c => { :d => 'f' } }:d => 'f' } }h = { :a => 'a', :b => 42, :c => { h = { :a => 'a', :b => 42, :c => { :d => 'f' } }:d => 'f' } }
h2 = { :a => 'foo' }h2 = { :a => 'foo' }h.h.mergemerge(h2)(h2)=> => {:a=>"foo", :b=>42, :c=>{:d=>"{:a=>"foo", :b=>42, :c=>{:d=>"f"}}f"}}
h2 = { :a => 'foo' }h2 = { :a => 'foo' }h.h.mergemerge(h2)(h2)=> => {:a=>"foo", :b=>42, :c=>{:d=>"{:a=>"foo", :b=>42, :c=>{:d=>"f"}}f"}}
Association clé / valeur
Class: les constructeurs
classclass A def A.new A def A.new endendendendA.newA.newclassclass A def A.new A def A.new endendendendA.newA.new
classclass B def self.new B def self.new endendendend
B.newB.new
classclass B def self.new B def self.new endendendend
B.newB.new
classclass C def C def initializeinitialize endendendendC.newC.newclassclass C def C def initializeinitialize endendendendC.newC.new
class D # pas de class D # pas de constructeur multiple def constructeur multiple def initializeinitialize; end def ; end def initializeinitialize(*args) endend(*args) endendD.new # => KOD.new(1) D.new # => KOD.new(1) # => OK# => OK
class D # pas de class D # pas de constructeur multiple def constructeur multiple def initializeinitialize; end def ; end def initializeinitialize(*args) endend(*args) endendD.new # => KOD.new(1) D.new # => KOD.new(1) # => OK# => OK
Class: les accesseurs
classclass Product Productdef def initializeinitialize(name, (name, description, price)description, price)
@name = name@name = name@description = description@description = description@price = price@price = price
endend
def namedef name@name@name
endend
def name=(name)def name=(name)@name = name@name = name
endend……
endend
classclass Product Productdef def initializeinitialize(name, (name, description, price)description, price)
@name = name@name = name@description = description@description = description@price = price@price = price
endend
def namedef name@name@name
endend
def name=(name)def name=(name)@name = name@name = name
endend……
endend
classclass Product Productattr_accessorattr_accessor :name :nameattr_readerattr_reader: description: descriptionattr_writerattr_writer :price :price
def def initializeinitialize(name, (name, description, price)description, price)
@name = name@name = name@description = description@description = description@price = price@price = price
endendendend
classclass Product Productattr_accessorattr_accessor :name :nameattr_readerattr_reader: description: descriptionattr_writerattr_writer :price :price
def def initializeinitialize(name, (name, description, price)description, price)
@name = name@name = name@description = description@description = description@price = price@price = price
endendendend
Class: portée & héritage
classclass Animal def Animal def initializeinitialize puts puts "Born to be alive." end "Born to be alive." end protectedprotected def breathe? puts def breathe? puts "inhale, exhale" "inhale, exhale" truetrue end end privateprivate def speak; end def speak; endendend
# Animal.new.speak # Animal.new.speak # => fail with private method # => fail with private method `speak'`speak'
classclass Animal def Animal def initializeinitialize puts puts "Born to be alive." end "Born to be alive." end protectedprotected def breathe? puts def breathe? puts "inhale, exhale" "inhale, exhale" truetrue end end privateprivate def speak; end def speak; endendend
# Animal.new.speak # Animal.new.speak # => fail with private method # => fail with private method `speak'`speak'
classclass Dog < Animal def Dog < Animal def alive?alive? puts "I'm alive" if breathe? end puts "I'm alive" if breathe? end def def speakspeak puts "woff." endend puts "woff." endend
snoopy = Dog.newsnoopy = Dog.new# Born to be alive.# Born to be alive.snoopy.speaksnoopy.speak# woff.# woff.snoopy.alive?snoopy.alive?# inhale, exhale# I'm alive# inhale, exhale# I'm alive
classclass Dog < Animal def Dog < Animal def alive?alive? puts "I'm alive" if breathe? end puts "I'm alive" if breathe? end def def speakspeak puts "woff." endend puts "woff." endend
snoopy = Dog.newsnoopy = Dog.new# Born to be alive.# Born to be alive.snoopy.speaksnoopy.speak# woff.# woff.snoopy.alive?snoopy.alive?# inhale, exhale# I'm alive# inhale, exhale# I'm alive
Class: étendre
# Etendre un objetstr = "foo"class << str def blank? self !~ /\S/ endendstr.blank?# => false# Etendre un objetstr = "foo"class << str def blank? self !~ /\S/ endendstr.blank?# => false
# Etendre une classe# Etendre une classeclassclass String String defdef blank? self ! blank? self !~ /\S/ ~ /\S/ endendendend" ".blank?" ".blank?# # => true=> true"foo".blank?"foo".blank?# => false# => false
# Etendre une classe# Etendre une classeclassclass String String defdef blank? self ! blank? self !~ /\S/ ~ /\S/ endendendend" ".blank?" ".blank?# # => true=> true"foo".blank?"foo".blank?# => false# => false
Class: ce qui n’existe pas
•Pas d’héritage multiple
•Basé sur les mixins
•Pas d’interface
•Pas de classe abstraite native
Les outils
[17:59:57] fuse@h [~]$ irb[17:59:57] fuse@h [~]$ irbruby-1.8.7-ruby-1.8.7-p302 :001 >p302 :001 > puts ‘foo’ puts ‘foo’foo => nilfoo => nilruby-1.8.7-p302 :002 >ruby-1.8.7-p302 :002 > 21 * 2 => 42 21 * 2 => 42ruby-1.8.7-p302 :008 >ruby-1.8.7-p302 :008 > String.methods.sort => ["<", "<=", String.methods.sort => ["<", "<=", "<=>", "==", "===", "=~", ">", …]"<=>", "==", "===", "=~", ">", …]
[17:59:57] fuse@h [~]$ irb[17:59:57] fuse@h [~]$ irbruby-1.8.7-ruby-1.8.7-p302 :001 >p302 :001 > puts ‘foo’ puts ‘foo’foo => nilfoo => nilruby-1.8.7-p302 :002 >ruby-1.8.7-p302 :002 > 21 * 2 => 42 21 * 2 => 42ruby-1.8.7-p302 :008 >ruby-1.8.7-p302 :008 > String.methods.sort => ["<", "<=", String.methods.sort => ["<", "<=", "<=>", "==", "===", "=~", ">", …]"<=>", "==", "===", "=~", ">", …]
irb : votre shell ruby
rvm : gérer plusieurs versions de ruby
Documentation
•http://www.ruby-doc.org/
•http://www.ruby-lang.org/
•http://apidock.com/ruby
•Le pickAxe: LA référence
•http://tryruby.org/
•http://groups.google.com/group/rubyfr-public
RailsOn ne va pas (ou peu) parler de:
Test
CacheRake
ActionMailer
ActiveSupport
I18n
Déploiement
Copieurs
Rails 3
L’Histoire
•Créé par DHH (David Heinemeier Hansson)
•Version 1 en décembre 2005
•Pragmatique dès le début: basecamp
Rails en entreprise
•Plus un jouet
•Présent en entreprise, grandes et petites
•Opportunités en temps que développeur
Les versions
•Stable actuelle: 3.0.7
•Rails 3 issue du merge avec merb (08/10)
•Framework agnostic
•Stable actuelle: 2.3.11
•La plus rencontrée en entreprise
Des conventions
•Convention over configuration
•DRY
•MVC
Quelques noms barbares:
Convention over configuration
•Votre passion est de customiser Tomcat ?Désolé !
•0 conf pour commencer à développer
•Serveur web embarqué
•BDD embarquée
Don’t repeat yourself
•- de code = - de bug
•- de code = - de maintenance
•- de code = + de temps
Modèle - Vue - Contrôleur
ContrôlContrôleureur
ContrôlContrôleureurModèleModèleModèleModèle VueVueVueVueContrôlContrôleureur
ContrôlContrôleureur
Métier Fonctionnel Affichage
Rails en vrai
•Pas de hello world, c’est trop simple…
•La classique liste de produits plutôt.
Créons notre application
$ rails app$ rails app$ rails app$ rails app
3 environnements
class class ProductProduct < ActiveRecord::Base < ActiveRecord::Baseendendclass class ProductProduct < ActiveRecord::Base < ActiveRecord::Baseendend
Modèleapp/models/product.rb
class class ProductsControllerProductsController < < ApplicationControllerApplicationController
def indexdef index@products@products = = ProductProduct.all.all
endendendend
class class ProductsControllerProductsController < < ApplicationControllerApplicationController
def indexdef index@products@products = = ProductProduct.all.all
endendendend
Contrôleurapp/controllers/products_controller.rb
<html><body><html><body><% for product in <% for product in @products@products %> %><p><%= product.name %></p><p><%= product.name %></p><% end %><% end %></body></html></body></html>
<html><body><html><body><% for product in <% for product in @products@products %> %><p><%= product.name %></p><p><%= product.name %></p><% end %><% end %></body></html></body></html>
Vueapp/views/products/index.html.erb
Que vient on de faire ?
•Créer une classe product qui hérite de AR::Base
•Créer un contrôleur qui gère les actions (ex: index)
•Créer des vues pour afficher la liste et les formulaires
Model
Contrôleur
Vue
Le modèle avec Active Record
•S’interface avec la base de données
•SQLite par défaut
•Plus de SQL manuel
•Plus de risque d’injections
Créer un modèle$ ruby $ ruby script/generatescript/generate model Product name: model Product name:stringstring description: description:texttext price:price:floatfloat category_id:category_id:integerinteger$ ruby $ ruby script/generatescript/generate model Product name: model Product name:stringstring description: description:texttext price:price:floatfloat category_id:category_id:integerinteger
classclass CreateProducts < CreateProducts < ActiveRecord::Migration def self.up ActiveRecord::Migration def self.up create_tablecreate_table :products do |t| :products do |t| t.string :name t.text :description t.string :name t.text :description t.float :price t.integer :category_id t.float :price t.integer :category_id t.timestamps end end def self.down t.timestamps end end def self.down drop_tabledrop_table :products endend :products endend
classclass CreateProducts < CreateProducts < ActiveRecord::Migration def self.up ActiveRecord::Migration def self.up create_tablecreate_table :products do |t| :products do |t| t.string :name t.text :description t.string :name t.text :description t.float :price t.integer :category_id t.float :price t.integer :category_id t.timestamps end end def self.down t.timestamps end end def self.down drop_tabledrop_table :products endend :products endend
db/migrate/20110322113407_create_products.rb
rake db:migrate
Configuration & log
@products@products = = ProductProduct.all.all@products@products = = ProductProduct.all.all
Dans le contrôleurlog/development.log
Product Load (0.3ms) Product Load (0.3ms) SELECT * FROM SELECT * FROM "products""products"
Product Load (0.3ms) Product Load (0.3ms) SELECT * FROM SELECT * FROM "products""products"
config/database.yml
development: adapter: development: adapter: sqlite3 database: sqlite3 database: db/development.sqlite3 db/development.sqlite3 pool: 5 timeout: 5000pool: 5 timeout: 5000
development: adapter: development: adapter: sqlite3 database: sqlite3 database: db/development.sqlite3 db/development.sqlite3 pool: 5 timeout: 5000pool: 5 timeout: 5000
Adaptateurs pour MySQL, Postgresql, oracle…
Créer un produit
c = Category.firstc = Category.firstProduct.Product.createcreate({ ({
:name => "Ruby 'on Rails", # injection SQL:name => "Ruby 'on Rails", # injection SQL:description => "First book on RoR", :description => "First book on RoR", :price => 10.0, :price => 10.0, :category => c }):category => c })
c = Category.firstc = Category.firstProduct.Product.createcreate({ ({
:name => "Ruby 'on Rails", # injection SQL:name => "Ruby 'on Rails", # injection SQL:description => "First book on RoR", :description => "First book on RoR", :price => 10.0, :price => 10.0, :category => c }):category => c })
Product Create (0.8ms)Product Create (0.8ms) INSERT INTO "products" INSERT INTO "products" ("name", "price", "created_at", "updated_at", "category_id", ("name", "price", "created_at", "updated_at", "category_id", "description") VALUES"description") VALUES('Ruby ''on Rails', 10.0, '2011-03-22 18:13:07', '2011-03-22 ('Ruby ''on Rails', 10.0, '2011-03-22 18:13:07', '2011-03-22 18:13:07', 1, 'First book on RoR')18:13:07', 1, 'First book on RoR')
Product Create (0.8ms)Product Create (0.8ms) INSERT INTO "products" INSERT INTO "products" ("name", "price", "created_at", "updated_at", "category_id", ("name", "price", "created_at", "updated_at", "category_id", "description") VALUES"description") VALUES('Ruby ''on Rails', 10.0, '2011-03-22 18:13:07', '2011-03-22 ('Ruby ''on Rails', 10.0, '2011-03-22 18:13:07', '2011-03-22 18:13:07', 1, 'First book on RoR')18:13:07', 1, 'First book on RoR')
log/development.log
Encore trop complexe ?
•Create
•Read
•Update
•Delete
Besoin d’un outil qui gère le CRUD en 1 commande:
Scaffold
Plus qu’une migration
•Et vous pouvez:
•Créer des produits (Create)
•Les lister et afficher (Read)
•Les mettre à jour (Update)
•Et les supprimer (Delete)
En images
Les relationsclass class CategoryCategory < <
ActiveRecord::BaseActiveRecord::Basehas_manyhas_many :products :products
endend
class class CategoryCategory < < ActiveRecord::BaseActiveRecord::Base
has_manyhas_many :products :productsendend
Une catégorie possède n produits
class class ProductProduct < < ActiveRecord::BaseActiveRecord::Base
belongs_tobelongs_to :category :categoryendend
class class ProductProduct < < ActiveRecord::BaseActiveRecord::Base
belongs_tobelongs_to :category :categoryendend
Un produit appartient à une catégorie
class class ProductProduct < < ActiveRecord::BaseActiveRecord::Base
has_and_belongs_to_manyhas_and_belongs_to_many :ca :categorytegory
endend
class class ProductProduct < < ActiveRecord::BaseActiveRecord::Base
has_and_belongs_to_manyhas_and_belongs_to_many :ca :categorytegory
endend
Une catégorie possède n produits et un produit n catégories
ou
Les finderProduct.find(Product.find(:all:all, , :conditions:conditions => { :price => => { :price => 18 })18 })Product.find(Product.find(:first:first))Product.find(Product.find(:last:last))
Product.find(Product.find(:all:all, , :conditions:conditions => { :price => => { :price => 18 })18 })Product.find(Product.find(:first:first))Product.find(Product.find(:last:last))
Plusieurs finder dynamiques:
Product.Product.find_by_namefind_by_name(‘rails’)(‘rails’)Product.Product.find_all_by_namefind_all_by_name(‘rails’)(‘rails’)Product.Product.find_by_name_and_pricefind_by_name_and_price(‘book’, 18)(‘book’, 18)
Product.Product.find_by_namefind_by_name(‘rails’)(‘rails’)Product.Product.find_all_by_namefind_all_by_name(‘rails’)(‘rails’)Product.Product.find_by_name_and_pricefind_by_name_and_price(‘book’, 18)(‘book’, 18)
Product.find(:all, Product.find(:all, :joins:joins => :category, => :category, :conditions:conditions => { :categories => { :name => => { :categories => { :name => "books" }})"books" }})
Product.find(:all, Product.find(:all, :joins:joins => :category, => :category, :conditions:conditions => { :categories => { :name => => { :categories => { :name => "books" }})"books" }})
Jointure:
Les scopeclass class ProductProduct < ActiveRecord::Base < ActiveRecord::Base
named_scopenamed_scope :recent, :recent, lambdalambda { { { { :conditions:conditions => ["created_at > ?", => ["created_at > ?", 5.days.ago5.days.ago] } } ] } } named_scopenamed_scope :limit, :limit, lambdalambda { |n| { { |n| { :limit:limit => n } } => n } } named_scopenamed_scope :ordered_by_name, { :ordered_by_name, { :order:order => "NAME ASC" } => "NAME ASC" }endend
class class ProductProduct < ActiveRecord::Base < ActiveRecord::Base named_scopenamed_scope :recent, :recent, lambdalambda { { { { :conditions:conditions => ["created_at > ?", => ["created_at > ?", 5.days.ago5.days.ago] } } ] } } named_scopenamed_scope :limit, :limit, lambdalambda { |n| { { |n| { :limit:limit => n } } => n } } named_scopenamed_scope :ordered_by_name, { :ordered_by_name, { :order:order => "NAME ASC" } => "NAME ASC" }endend
Invoquer les scope, de façon chaînable
Product.Product.recentrecent..ordered_by_nameordered_by_name..limitlimit(2)(2)Product Load (0.3ms)Product Load (0.3ms) SELECT * FROM "products" WHERE (created_at > SELECT * FROM "products" WHERE (created_at > '2011-03-17 21:08:24') ORDER BY NAME ASC LIMIT 2'2011-03-17 21:08:24') ORDER BY NAME ASC LIMIT 2
Product.Product.recentrecent..ordered_by_nameordered_by_name..limitlimit(2)(2)Product Load (0.3ms)Product Load (0.3ms) SELECT * FROM "products" WHERE (created_at > SELECT * FROM "products" WHERE (created_at > '2011-03-17 21:08:24') ORDER BY NAME ASC LIMIT 2'2011-03-17 21:08:24') ORDER BY NAME ASC LIMIT 2
Les validationsclass class ProductProduct < ActiveRecord::Base < ActiveRecord::Base
belongs_to :categorybelongs_to :categoryvalidates_presence_ofvalidates_presence_of :name, :category_id :name, :category_idvalidates_numericality_ofvalidates_numericality_of :price :price
class class ProductProduct < ActiveRecord::Base < ActiveRecord::Basebelongs_to :categorybelongs_to :categoryvalidates_presence_ofvalidates_presence_of :name, :category_id :name, :category_idvalidates_numericality_ofvalidates_numericality_of :price :price
et bien d’autres:
validates_confirmation_ofvalidates_exclusion_ofvalidates_format_ofvalidates_inclusion_ofvalidates_length_of…
Le contrôleur avec ActionController
•L’interface entre le modèle et la vue
•Association automatique méthode / vue
•@variable directement disponible dans la vue
•Possibilité de filtrer des actions
Le routeurconfig/routes.rb
ActionController::Routing::Routes.draw do |map|ActionController::Routing::Routes.draw do |map|# # RESTfulRESTful /categories/1/products/1/map. /categories/1/products/1/map.resourcesresources :categories do | :categories do |category|category|
category.resources :productscategory.resources :productsendend# route nommée# route nomméemap.map.purchasepurchase 'products/:id/purchase', :controller => 'catalog', :action 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'=> 'purchase'# namespace# namespacemap.map.namespacenamespace :admin do |admin| :admin do |admin|
# /admin/products# /admin/productsadmin.resource :productsadmin.resource :products
endend# route par défaut# route par défautmap.map.rootroot :controller => 'products' :controller => 'products'endend
ActionController::Routing::Routes.draw do |map|ActionController::Routing::Routes.draw do |map|# # RESTfulRESTful /categories/1/products/1/map. /categories/1/products/1/map.resourcesresources :categories do | :categories do |category|category|
category.resources :productscategory.resources :productsendend# route nommée# route nomméemap.map.purchasepurchase 'products/:id/purchase', :controller => 'catalog', :action 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'=> 'purchase'# namespace# namespacemap.map.namespacenamespace :admin do |admin| :admin do |admin|
# /admin/products# /admin/productsadmin.resource :productsadmin.resource :products
endend# route par défaut# route par défautmap.map.rootroot :controller => 'products' :controller => 'products'endend
Les routesrake routes categories categories GETGET /categories(.:format) /categories(.:format) {:controller=>"categories", :action=>"index"} {:controller=>"categories", :action=>"index"} POSTPOST /categories(.:format) {:controller=>"categories", :action=>"create"} /categories(.:format) {:controller=>"categories", :action=>"create"} new_category GET /categories/new(.:format) new_category GET /categories/new(.:format) {:controller=>"categories", :action=>"new"}edit_category GET {:controller=>"categories", :action=>"new"}edit_category GET /categories/:id/edit(.:format) {:controller=>"categories", :action=>"edit"} /categories/:id/edit(.:format) {:controller=>"categories", :action=>"edit"} category GET /categories/:id(.:format) category GET /categories/:id(.:format) {:controller=>"categories", :action=>"show"} {:controller=>"categories", :action=>"show"} PUTPUT /categories/:id(.:format) {:controller=>"categories", :action=>"update"} /categories/:id(.:format) {:controller=>"categories", :action=>"update"} DELETEDELETE /categories/:id(.:format) /categories/:id(.:format) {:controller=>"categories", :action=>"destroy"} products GET {:controller=>"categories", :action=>"destroy"} products GET /products(.:format) {:controller=>"products", :action=>"index"} /products(.:format) {:controller=>"products", :action=>"index"} POST /products(.:format) POST /products(.:format) {:controller=>"products", :action=>"create"} new_product GET {:controller=>"products", :action=>"create"} new_product GET /products/new(.:format) {:controller=>"products", :action=>"new"} /products/new(.:format) {:controller=>"products", :action=>"new"} edit_product GET /products/:id/edit(.:format) edit_product GET /products/:id/edit(.:format) {:controller=>"products", :action=>"edit"} product GET {:controller=>"products", :action=>"edit"} product GET /products/:id(.:format) {:controller=>"products", :action=>"show"} /products/:id(.:format) {:controller=>"products", :action=>"show"} PUT /products/:id(.:format) PUT /products/:id(.:format) {:controller=>"products", :action=>"update"} DELETE {:controller=>"products", :action=>"update"} DELETE /products/:id(.:format) {:controller=>"products", :action=>"destroy"} /products/:id(.:format) {:controller=>"products", :action=>"destroy"} /:controller/:action/:id /:controller/:ac /:controller/:action/:id /:controller/:action/:id(.:format) tion/:id(.:format) rootroot / / {:controller=>"products", :action=>"index"}{:controller=>"products", :action=>"index"}
categories categories GETGET /categories(.:format) /categories(.:format) {:controller=>"categories", :action=>"index"} {:controller=>"categories", :action=>"index"} POSTPOST /categories(.:format) {:controller=>"categories", :action=>"create"} /categories(.:format) {:controller=>"categories", :action=>"create"} new_category GET /categories/new(.:format) new_category GET /categories/new(.:format) {:controller=>"categories", :action=>"new"}edit_category GET {:controller=>"categories", :action=>"new"}edit_category GET /categories/:id/edit(.:format) {:controller=>"categories", :action=>"edit"} /categories/:id/edit(.:format) {:controller=>"categories", :action=>"edit"} category GET /categories/:id(.:format) category GET /categories/:id(.:format) {:controller=>"categories", :action=>"show"} {:controller=>"categories", :action=>"show"} PUTPUT /categories/:id(.:format) {:controller=>"categories", :action=>"update"} /categories/:id(.:format) {:controller=>"categories", :action=>"update"} DELETEDELETE /categories/:id(.:format) /categories/:id(.:format) {:controller=>"categories", :action=>"destroy"} products GET {:controller=>"categories", :action=>"destroy"} products GET /products(.:format) {:controller=>"products", :action=>"index"} /products(.:format) {:controller=>"products", :action=>"index"} POST /products(.:format) POST /products(.:format) {:controller=>"products", :action=>"create"} new_product GET {:controller=>"products", :action=>"create"} new_product GET /products/new(.:format) {:controller=>"products", :action=>"new"} /products/new(.:format) {:controller=>"products", :action=>"new"} edit_product GET /products/:id/edit(.:format) edit_product GET /products/:id/edit(.:format) {:controller=>"products", :action=>"edit"} product GET {:controller=>"products", :action=>"edit"} product GET /products/:id(.:format) {:controller=>"products", :action=>"show"} /products/:id(.:format) {:controller=>"products", :action=>"show"} PUT /products/:id(.:format) PUT /products/:id(.:format) {:controller=>"products", :action=>"update"} DELETE {:controller=>"products", :action=>"update"} DELETE /products/:id(.:format) {:controller=>"products", :action=>"destroy"} /products/:id(.:format) {:controller=>"products", :action=>"destroy"} /:controller/:action/:id /:controller/:ac /:controller/:action/:id /:controller/:action/:id(.:format) tion/:id(.:format) rootroot / / {:controller=>"products", :action=>"index"}{:controller=>"products", :action=>"index"}
Les contrôleursclass ProductsController < ApplicationControllerclass ProductsController < ApplicationController
before_filterbefore_filter :authenticate, :authenticate, :only:only => :index => :index
def def indexindex @products@products = Product.all #disponible dans la vue = Product.all #disponible dans la vuerespond_torespond_to do |format| do |format|
# GET /products# GET /productsformat.html # index.html.erbformat.html # index.html.erb# GET /products.xml# GET /products.xmlformat.xml { format.xml { renderrender :xml => :xml => @products@products } } endendendend
privateprivatedef def authenticateauthenticate# do some stuff# do some stuffendend
endend
class ProductsController < ApplicationControllerclass ProductsController < ApplicationControllerbefore_filterbefore_filter :authenticate, :authenticate, :only:only => :index => :index
def def indexindex @products@products = Product.all #disponible dans la vue = Product.all #disponible dans la vuerespond_torespond_to do |format| do |format|
# GET /products# GET /productsformat.html # index.html.erbformat.html # index.html.erb# GET /products.xml# GET /products.xmlformat.xml { format.xml { renderrender :xml => :xml => @products@products } } endendendend
privateprivatedef def authenticateauthenticate# do some stuff# do some stuffendend
endend
La vue avec ActionView
•Les vues : pas forcément du HTML
•Layout
•Partials
•Helpers
Les types de vues
i18n des vuesapp/views/products/app/views/products/index.fr.html.erbindex.fr.html.erbapp/views/products/app/views/products/index.es.html.erbindex.es.html.erb
app/views/products/app/views/products/index.fr.html.erbindex.fr.html.erbapp/views/products/app/views/products/index.es.html.erbindex.es.html.erb
Rails >= 2.3
Différents formats
app/views/products/app/views/products/index.iphone.erbindex.iphone.erbapp/views/products/app/views/products/index.xml.erbindex.xml.erb
app/views/products/app/views/products/index.iphone.erbindex.iphone.erbapp/views/products/app/views/products/index.xml.erbindex.xml.erb
Le layoutSquelette de base app/views/app/views/layoutslayouts//
application.html.erbapplication.html.erbapp/views/app/views/layoutslayouts//application.html.erbapplication.html.erb
Modifiable depuis le contrôleur
<!DOCTYPE…><html><body><<!DOCTYPE…><html><body><%= %= yieldyield %></body></html> %></body></html><!DOCTYPE…><html><body><<!DOCTYPE…><html><body><%= %= yieldyield %></body></html> %></body></html>
Les partialsDes morceaux de vue
<!DOCTYPE…><html><body><!DOCTYPE…><html><body><%= render :<%= render :partialpartial => => ‘products/sidebar’ %><%= ‘products/sidebar’ %><%= yieldyield %></body></html>%></body></html>
<!DOCTYPE…><html><body><!DOCTYPE…><html><body><%= render :<%= render :partialpartial => => ‘products/sidebar’ %><%= ‘products/sidebar’ %><%= yieldyield %></body></html>%></body></html>
app/views/products/app/views/products/_sidebar.html.erb_sidebar.html.erbapp/views/products/app/views/products/_sidebar.html.erb_sidebar.html.erb
Exemple de vue: liste
<h1>Listing categories</h1><table> <tr> <h1>Listing categories</h1><table> <tr> <th>Name</th> </tr><% @categories.each do |category| <th>Name</th> </tr><% @categories.each do |category| %> <tr> <td><%=h %> <tr> <td><%=h truncatetruncate(category.name, :size => 10) (category.name, :size => 10) %></td> <td><%= link_to 'Show', category %></td> %></td> <td><%= link_to 'Show', category %></td> <td><%= link_to 'Edit', <td><%= link_to 'Edit', edit_category_pathedit_category_path(category) (category) %></td> <td><%= link_to 'Destroy', category, :%></td> <td><%= link_to 'Destroy', category, :confirmconfirm => => 'Are you sure?', :'Are you sure?', :methodmethod => :delete %></td> </tr><% end => :delete %></td> </tr><% end %></table><br /><%= link_to 'New category', %></table><br /><%= link_to 'New category', new_category_path %>new_category_path %>
<h1>Listing categories</h1><table> <tr> <h1>Listing categories</h1><table> <tr> <th>Name</th> </tr><% @categories.each do |category| <th>Name</th> </tr><% @categories.each do |category| %> <tr> <td><%=h %> <tr> <td><%=h truncatetruncate(category.name, :size => 10) (category.name, :size => 10) %></td> <td><%= link_to 'Show', category %></td> %></td> <td><%= link_to 'Show', category %></td> <td><%= link_to 'Edit', <td><%= link_to 'Edit', edit_category_pathedit_category_path(category) (category) %></td> <td><%= link_to 'Destroy', category, :%></td> <td><%= link_to 'Destroy', category, :confirmconfirm => => 'Are you sure?', :'Are you sure?', :methodmethod => :delete %></td> </tr><% end => :delete %></td> </tr><% end %></table><br /><%= link_to 'New category', %></table><br /><%= link_to 'New category', new_category_path %>new_category_path %>
Exemple de vue: édition
<% <% form_forform_for(@product) do |f| %> <%= f.error_messages %> (@product) do |f| %> <%= f.error_messages %> <p> <%= f.label :name %><br /> <%= f.<p> <%= f.label :name %><br /> <%= f.text_fieldtext_field :name :name %> </p><p> <%= f.label :description %><br /> <%= %> </p><p> <%= f.label :description %><br /> <%= f.f.text_areatext_area :description %> </p><p> <%= f.label :available :description %> </p><p> <%= f.label :available %><br /> <%= f.%><br /> <%= f.check_boxcheck_box :available %> </p><p> <%= :available %> </p><p> <%= f.label :category_id %><br /> <%= f.f.label :category_id %><br /> <%= f.selectselect :category_id, :category_id, (Category.all.map do |c| [c.name, c.id] end) %> </p><p> <(Category.all.map do |c| [c.name, c.id] end) %> </p><p> <%= f.%= f.submitsubmit 'Update' %> </p><% end %><%= 'Update' %> </p><% end %><%= link_tolink_to 'Show', @product %> |<%= 'Show', @product %> |<%= link_tolink_to 'Back', products_path %> 'Back', products_path %>
<% <% form_forform_for(@product) do |f| %> <%= f.error_messages %> (@product) do |f| %> <%= f.error_messages %> <p> <%= f.label :name %><br /> <%= f.<p> <%= f.label :name %><br /> <%= f.text_fieldtext_field :name :name %> </p><p> <%= f.label :description %><br /> <%= %> </p><p> <%= f.label :description %><br /> <%= f.f.text_areatext_area :description %> </p><p> <%= f.label :available :description %> </p><p> <%= f.label :available %><br /> <%= f.%><br /> <%= f.check_boxcheck_box :available %> </p><p> <%= :available %> </p><p> <%= f.label :category_id %><br /> <%= f.f.label :category_id %><br /> <%= f.selectselect :category_id, :category_id, (Category.all.map do |c| [c.name, c.id] end) %> </p><p> <(Category.all.map do |c| [c.name, c.id] end) %> </p><p> <%= f.%= f.submitsubmit 'Update' %> </p><% end %><%= 'Update' %> </p><% end %><%= link_tolink_to 'Show', @product %> |<%= 'Show', @product %> |<%= link_tolink_to 'Back', products_path %> 'Back', products_path %>
Autour de Rails
•Communauté active
•Autour de github
•Gems
•Authentification
•Pagination
•…
config/environment.rbRails::Initializer.run do |Rails::Initializer.run do |config|config|
config.gemconfig.gem ‘hpricot’ ‘hpricot’endend
Rails::Initializer.run do |Rails::Initializer.run do |config|config|
config.gemconfig.gem ‘hpricot’ ‘hpricot’endend
Documentation
•http://www.rubyonrails.org/
•http://guides.rubyonrails.org/
•http://apidock.com/rails
•http://railsforzombies.org/
•http://groups.google.com/group/railsfrance
Merci, c’est à vous !
•Des questions ?
_fuse mcatty@synbioz.com
top related