/episodes/267/es.html
HTML | 234 lines | 177 code | 57 blank | 0 comment | 0 complexity | 82727dd8a7af17128b9fc6021c55fb94 MD5 | raw file
- <p>CoffeScript es un lenguaje que genera JavaScript. Viene incluido por defecto con Rails 3.1 por lo que pronto muchos desarrolladores de Rails van a tener que echarle un vistazo por primera vez. Para empezar a aprenderlo en este episodio veremos cómo convertir código JavaScript ya existente a CoffeScript. El código que vamos a convertir es el que escribimos en el episodio 261 [<a href="http://railscasts.com/episodes/261-testing-javascript-with-jasmine">verlo</a>, <a href="http://es.asciicasts.com/episodes/261-tests-de-javascript-con-jasmine">leerlo</a>] que validaba números de tarjeta de crédito.</p>
- <p>Para aquellos que no hayan usado nunca CoffeeScript la <a href="http://jashkenas.github.com/coffee-script/">web oficial</a> es el mejor sitio por donde empezar. Allí encontraremos varios ejemplos de CoffeeScript así como el JavaScript en que se convierten. También hay una página donde podemos escribir código CoffeeScript y verlo convertido en JavaScript que luego podremos ejecutar en el navegador y todo mediante código en el cliente sin realizar llamadas AJAX al servidor.</p>
- <p>El código JavaScript que vamos a convertir es el de la siguiente página, que se activa cuando un usuario sale del campo de número de tarjeta de crédito y que emplea la técnica de calcular el resto de dividir por 10 como validación básica del número introducido, mostrándose un mensaje de error junto al campo de texto si detecta que el número no es correcto.</p>
- <div class="imageWrapper">
- <img src="/system/photos/662/original/E267I01.png" width="808" height="375" alt="Página de validación de tarjeta de crédito."/>
- </div>
- <p>A continuación mostramos el código JavaScript:</p>
- <pre class="javascript">var CreditCard = {
- cleanNumber: function(number) {
- return number.replace(/[- ]/g, "");
- },
-
- validNumber: function(number) {
- var total = 0;
- number = this.cleanNumber(number);
- for (var i=number.length-1; i >= 0; i--) {
- var n = +number[i];
- if ((i+number.length) % 2 == 0) {
- n = n*2 > 9 ? n*2 - 9 : n*2;
- }
- total += n;
- };
- return total % 10 == 0;
- }
- };
- $(function() {
- $("#order_credit_card_number").blur(function() {
- if (CreditCard.validNumber(this.value)) {
- $("#credit_card_number_error").text("");
- } else {
- $("#credit_card_number_error").text("Invalid credit card number.");
- }
- });
- });</pre>
- <p>La versión de Rails de esta aplicación es la 3.1 Release Candidate 1, que se anunció en el momento de escribir este episodio. Podemos actualizarnos a ella con <code>gem install rails --pre</code>.</p>
- <h3>Los primeros cambios</h3>
- <p>Para cambiar un fichero JavaScript a CoffeeScript tan sólo tenemos que ponerle la extensión <code>.coffee</code> al nombre de archivo. Por supuesto podemos seguir usando ficheros JavaScript normales con Rails 3.1 usando la extensión habitual <code>.js</code>, el uso de CoffeeScript es completamente opcional.</p>
- <p>Podemos empezar comentando todo el código JavaScript y cambiarlo paso a paso por su equivalente CoffeeScript. En la primera sección convertiremos la función <code>cleanNumber</code> que limpiaba el número de tarjeta de crédito recibido eliminando los espacios y los guiones que vinieran.</p>
- <p class="codeFilePath">/app/assets/javascripts/orders.js.coffee</p>
- <pre class="javascript">var CreditCard = {
- cleanNumber: function(number) {
- return number.replace(/[- ]/g, "");
- }
- }</pre>
- <p>El código CoffeeScript equivalente es:</p>
- <p class="codeFilePath">/app/assets/javascripts/orders.js.coffee</p>
- <pre class="javascript">CreditCard =
- cleanNumber: (number) ->
- number.replace /[- ]/g, ""</pre>
- <p>Con CoffeeScript nos ahorramos buena parte de la sintaxis de JavaScript. Al usar la tabulación para definir los niveles de bloque nos libramos de todas las llaves pero, eso sí, deberemos ser consistentes con el uso de los espacios en blanco.</p>
- <p>También podemos dejar de utilizar la palabra clave <code>var</code> y al final de una función no tenemos por qué usar <code>return</code>: el último valor evaluado en la función se devuelve automáticamente, igual que en Ruby. No hace falta tampoco usar puntos y comas.</p>
- <p>No hay tampoco que rodear con paréntesis los argumentos en las llamadas a función. La única excepción a esto es cuando se invoca una función sin argumentos, en cuyo caso CoffeeScript necesita los paréntesis para saber que se está invocando una función.</p>
- <p>Por último cambia la forma en la que se invocan las funciones; hay que eliminar la palabra clave <code>function</code> y cambiarla por <code>-></code> tras los argumentos de la función. Cuesta un poco acostumbrarse a esta forma tan concisa de definir funciones.</p>
- <p>Con este fragmento de código ya convertido a CoffeeScript podemos compilarlo para ver qué JavaScript se genera. La salida se parece mucho a nuestro código original:</p>
- <pre class="javascript">var CreditCard;
- CreditCard = {
- cleanNumber: function(number) {
- return number.replace(/[- ]/g, "");
- }
- };</pre>
- <p>Echémosle un vistazo ahora a la función <code>validNumber</code> de nuestro código JavaScript.</p>
- <p class="codeFilePath">/app/assets/javascripts/orders.js.coffee</p>
- <pre class="javascript">validNumber: function(number) {
- var total = 0;
- number = this.cleanNumber(number);
- for (var i=number.length-1; i >= 0; i--) {
- var n = +number[i];
- if ((i+number.length) % 2 == 0) {
- n = n*2 > 9 ? n*2 - 9 : n*2;
- }
- total += n;
- };
- return total % 10 == 0;
- }</pre>
- <p>Sigamos los mismos pasos para convertir este código a CoffeeScript.</p>
- <p class="codeFilePath">/app/assets/javascripts/orders.js.coffee</p>
- <pre class="javascript">validNumber: (number) ->
- total = 0
- number = @cleanNumber(number)
- for i in [(number.length-1)..0]
- n = +number[i]
- if (i+number.length) % 2 == 0
- n = if n*2 > 9 then n*2 - 9 else n*2
- total += n
- total % 10 == 0</pre>
- <p>De nuevo hemos quitado las llaves y los punto y coma así como las palabras clave <code>var</code> y <code>return</code>, y por último hemos cambiado <code>function</code> por <code>-></code>. También hemos hecho otros cambios para limpiar un poco el código.</p>
- <p>Donde aparezca <code>this</code> podemos poner el signo <code>@</code>, de forma que <code>this.cleanNumber</code> se convierte en <code>@number</code>. Podemos también quitar las llaves de la sentencia <code>if</code> porque tampoco hacen falta. El operador ternario también ha cambiado, por lo que podemos cambiar la interrogación y los dos puntos al estilo de C por una sentencia <code>if then else</code>.</p>
- <p>El resto del código tiene buen aspecto; sólo nos queda cambiar el bucle <code>for</code>. En CoffeeScript los iteradores funcionan de forma distinta a JavaScript: podemos iterar sobre un <em>array</em> con código como este:</p>
- <pre class="javascript">for number in [1,2,3]
- alert number</pre>
- <p>Esto genera el siguiente JavaScript:</p>
- <pre class="javascript">var number, _i, _len, _ref;
- _ref = [1, 2, 3];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- number = _ref[_i];
- alert(number);
- }</pre>
- <p>Alternativamente podemos escribir el código de la siguiente manera y se traducirá al mismo JavaScript:</p>
- <pre class="javascript">alert number for number in [1,2,3]</pre>
- <p>Para una secuencia de números podemos usar un rango en lugar de un <em>array</em>:</p>
- <pre class="javascript">for number in [1..3]
- alert number</pre>
- <p>Con esto el JavaScript generado se simplifica:</p>
- <pre class="javascript">var number;
- for (number = 1; number <= 3; number++) {
- alert(number);
- }</pre>
- <p>Si queremos que el bucle cuente hacia atrás tan sólo tenemos que darle la vuelta a los números del rango.</p>
- <pre class="javascript">for number in [3..1]
- alert number</pre>
- <p>Esto se parece mucho a lo que hace el bucle <code>for</code> de nuestro código, así que lo podemos reemplazar por una cuenta regresiva similar.</p>
- <p>Ya sólo nos queda el último fragmento de código JavaScript.</p>
- <p class="codeFilePath">/app/assets/javascripts/orders.js.coffee</p>
- <pre class="javascript">$(function() {
- $("#order_credit_card_number").blur(function() {
- if (CreditCard.validNumber(this.value)) {
- $("#credit_card_number_error").text("");
- } else {
- $("#credit_card_number_error").text("Invalid credit ↵
- card number.");
- }
- });
- });</pre>
- <p>Este fragmento de jQuery adjunto el código de validación al evento <code>blur</code> del campo correspondiente al número de tarjeta de crédito. No hay que hacer nada especial para gestionar el código jQuery en CoffeeScript, el código equivalente es:</p>
- <p class="codeFilePath">/app/assets/javascripts/orders.js.coffee</p>
- <pre class="javascript">jQuery ->
- $("#order_credit_card_number").blur ->
- if CreditCard.validNumber(@value)
- $("#credit_card_number_error").text("")
- else
- $("#credit_card_number_error").text("Invalid credit ↵
- card number.")</pre>
- <p>Igual que antes hemos cambiado las llaves y punto y coma, cambiado <code>function</code> por <code>-></code> y las referencias a <code>this</code> las hemos pasado a <code>@</code>. Queda una última modificación que es cambiar la primera llamada a <code>$</code> por <code>jQuery</code>, que no surte ningún efecto a nivel de funcionalidad pero pone más de manifiesto que estamos usando jQuery.</p>
- <p>Con todo el JavaScript cambiado a CoffeeScript recargamos la página en el navegador para ver si todo funciona igual que antes.</p>
- <div class="imageWrapper">
- <img src="/system/photos/663/original/E267I02.png" width="808" height="375" alt="El código CoffeeScript se comporta igual que el JavaScript que sustituye."/>
- </div>
- <p>Y lo hace. Si introducimos un código incorrecto de tarjeta de crédito veremos el mensaje de error que desaparece tan pronto como introduzcamos un número válido. Al final del fichero JavaScript de nuestra aplicación veremos el código JavaScript que ha sido compilado a partir del fichero CoffeeScript.</p>
- <p class="codeFilePath">http://localhost:3000/assets/application.js</p>
- <pre class="javascript">(function() {
- var CreditCard;
- CreditCard = {
- cleanNumber: function(number) {
- return number.replace(/[- ]/g, "");
- },
- validNumber: function(number) {
- var i, n, total, _ref;
- total = 0;
- number = this.cleanNumber(number);
- for (i = _ref = number.length - 1; _ref <= 0 ? i <= 0 : ↵
- i >= 0; _ref <= 0 ? i++ : i--) {
- n = +number[i];
- if ((i + number.length) % 2 === 0) {
- n = n * 2 > 9 ? n * 2 - 9 : n * 2;
- }
- total += n;
- }
- return total % 10 === 0;
- }
- };
- jQuery(function() {
- return $("#order_credit_card_number").blur(function() {
- if (CreditCard.validNumber(this.value)) {
- return $("#credit_card_number_error").text("");
- } else {
- return $("#credit_card_number_error").text("Invalid ↵
- credit card number.");
- }
- });
- });
- }).call(this);</pre>
- <h3>Depuración</h3>
- <p>¿Qué ocurre si tenemos un error de sintaxis en nuestro código CofeeScript? Si cambiamos el fichero CoffeeScript para que tenga un error y recargamos la página en el naveador no veremos nada, porque la petición de JavaScript va aparte. Sin embargo si vemos en la consola de desarrollo veremos que el error sí aparece.</p>
- <div class="imageWrapper">
- <img src="/system/photos/664/original/E267I03.png" width="814" height="594" alt="El error en CoffeeScript aparece en la consola de desarrollo del navegador."/>
- </div>
- <p>Aparece suficiente información en el mensaje de error como para indicar qué es lo que ha ido mal y qué línea de código debemos mirar para empezar a depurar el código.</p>
- <p>Con esto cerramos este episodio. Todavía nos queda mucho por ver de CofeeScript, se recomienda visitar el sitio oficial de CofeeScript para leer más de este pequeño y divertido lenguaje.</p>