Upload
sergio-gil
View
3.417
Download
2
Embed Size (px)
DESCRIPTION
Citation preview
Buenas prácticas de desarrollo en
Ruby on Rails
Sergio Gil Pérez de la Manga
Súper Mega Disclaimer
Todo el contenido de esta charla se basa en opiniones personalísimas del autor, salvo cuando se citen
expresamente las opiniones de otros autores, en cuyo caso se trata de
interpretaciones personalísimas de las mismas
Súper Mega Disclaimer
Lo que tenga un asterisco gordo al lado, es que no me lo he inventado (y está en la lista de referencias)
*
“Best Practice is an idea that asserts that there is a technique, method, or process
that is more e!ective at delivering a particular outcome than any other
technique, method or process”
*
“En el Go todas las piezas son iguales; en el Ajedrez,
son diferentes, y obedecen a reglas de desplazamiento
distintas, lo que complica el aprendizaje de la regla,
pero facilita la tarea del debutante: un cuadro de
juego codificado es de un uso más sencillo que una libertad completa con la
que no se sabe qué hacer”
Pierre Aroutche!,“El Go”
Cosas de las que no voy a hablar (por obvias)
TESTING
Buen Código
Código fácil de testear
Refactorizar sin tests es un deporte
de riesgo
Control de Versiones
Infórmate, participa, interactúa
LEE
LEE
Documentación
LEE
DocumentaciónBlogs
LEE
DocumentaciónBlogs
Listas de correo
ESCRIBE
DocumentaciónBlogs
Listas de correo
Acude a Conferencias
Acude a Conferencias
¡Y habla si te dejan!
Ven a las quedadas
Haz pair programming si puedes
Hablemos de código
Conoce tus herramientas
some_things = []some_other_things.each do |some_other_thing| some_things << some_other_thing.wadusend
some_things = some_other_things.map do |some_other_thing| some_other_thing.wadusend
some_things = some_other_things.map(&:wadus)
DRY pero no tanto
Don’t Repeat Yourself
*
“Every piece of knowledge must have a single, unambiguous, authoritative
representation within a system”
Pero DRY es un medio, no un fin
Pero DRY es un medio, no un finLo único importante es:
Pero DRY es un medio, no un finLo único importante es:
Legibilidad
Pero DRY es un medio, no un finLo único importante es:
LegibilidadMantenibilidad
Pero DRY es un medio, no un finLo único importante es:
LegibilidadMantenibilidad
login: &login adapter: mysql username: username password: password host: mysql.example.com
development: <<: *login database: app_dev
test: <<: *login database: app_test
production: <<: *login database: app_prod
MVC
Skinny Controller, Fat Model
*
Modelo cocinero, controlador camarero
*
class Person < AR::B has_one :addressend
class PeopleController < ACend
<% people = Person.find( :conditions => ["added_at > ? and deleted = ?", Time.now.utc, false], :order => "last_name, first_name") %><% people.reject { |p| p.address.nil? }.each do |person| %> <div class="person"> <span class="name"> <%= person.last_name %>, <%= person.first_name %> </span> <span class="age"> <%= (Date.today - person.birthdate) / 365 %> </span> </div><% end %> *
class Person < AR::B has_one :addressend
class PeopleController < AC def index @people = Person.find( :conditions => ["added_at > ? and deleted = ?", Time.now.utc, false], :order => "last_name, first_name") @people = @people.reject { |p| p.address.nil? } endend
<% @people.each do |person| %> <div class="person"> <span class="name"> <%= person.last_name %>, <%= person.first_name %> </span> <span class="age"> <%= (Date.today - person.birthdate) / 365 %> </span> </div><% end %> *
class Person < ActiveRecord::Base has_one :address def self.find_recent people = find( :conditions => ["added_at > ? and deleted = ?", Time.now.utc, false], :order => "last_name, first_name") people.reject { |p| p.address.nil? } end def name "#{last_name}, #{first_name}" end def age (Date.today - person.birthdate) / 365 endendclass PeopleController < ActionController::Base def index @people = Person.find_recent endend
<% @people.each do |person| %> <div class="person"> <span class="name"><%= person.name %></span> <span class="age"><%= person.age %></span> </div><% end %> *
Hacia el controlador trivial
Hacia el controlador trivial
Plugins como resource_controller
Hacia el controlador trivial
Plugins como resource_controller
class PostsController < ApplicationController resource_controllerend
REST
RESTricción liberadora
Si lo que quieres hacer no encaja en REST
• Puede que pertenezca al 20% de cosas que no encajan
Si lo que quieres hacer no encaja en REST
• Puede que pertenezca al 20% de cosas que no encajan
• Puede que lo estés enfocando mal
Si lo que quieres hacer no encaja en REST
• Puede que pertenezca al 20% de cosas que no encajan
• Puede que lo estés enfocando mal
• Así que dale una vuelta
Si lo que quieres hacer no encaja en REST
Demeter y el Acoplamiento
*
Un método de un objeto sólo debe llamar a:
*
Un método de un objeto sólo debe llamar a:
• Métodos del mismo objeto
*
Un método de un objeto sólo debe llamar a:
• Métodos del mismo objeto
• Métodos de objetos directamente relacionados
comment.article.author.address.city.country.two_letter_code
class User < AR::B # emailend
class Blog < AR::B belongs_to :user has_many :articlesend
class Article < AR::B belongs_to :blogend
article.blog.user.email
class User < AR::B # emailend
class Blog < AR::B belongs_to :user has_many :articles delegate :email, :to => :userend
class Article < AR::B belongs_to :blog delegate :email, :to => :blogend
article.email
El termómetro del test
before(:each) do @country = mock_model(Country, :two_letter_code => 'es') @city = mock_model(City, :country => @country) @address = mock_model(Address, :city => @city) @author = mock_model(User, :address => @address) @article = mock_model(Article, :author => @author) @comment = mock_model(Comment, :article => @article)end
it "should have two_letter_code 'es'" do @comment.article.author.adress.city.country.two_letter_code.should == 'es'end
El termómetro del test
before(:each) do @comment = mock_model(Comment, :author_country_code => 'es')end
it "should have two_letter_code 'es'" do @comment.author_country_code.should == 'es'end
Uso de convenciones
Las de Rails: piénsatelo antes de saltártelas
Crea las tuyas propias
Crea las tuyas propias¡Y cúmplelas!
Refactorización
Refactoriza durante el desarrollo...
*
...y no al final
*
*
1. No refactorices y añadas funcionalidad a la vez
*
1. No refactorices y añadas funcionalidad a la vez
2. Haz tests antes de refactorizar. Y ejecútalos a menudo
*
1. No refactorices y añadas funcionalidad a la vez
2. Haz tests antes de refactorizar. Y ejecútalos a menudo
3. Refactoriza en pasos pequeños
*
Uso de variables
Minimizar uso de variables, y su scope
def show @post = Post.find(params[:id]) @related_posts = Post.find(:all, :conditions => { :category_id => @post.category_id }, :limit => 5)end
<h2><%= @post.title %></h2><%= simple_format(@post.body) %>
<%- @recent_posts.each do |post| -%> ...<%- end -%>
Minimizar uso de variables, y su scope
<h2><%= @post.title %></h2><%= simple_format(@post.body) %>
<%- @recent_posts.each do |post| -%> ...<%- end -%>
def show @post = Post.find(params[:id]) @related_posts = @post.recent_postsend
<h2><%= @post.title %></h2><%= simple_format(@post.body) %>
<%- @post.recent_posts.each do |post| -%> ...<%- end -%>
Minimizar uso de variables, y su scope
def show @post = Post.find(params[:id])end
Minimizar uso de variables, y su scope
Minimizar uso de variables, y su scope
Mi convención:
Minimizar uso de variables, y su scope
Mi convención:Una variable de instancia por acción
Minimizar uso de variables, y su scope
Mi convención:Una variable de instancia por acción
• Instancia de un modelo
Minimizar uso de variables, y su scope
Mi convención:Una variable de instancia por acción
• Instancia de un modelo
• Nombrada como el modelo
Minimizar uso de variables, y su scope
Mi convención:Una variable de instancia por acción
• Instancia de un modelo
• Nombrada como el modelo
• Array de instancias de un modelo
Minimizar uso de variables, y su scope
Mi convención:Una variable de instancia por acción
• Instancia de un modelo
• Nombrada como el modelo
• Array de instancias de un modelo
• Nombrada como el modelo en plural
El idioma del código
• Se trata de intercambiar, ¿no?
• Se trata de intercambiar, ¿no?
• Rails hace un gran esfuerzo por acercarse al lenguaje humano. Si escribimos en spanglish, nos lo cargamos
• Se trata de intercambiar, ¿no?
• Rails hace un gran esfuerzo por acercarse al lenguaje humano. Si escribimos en spanglish, nos lo cargamos
has_many :legajos
Recomendaciones bibliográficas
¿...?
Muchas Gracias
[email protected]@bebanjo.comhttp://lacoctelera.com/porrashttp://twitter.com/porras
Referencias
http://en.wikipedia.org/wiki/Best_practiceshttp://en.wikipedia.org/wiki/Law_of_Demeterhttp://www.ccs.neu.edu/home/lieber/LoD.htmlhttp://brian.maybeyoureinsane.net/blog/2006/12/15/law‐of‐demeter‐or‐how‐to‐avoid‐coding‐yourself‐into‐a‐corner‐in‐rails/http://weblog.jamisbuck.org/2006/10/18/skinny‐controller‐fat‐modelhttp://c2.com/cgi/wiki?DontRepeatYourself
Pierre Aroutche!, “El Go”Andrew Hunt, Dave Thomas, “The Pragmatic Programmers”Andrew Hunt, Venkat Subramaniam, “Practices of an Agile Developer”Martin Fowler, “Refactoring: Improving the Design of Existing Code”
Enjuto Mojamuto en:“Debuggeando una aplicación sin tests”
Caca de bug
Como Se Fue Vino™