Buenas Prácticas de desarrollo en Ruby on Rails

Preview:

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

¿...?

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™

Recommended