Este documento presenta las buenas prácticas de desarrollo en Ruby on Rails. Cubre temas como testing, control de versiones, documentación, arquitectura MVC, REST, acoplamiento, refactorización, uso de variables y el idioma del código. El objetivo es compartir opiniones sobre cómo escribir código Ruby on Rails de manera limpia, legible y mantenible.
2. 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
3. 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)
4. “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”
5.
6.
7. “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”
37. Pero DRY es un medio, no un fin
Lo único importante es:
38. Pero DRY es un medio, no un fin
Lo único importante es:
Legibilidad
39. Pero DRY es un medio, no un fin
Lo único importante es:
Legibilidad
Mantenibilidad
40. login: &login
adapter: mysql
username: username
password: password
host: mysql.example.com
Pero DRY es un medio, no un fin development:
<<: *login
Lo único importante es:
database: app_dev
Legibilidad
Mantenibilidad test:
<<: *login
database: app_test
production:
<<: *login
database: app_prod
45. class Person < AR::B
has_one :address
end
class PeopleController < AC
end
<% people = Person.find(
:conditions => [quot;added_at > ? and deleted = ?quot;, Time.now.utc, false],
:order => quot;last_name, first_namequot;) %>
<% people.reject { |p| p.address.nil? }.each do |person| %>
<div class=quot;personquot;>
<span class=quot;namequot;>
<%= person.last_name %>, <%= person.first_name %>
</span>
<span class=quot;agequot;>
<%= (Date.today - person.birthdate) / 365 %>
</span>
</div>
<% end %>
46. class Person < AR::B
has_one :address
end
class PeopleController < AC
def index
@people = Person.find(
:conditions => [quot;added_at > ? and deleted = ?quot;, Time.now.utc, false],
:order => quot;last_name, first_namequot;)
@people = @people.reject { |p| p.address.nil? }
end
end
<% @people.each do |person| %>
<div class=quot;personquot;>
<span class=quot;namequot;>
<%= person.last_name %>, <%= person.first_name %>
</span>
<span class=quot;agequot;>
<%= (Date.today - person.birthdate) / 365 %>
</span>
</div>
<% end %>
47. class Person < ActiveRecord::Base
has_one :address
def self.find_recent
people = find(
:conditions => [quot;added_at > ? and deleted = ?quot;, Time.now.utc, false],
:order => quot;last_name, first_namequot;)
people.reject { |p| p.address.nil? }
end
def name
quot;#{last_name}, #{first_name}quot;
end
def age
(Date.today - person.birthdate) / 365
end
end
class PeopleController < ActionController::Base
def index
@people = Person.find_recent
end
end
<% @people.each do |person| %>
<div class=quot;personquot;>
<span class=quot;namequot;><%= person.name %></span>
<span class=quot;agequot;><%= person.age %></span>
</div>
<% end %>
54. Si lo que quieres hacer no encaja en REST
• Puede que pertenezca al 20% de
cosas que no encajan
55. 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
56. 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
62. class User < AR::B
# email
end
class Blog < AR::B
belongs_to :user
has_many :articles
end
class Article < AR::B
belongs_to :blog
end
article.blog.user.email
63. class User < AR::B
# email
end
class Blog < AR::B
belongs_to :user
has_many :articles
delegate :email, :to => :user
end
class Article < AR::B
belongs_to :blog
delegate :email, :to => :blog
end
article.email
64. 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 quot;should have two_letter_code 'es'quot; do
@comment.article.author.adress.city.country.two_letter_code.should == 'es'
end
65. El termómetro del test
before(:each) do
@comment = mock_model(Comment, :author_country_code => 'es')
end
it quot;should have two_letter_code 'es'quot; do
@comment.author_country_code.should == 'es'
end
79. 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 -%>
80. Minimizar uso de variables, y su scope
def show
@post = Post.find(params[:id])
@related_posts = @post.recent_posts
end
<h2><%= @post.title %></h2>
<%= simple_format(@post.body) %>
<%- @recent_posts.each do |post| -%>
...
<%- end -%>
81. Minimizar uso de variables, y su scope
def show
@post = Post.find(params[:id])
end
<h2><%= @post.title %></h2>
<%= simple_format(@post.body) %>
<%- @post.recent_posts.each do |post| -%>
...
<%- end -%>
84. Minimizar uso de variables, y su scope
Mi convención:
Una variable de instancia por acción
85. Minimizar uso de variables, y su scope
Mi convención:
Una variable de instancia por acción
• Instancia de un
modelo
86. 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
87. Minimizar uso de variables, y su scope
Mi convención:
Una variable de instancia por acción
• Instancia de un • Array de instancias
modelo de un modelo
• Nombrada como el
modelo
88. Minimizar uso de variables, y su scope
Mi convención:
Una variable de instancia por acción
• Instancia de un • Array de instancias
modelo de un modelo
• Nombrada como el • Nombrada como el
modelo modelo en plural
92. • Se trata de
intercambiar, ¿no?
• Rails hace un gran
esfuerzo por acercarse
al lenguaje humano. Si
escribimos en
spanglish, nos lo
cargamos
93. • 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
99. Referencias
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”
http://en.wikipedia.org/wiki/Best_practices
http://en.wikipedia.org/wiki/Law_of_Demeter
http://www.ccs.neu.edu/home/lieber/LoD.html
http://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‐model
http://c2.com/cgi/wiki?DontRepeatYourself