Peculiaridades de Ruby para desarrolladores Java
Durante estos días en que he estado aprendiendo Ruby y Rails para desarrollar SprintTracker 0.1 me he ido encontrando diferentes elementos de sintaxis que me han sorprendido, confundido o incluso disgustado (al menos inicialmente). He pensado que podía ser buen idea revisarlos porque creo que conociéndolos resulta mucho más fácil entender los fragmentos de código presentados en la mayoría de artículos sobre el tema. Como son bastantes cosas lo voy a dividir en dos entradas del blog.
Llaves y paréntesis opcionales
En Ruby los paréntesis y las llaves (() y {}) pueden omitirse cuando son obvios. Por ello en Rails es habitual encontrar invocaciones a métodos de la forma:
nombre_método parametro
Que es equivalente a :
nombre_metodo(parametro)
Cuando un mapa (hash) es el último parámetro de un método las llaves pueden ignorarse, así que uniendo esto a lo anterior es habitual encontrarse llamadas a métodos de la forma:
nombre_metodo primer_parametro, :clave1 => "valor1", :clave2 => "valor2"
Que es equivalente a:
nombre_metodo(primer_parametro, {:clave1 => "valor1", :clave2 => "valor2"})
Y que un programador Java escribiría como:
Map segundoParametro = new HashMap();
segundoParametro("clave1", "valor1");
segundoParametro("clave2", "valor2");
nombreMetodo(primerMarametro, segundoParametro);
Después de programar sólo unas horas en Ruby echo mucho de menos en Java un inicializador rápido para mapas. El ejemplo de código anterior lo dice todo.
Interpolación de expresiones
Ruby permite acceder al valor de variables desde las cadenas de texto usando el operador #{nombre_variable}
Esta funcionalidad es muy sencillo pero me despistó un poco el primer día con Ruby y por eso he decidido incluirla aquí. Con un ejemplo se entiende perfectamente:
my_number = 5
puts "Mi numero favorito es el #{my_number}"
Que imprimiría en pantalla:
Mi numero favorito es el 5
Es importante tener en cuenta que la interpolación de variables sólo funciona en cadenas de texto que están entre dobles comillas, las comillas simples no implementan este comportamiento.
nil en lugar de null ¿son lo mismo?
La respuesta correcta es casi. Se usa con el mismo objetivo, pero en Ruby nil es un objeto completo con el que puede operarse de esta forma nunca puede ocurrir una NullPointerException (aunque si otros errores similares, pero quizá no tan fastidiosos).
Esto objeto sobrecarga varios operadores lo que evita las típicas comprobaciones previas de Java por si el objeto es null. En caso de que se invoque un método que no existe en nil un mensaje de error muy informativo informa del nombre del método que ha intentado invocarse explicando que en el objeto nil no existe.
Símbolos y sus usos en Rails
En Ruby toda variable precedida por : es un símbolo. Los símbolos se usan para referirse a variables, métodos, etc. Por ejemplo si tienes un campo de clase denominado projects es posible referirse a él como :projects.
Rails usa mucho los símbolos por ejemplo:
has_many :projects
Otro ejemplo muy abundante en Rails es el uso de símbolos como claves en las tablas, por ejemplo:
redirect_to :action => 'show', :id => product.id
En Java es más habitual usar Strings como claves, así que este uso de los símbolos al principio pensé que escondía algún elemento nuevo de la sintaxis de Ruby. Nada más lejos de la realidad, en ruby todo son objetos incluso los símbolos y por tanto pueden usarse como claves de sus mapas. Esas mismas claves podrían ser perfectamente strings aunque parece que Rails aprovecha el que sean símbolos para facilitar la implementación de ciertas funcionalidades (esto es pura intuición, no lo he confirmado aún).
El valor de retorno de un método
En Ruby, no es obligatorio usar la palabra clave return para señalar el valor de retorno. En caso de que no se use el método devolverá el resultado de la última línea. Por ejemplo:
def five
5
end
Es un método que devuelve el número 5. Exactamente equivalente a:
def five
return 5
end
Este es uno de los aspectos que más me cuestan viniendo de Java así que al principio estaba poniendo los return, pero parece que lo habitual es no hacerlo, así que habrá que acostumbrarse.
Invocación a métodos o envío de mensajes
En Ruby la invocación a un método se trata como el envío de un mensaje al objeto con el nombre del método y sus parámetros. Aunque pueda parecer un tecnicismo esta diferencia con Java tiene algunas repercusiones prácticas. Por ejemplo, para invocar a un método cuyo nombre obtendremos de forma dinámica se usa el método send que tienen todos los objetos:
def invoca_metodo(objeto, nombre_del_metodo)
objeto.send(nombre_del_metodo)
end
Esta es una sintaxis muy cómoda pero nada que no pueda lograrse con los mecanismos de reflexión de Java.
Algo más sorprendente es la capacidad de capturar llamadas a métodos que no existen. Para ello se usa el método method_missing(aSymbol, [*args]) que es invocado por el un objeto Ruby cuando recibe un mensaje con un nombre de método que no existe. Esta funcionalidad es tremendamente útil para crear proxies y patrones similares. Por ejemplo:
class MyClassProxy
realClass = MyClass.new
def method_missing(method_name, method_params)
realClass.send(method_name, method_params)
end
end
En el libro Programming Ruby puede encontrarse un curioso ejemplo de uso de method_missing
Esto es todo por ahora. En una próxima entrada trataré los closures, operadores como ||=, expresiones regulares, ... y algunas conclusiones sobre las diferencias de sintaxis en comparación con Java.


Y lo que no te gusta...
Hola Jorge,
Muy instructiva esta entrada, como el resto de la serie, resultan muy útiles y te animo a continuarlas. Pero echo en falta, sobre todo en esta entrada, que te mojes un poco sobre aquellas cosas que no te gustan desde el punto de vista de alguien que viene del mundo Java. Ya sé que no es conveniente criticar al principio, cuando aún no se tiene una visión global y es fácil dejarse llevar por prejuicios y convencionalismos.
Comentas que algunas cosas te han disgustado, pero no explicas cuáles ni por qué. A mi particularmente no me gusta la idea de sacrificar la legibilidad del código con tal de ahorrarse algunos caracteres. Claro que la legibilidad es algo muy personal... La impresión que saco es que Ruby te da más libertad que Java, te deja ser más anárquico, menos disciplinado en cuanto a la sintaxis y la organización del código, con la esperanza de que sea el programador concienciado el que haga un uso juicioso de esta libertad. Eso hace de Ruby un lenguaje más elitista que Python, por ejemplo.
No sé, no lo veo claro. Python en cambio te obliga a utilzar la indentación como parte de la sintaxis, y escribir código con un estilo más uniforme, menos sofisticado en un momento dado, pero en principio, más legible. Yo aún no he utilzado ni uno ni otro (estoy a la expectativa :-), pero este es uno de los pocos aspectos que en principio me predispone más a favor de Python que de Ruby.
Un saludo
De momento pocos y pequeños
Tienes toda la razón, mi intención desde el principio era contar las cosas buenas como las malas.
Pero la verdad es que hasta ahora más que aspectos negativos lo que me he encontrado son aspectos que me llamaban la atención porque iban en contra de lo que había aprendido en el mundo Java, pero que tras la práctica no sólo me dejaron de parecer negativos sino que pasaron a ser uno de los puntos fuertes. Un ejemplo de esto es la sintaxis relajada como comentaba en la conclusión de este post.
Con respecto a la diferencia con python no estoy muy seguro de que vaya en la línea que comentas. Hace unos años tuve que programar un tiempo en python y aunque no lo llegué a dominar no me parece que tenga una sintaxis más rígida que Ruby. El hecho de que usara la indentación para delimitar bloques de código es algo que me gustó, pero tampoco el sistema de Ruby me parece malo.
Por último, en lo que comentas respecto a la libertad: en algunos aspectos de Ruby si se nota eso, por ejemplo en el hecho de que las clases no estén cerradas y cualquier puede añadirles un método desde cualquier fichero. Estoy esperando a conocerlo un poco mejor para poder definirme a favor o en contra de esta característica. Por lo demás, en lo que a Rails se refiere ocurre justo lo contrario, da mucha menos libertad que una plataforma J2EE porque incluye la funcionalidad de un servidor de aplicaciones J2EE + struts + hibernate + ant + maven y no puedes elegir. Las repercusiones que esto ha tenido en mi caso hasta ahora han sido muy buenas dado que he tardado muy poco en aprenderlo mientras que en Java hay que saber de muchas tecnologías para poder tomar las decisiones correctas con respecto a cuales usar y cuando. Si se podría pensar que quizá en el desarrollo de aplicaciones muy complejas más libertad será necesaria, pero aún no he llegado a ese punto para comprobarlo.
En resumen, hasta ahora Ruby y en particular Rails me han parecido un ejemplo muy bueno del principio ágil "lo más simple que pueda funcionar, pero no más". Cuando me sale la vena ingenieril (esa que nos lleva a hacer sobreingeniería) me preocupo de que quizá más adelante esta simplicidad provocará problemas. Pero la verdad es que hasta ahora ha sido todo lo contrario.