<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss'><id>tag:blogger.com,1999:blog-3499379363161703464</id><updated>2009-11-16T09:19:16.995+01:00</updated><title type='text'>Blog de javiyu</title><subtitle type='html'>Blog de programación y diseño web</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default?start-index=26&amp;max-results=25'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>100</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-6947572343609038088</id><published>2009-11-01T16:46:00.006+01:00</published><updated>2009-11-03T22:26:58.242+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Ejecución asíncrona de javascript con web workers</title><content type='html'>Tal y como anunciaba Google en la &lt;a href="http://code.google.com/events/io/2009/"&gt;Google IO&lt;/a&gt; de este año han estado trabajando junto a la fundación Mozilla en la definición e implementación de los llamados web workers.&lt;br /&gt;&lt;br /&gt;Los web workers permiten carga y ejecución asíncrona de javascript, tienen algunas limitaciones ya que no se puede acceder al DOM entre otras funciones, usa threads a nivel de sistema operativo y usa como método de sincronización paso de mensajes.&lt;br /&gt;&lt;br /&gt;Un ejemplo de sincronización sencillo.&lt;br /&gt;&lt;br /&gt;long_work.js&lt;br /&gt;&lt;pre class="javascript:nocontrols" name="code"&gt;&lt;br /&gt;var j=1;&lt;br /&gt;for(var i=0; i&amp;lt;999999999; i++){j=j*7+2;j=1}&lt;br /&gt;this.postMessage('Done');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;index.html&lt;br /&gt;&lt;pre class="javascript:nocontrols" name="code"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;     var worker = new Worker("/javascripts/long_work.js");&lt;br /&gt;     worker.onmessage = function(event){&lt;br /&gt;       alert(event.data);&lt;br /&gt;     };&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="https://developer.mozilla.org/En/Using_web_workers"&gt;Ejemplo&lt;/a&gt; de uso en la web de la fundación Mozilla.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-6947572343609038088?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/6947572343609038088/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=6947572343609038088&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/6947572343609038088'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/6947572343609038088'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/11/ejecucion-asincrona-de-javascript-con.html' title='Ejecución asíncrona de javascript con web workers'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-2556226747489023050</id><published>2009-08-15T22:50:00.002+02:00</published><updated>2009-08-15T22:59:44.015+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><category scheme='http://www.blogger.com/atom/ns#' term='symbian'/><title type='text'>Como hacer copias de seguridad de tu agenda en symbian</title><content type='html'>Ya que tenemos acceso al API de symbian desde python (gracias al proyecto &lt;a href="http://sourceforge.net/projects/pys60/"&gt;pys60&lt;/a&gt;) podemos aprovecharnos para hacer algunas cosas útiles con unos scripts simples en python.&lt;br /&gt;&lt;br /&gt;Por ejemplo, para hacer una copia de seguridad de la agenda serviría el siguiente script.&lt;br /&gt;&lt;pre class="python:nocontrols" name="code"&gt;&lt;br /&gt;from contacts import ContactsDb&lt;br /&gt;&lt;br /&gt;contacts = ContactsDb().values()&lt;br /&gt;&lt;br /&gt;filename = 'e:\\agenda.txt'&lt;br /&gt;f = open(filename, "w")&lt;br /&gt;&lt;br /&gt;for c in contacts:&lt;br /&gt; f.write(c.as_vcard())&lt;br /&gt;&lt;br /&gt;f.close()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Este programa exporta a un fichero toda la agenda en formato &lt;a href="http://en.wikipedia.org/wiki/VCard"&gt;VCard&lt;/a&gt; para posteriormente transferirlo a otro dispositivo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-2556226747489023050?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/2556226747489023050/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=2556226747489023050&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/2556226747489023050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/2556226747489023050'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/08/como-hacer-copias-de-seguridad-de-tu.html' title='Como hacer copias de seguridad de tu agenda en symbian'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-1690351813704892276</id><published>2009-07-18T16:54:00.003+02:00</published><updated>2009-07-18T17:05:36.750+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Conectar una aplicación rails a varias bases de datos simultáneamente</title><content type='html'>Se puede conectar una aplicación rails a varias bases de datos a la vez, eso sí, hay que tener un especial cuidado con el rendimiento de la aplicación, aunque ActiveRecord nos provee una interfaz transparente a la hora de acceder a los modelos.&lt;br /&gt;&lt;br /&gt;Suponiendo como ejemplo que tenemos dos modelos (&lt;span style="font-style:italic;"&gt;Post&lt;/span&gt; y &lt;span style="font-style:italic;"&gt;Comment&lt;/span&gt;) y este último está localizado en una base de datos externa, se puede configurar rails de la siguiente forma:&lt;br /&gt;&lt;br /&gt;1) Primero, creamos un fichero de configuración para la conexión a la nueva base de datos (&lt;span style="font-style:italic;"&gt;config/external_database.yml&lt;/span&gt;).&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;development:&lt;br /&gt;    adapter: mysql&lt;br /&gt;    database: test&lt;br /&gt;    username: root&lt;br /&gt;    password:&lt;br /&gt;&lt;br /&gt;production:&lt;br /&gt;    adapter: mysql&lt;br /&gt;    database: test&lt;br /&gt;    username: root&lt;br /&gt;    password:&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;2) Después, creamos un módulo para encapsular la lectura del fichero y la nueva conexión.&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;module ExternalDatabase&lt;br /&gt;  def self.included(base)&lt;br /&gt;    config = YAML.load(File.open('config/external_database.yml'))    &lt;br /&gt;    &lt;br /&gt;    if config and ENV['RAILS_ENV']&lt;br /&gt;      base.establish_connection(config[ENV['RAILS_ENV']])&lt;br /&gt;    else&lt;br /&gt;      raise 'External Database not configured'&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;3) Por último, incluimos en los modelos que van a la base de datos externa el módulo que hemos creado.&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;class Comment &lt; ActiveRecord::Base&lt;br /&gt;  include ExternalDatabase&lt;br /&gt;  &lt;br /&gt;  belongs_to :post&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Con esto ya podemos utilizar los modelos aunque cada uno esté en una base de datos diferente.&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;Comment.find(:all, :include =&gt; :post)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-1690351813704892276?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/1690351813704892276/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=1690351813704892276&amp;isPopup=true' title='3 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/1690351813704892276'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/1690351813704892276'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/07/conectar-una-aplicacion-rails-varias.html' title='Conectar una aplicación rails a varias bases de datos simultáneamente'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-1927731191029545735</id><published>2009-07-11T15:55:00.002+02:00</published><updated>2009-07-11T16:06:29.358+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Diferencias de rendimiento entre eval y send</title><content type='html'>La metaprogramación en ruby permite mucha expresividad y flexibilidad, aún así hace caer bastante el rendimiento.&lt;br /&gt;&lt;br /&gt;A pesar de eso, lo más importante es evitar en la medida de lo posible &lt;span style="font-style:italic;"&gt;eval&lt;/span&gt; ya que es bastante más lento.&lt;br /&gt;&lt;br /&gt;Al hacer una llamada a &lt;span style="font-style:italic;"&gt;send&lt;/span&gt; la máquina virtual de ruby tiene que buscar el nombre del método que se le pasa como parámetro y ejecutarlo.&lt;br /&gt;&lt;br /&gt;En cambio, al hacer un &lt;span style="font-style:italic;"&gt;eval&lt;/span&gt; además de lo anterior la máquina virtual necesita parsear la cadena y después ejecutar el código.&lt;br /&gt;&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;Benchmark.bm{|x| x.report{100000.times{eval('Post.scopes')}}}&lt;br /&gt;    user     system      total        real&lt;br /&gt;0.410000   0.000000   0.410000 (  0.410306)&lt;br /&gt;&lt;br /&gt;Benchmark.bm{|x| x.report{100000.times{Post.send('scopes')}}}&lt;br /&gt;    user     system      total        real&lt;br /&gt;0.140000   0.000000   0.140000 (  0.144820)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-1927731191029545735?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/1927731191029545735/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=1927731191029545735&amp;isPopup=true' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/1927731191029545735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/1927731191029545735'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/07/diferencias-de-rendimiento-entre-eval-y.html' title='Diferencias de rendimiento entre eval y send'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-3092663322700887521</id><published>2009-06-26T18:39:00.005+02:00</published><updated>2009-06-26T19:03:41.208+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Optimización de consultas en rails</title><content type='html'>Cuando en rails se hace una consulta a la base de datos sobre una tabla que contiene mucha información, no todo el tiempo se pierde en la consulta. Una parte importante del tiempo se pierde en la creación de cada objeto ActiveRecord.&lt;br /&gt;&lt;br /&gt;Usando directamente el método &lt;span style="font-style:italic;"&gt;select_all&lt;/span&gt; de la conexión ActiveRecord devolverá un array de hash, ahorrando el tiempo de construcción de objetos.&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;Benchmark.bm{|x| x.report{100.times{BigTable.find(:all)}}}&lt;br /&gt;    user     system      total        real&lt;br /&gt;10.090000   2.090000  12.180000 ( 19.199150)&lt;br /&gt;&lt;br /&gt;Benchmark.bmbm{|x| x.report{100.times{BigTable.connection.select_all('select * from big_table')}}}&lt;br /&gt;    user     system      total        real&lt;br /&gt;7.350000   2.080000   9.430000 ( 16.646406)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;En general, no es buena idea guardar todos los elementos de una tabla grande en un array, pero aún seleccionando tan solo el índice se obtiene una mejora sustancial. &lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;Benchmark.bm{|x| x.report{100.times{BigTable.find(:all, :select =&gt; :id)}}}&lt;br /&gt;    user     system      total        real&lt;br /&gt;0.610000   0.010000   0.620000 (  0.707702)&lt;br /&gt;&lt;br /&gt;Benchmark.bmbm{|x| x.report{100.times{BigTable.connection.select_all('select id from big_table')}}}&lt;br /&gt;    user     system      total        real&lt;br /&gt;0.080000   0.000000   0.080000 (  0.161878)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-3092663322700887521?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/3092663322700887521/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=3092663322700887521&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3092663322700887521'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3092663322700887521'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/06/optimizacion-de-consultas-en-rails.html' title='Optimización de consultas en rails'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-3620678915219738821</id><published>2009-06-20T23:19:00.006+02:00</published><updated>2009-06-20T23:39:04.415+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><category scheme='http://www.blogger.com/atom/ns#' term='c'/><title type='text'>Crear extensiones de ruby en c</title><content type='html'>Crear extensiones en C del lenguaje ruby es bastante sencillo.&lt;br /&gt;&lt;br /&gt;En primer lugar hay que crear un fichero para extconf, este nos generará el fichero makefile de forma automática.&lt;br /&gt;&lt;br /&gt;En el fichero extconf.rb debería haber algo como:&lt;br /&gt;&lt;pre name="code" class="c:nocontrols"&gt;&lt;br /&gt;#!/usr/bin/env ruby&lt;br /&gt;&lt;br /&gt;require 'mkmf'&lt;br /&gt;create_makefile("ruby1")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Donde &lt;span style="font-style:italic;"&gt;ruby1&lt;/span&gt; es el nombre de la extensión.&lt;br /&gt;&lt;br /&gt;En segundo lugar hay que crear la extensión en código C, simplemente hay que incluir la cabecera &lt;span style="font-style:italic;"&gt;ruby.h&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;La función Init_ruby1 es la que se ejecutará al importar la extensión desde código ruby.&lt;br /&gt;&lt;pre name="code" class="c:nocontrols"&gt;&lt;br /&gt;#include "ruby.h"&lt;br /&gt;&lt;br /&gt;static VALUE c_printf(VALUE self, VALUE anObject){&lt;br /&gt; printf("%s\n", STR2CSTR(anObject));&lt;br /&gt; return Qnil;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static VALUE c_init(VALUE self){&lt;br /&gt;  return self;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;VALUE FromC;&lt;br /&gt;&lt;br /&gt;void Init_ruby1() {&lt;br /&gt; rb_define_global_function("printfc", c_printf, 1);&lt;br /&gt; FromC = rb_define_class("FromC", rb_cObject);&lt;br /&gt; rb_define_method(FromC, "initialize", c_init, 0);&lt;br /&gt; rb_define_method(FromC, "printfc", c_printf, 1);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;En este ejemplo se define una función global &lt;span style="font-style:italic;"&gt;printfc&lt;/span&gt;, se define la clase &lt;span style="font-style:italic;"&gt;FromC&lt;/span&gt;, el método de instancia &lt;span style="font-style:italic;"&gt;printfc&lt;/span&gt; de esa clase.&lt;br /&gt;&lt;br /&gt;Para compilar e instalar este código hay que ejecutar &lt;span style="font-style:italic;"&gt;ruby extconf.rb&lt;/span&gt;, &lt;span style="font-style:italic;"&gt;make &lt;/span&gt;y &lt;span style="font-style:italic;"&gt;make install&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Ya deberíamos tener disponible la librería &lt;span style="font-style:italic;"&gt;ruby1&lt;/span&gt;, al ejecutar &lt;span style="font-style:italic;"&gt;require 'ruby1'&lt;/span&gt; podremos ejecutar las funciones de nuestro código en C.&lt;br /&gt;&lt;br /&gt;Hay una gran cantidad de funciones disponibles para crear extensiones en la siguiente dirección: &lt;a href="http://www.rubycentral.com/book/ext_ruby.html"&gt;http://www.rubycentral.com/book/ext_ruby.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-3620678915219738821?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/3620678915219738821/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=3620678915219738821&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3620678915219738821'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3620678915219738821'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/06/crear-extensiones-de-ruby-en-c.html' title='Crear extensiones de ruby en c'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-3809434356642226237</id><published>2009-06-03T23:42:00.005+02:00</published><updated>2009-06-04T00:01:39.100+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Definición de named_scopes dependientes del tiempo actual</title><content type='html'>Si tenemos una clase ActiveRecord como la siguiente:&lt;br /&gt;&lt;pre name="code" class="ruby:nocontrols"&gt;&lt;br /&gt;class Post &lt; ActiveRecord::Base&lt;br /&gt;  named_scope :test, :conditions =&gt; ['updated_at &lt; ?', Time.now]&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;En modo development funciona sin problemas, sin embargo en modo production la consulta que se genera no es la esperada.&lt;br /&gt;&lt;br /&gt;Al arrancar rails en modo production se cachean las clases, al cargar este modelo en concreto se evalúa &lt;span style="font-style:italic;"&gt;Time.now&lt;/span&gt; haciendo que para todas las consultas se coja como fecha la hora en la que se ha cargado el modelo.&lt;br /&gt;&lt;br /&gt;El descrito anteriormente no es el comportamiento que esperaríamos al definir el named_scope, sino que debería obtener la fecha y hora actual para cada consulta, no en la definición.&lt;br /&gt;&lt;br /&gt;Revisando la definición de los named_scopes nos encontramos lo siguiente:&lt;br /&gt;&lt;pre name="code" class="ruby:nocontrols"&gt;&lt;br /&gt;      def named_scope(name, options = {}, &amp;block)&lt;br /&gt;        name = name.to_sym&lt;br /&gt;        scopes[name] = lambda do |parent_scope, *args|&lt;br /&gt;          Scope.new(parent_scope, case options&lt;br /&gt;            when Hash&lt;br /&gt;              options&lt;br /&gt;            when Proc&lt;br /&gt;              options.call(*args)&lt;br /&gt;          end, &amp;block)&lt;br /&gt;        end&lt;br /&gt;        (class &lt;&lt; self; self end).instance_eval do&lt;br /&gt;          define_method name do |*args|&lt;br /&gt;            scopes[name].call(self, *args)&lt;br /&gt;          end&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;En nuestro caso pasamos un hash como opciones, por lo que efectivamente se evaluará el &lt;span style="font-style:italic;"&gt;Time.now&lt;/span&gt; solo en la definición de dicho hash.&lt;br /&gt;&lt;br /&gt;Si en lugar de eso definimos un objeto de tipo &lt;span style="font-style:italic;"&gt;Proc&lt;/span&gt; se evaluará cada vez.&lt;br /&gt;&lt;br /&gt;Por lo tanto para arreglar el comportamiento del primer ejemplo deberíamos escribirlo como:&lt;br /&gt;&lt;pre name="code" class="ruby:nocontrols"&gt;&lt;br /&gt;class Post &lt; ActiveRecord::Base&lt;br /&gt;  named_scope :test, lambda{{:conditions =&gt; ['updated_at &lt; ?', Time.now]}}&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-3809434356642226237?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/3809434356642226237/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=3809434356642226237&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3809434356642226237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3809434356642226237'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/06/si-tenemos-una-clase-activerecord-como.html' title='Definición de named_scopes dependientes del tiempo actual'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-9076827714660331168</id><published>2009-05-10T23:20:00.004+02:00</published><updated>2009-05-10T23:42:59.260+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='proyectos'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Aplicación de consola para twitter</title><content type='html'>Acabo de lanzar un proyecto en github para interaccionar con twitter desde consola.&lt;br /&gt;&lt;br /&gt;Por ahora incluye la funcionalidad básica: cambio de estado, consulta de estado de amigos, login y mensajería.&lt;br /&gt;&lt;br /&gt;La interfaz de comandos es muy similar al clásico IRC.&lt;br /&gt;&lt;br /&gt;La url del proyecto es la siguiente: &lt;a href="http://github.com/javiyu/twit/tree/master"&gt;twit&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-9076827714660331168?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/9076827714660331168/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=9076827714660331168&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/9076827714660331168'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/9076827714660331168'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/05/aplicacion-de-consola-para-twitter.html' title='Aplicación de consola para twitter'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-3958758805931905466</id><published>2009-05-01T13:32:00.003+02:00</published><updated>2009-05-01T13:46:33.925+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Añadiendo estado a los elementos del DOM</title><content type='html'>En aplicaciones complejas con javascript para guardar el estado de cada elemento se suele modificar alguna propiedad del elemento que queramos manipular.&lt;br /&gt;&lt;br /&gt;Sin embargo esta práctica es poco adecuada y no demasiado flexible, ya que no podemos guardar tipos de datos complejos.&lt;br /&gt;&lt;br /&gt;jQuery ya prevee que podemos necesitar esta funcionalidad y nos proporciona la función data para guardar información de cada elemento.&lt;br /&gt;&lt;br /&gt;Por ejemplo:&lt;br /&gt;&lt;pre class="javascript:nocontrols" name="code"&gt;&lt;br /&gt;$('a').data('activated_links', false);&lt;br /&gt;....&lt;br /&gt;var footer_links = $('.footer_links');&lt;br /&gt;if(!footer_links.data('activated_links')){&lt;br /&gt;  footer_links.data('activated_links', true);&lt;br /&gt;  ...&lt;br /&gt;}&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-3958758805931905466?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/3958758805931905466/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=3958758805931905466&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3958758805931905466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3958758805931905466'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/05/anadiendo-estado-los-elementos-del-dom.html' title='Añadiendo estado a los elementos del DOM'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-4025227692892532891</id><published>2009-03-28T12:30:00.002+01:00</published><updated>2009-03-28T12:48:33.760+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Combinación de named_scopes en rails</title><content type='html'>Cuando hay que realizar un filtrado de resultados en una web con muchas posibles combinaciones podemos acabar con muchas líneas de código para obtener una funcionalidad no demasiado compleja.&lt;br /&gt;&lt;br /&gt;En rails, la mayoría de los filtrados se hacen con named_scopes, queda parcialmente resuelto el problema, aún así quedan resolver como aplicar cada combinación de named_scopes según el filtrado que necesite el usuario.&lt;br /&gt;&lt;br /&gt;Suponiendo que tenemos un modelo Post como el siguiente:&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;class Post &lt; ActiveRecord::Base&lt;br /&gt;  named_scope :all&lt;br /&gt;  named_scope :published, :conditions =&gt; {:published =&gt; true}&lt;br /&gt;  named_scope :from_user, :conditions =&gt; {:kind =&gt; 'user'}&lt;br /&gt;  named_scope :from_admin, :conditions =&gt; {:kind =&gt; 'admin'}&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Podemos resolver el problema de aplicar filtros combinados de la siguiente forma usando &lt;span style="font-style:italic;"&gt;inject&lt;/span&gt;:&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;class PostsController &lt; ApplicationController&lt;br /&gt;  def index&lt;br /&gt;    filter = (params[:filter] or String.new)&lt;br /&gt;    filter = filter.split(',').map(&amp;:to_sym).select{|f| Post.scopes.include?(f)}&lt;br /&gt;    filter &lt;&lt; :all&lt;br /&gt;&lt;br /&gt;    @posts = filter.inject(Post){|model, nscope| model.send(nscope)}&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;En primer lugar obtenemos la lista de named_scopes a aplicar y filtramos los no válidos, en segundo con &lt;span style="font-style:italic;"&gt;inject&lt;/span&gt; se los aplicamos al modelo.&lt;br /&gt;&lt;br /&gt;Con ese código tan simple podemos usar rutas del tipo:&lt;br /&gt;&lt;br /&gt;http://server/posts&lt;br /&gt;http://server/posts?filter=published&lt;br /&gt;http://server/posts?filter=published,from_user&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-4025227692892532891?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/4025227692892532891/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=4025227692892532891&amp;isPopup=true' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/4025227692892532891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/4025227692892532891'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/03/combinacion-de-namedscopes-en-rails.html' title='Combinación de named_scopes en rails'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-892146013650285857</id><published>2009-03-01T14:58:00.003+01:00</published><updated>2009-03-01T15:23:47.497+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Optimización del tiempo de carga de webs</title><content type='html'>Dentro del tiempo total desde que se hace una petición a una página web hasta que se termina de dibujar en el navegador, gran parte corresponde a los distintos recursos externos que hay que recuperar: javascript, css e imágenes.&lt;br /&gt;&lt;br /&gt;Los ficheros javascript y css tienen una propiedad interesante, si se concatenan varios y se envían en un solo fichero no debe influir en el resultado final. &lt;br /&gt;Sin embargo, si se concatenan, se pueden cargar con una sola petición, con el consiguiente aumento de rendimiento.&lt;br /&gt;&lt;br /&gt;El método que rails proporciona para hacer esto de forma automática es el siguiente (solo en  modo producción):&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;&amp;lt;%= javascript_include_tag 'first.js', 'second.js', :cache =&gt; '1_2' %&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Sin embargo esto tiene un problema, normalmente en una aplicación según la página se carga unos ficheros u otros, no es muy práctico ir dándole claves a todas las combinaciones.&lt;br /&gt;&lt;br /&gt;Se pueden programar dos helpers para dar las claves de forma automática:&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;&amp;lt;%= cached_javascript_include_tag 'first.js', 'second.js' %&amp;gt;&lt;br /&gt;&amp;lt;%= cached_stylesheet_link_tag 'first.css', 'second.css' %&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Es importante que las versiones cacheadas tanto de css como de javascript no estén en el svn, a la hora de hacer un despliegue se regenerarán usando la nueva versión de los ficheros.&lt;br /&gt;&lt;br /&gt;Aún así, si se quiere que se genere de nuevo la versión cacheada para una página sin desplegar la aplicación ni borrar los ficheros antiguos, se puede usar un sistema de versiones.&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;&amp;lt;%= cached_javascript_include_tag 'first.js?v=1', 'second.js?v=3' %&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El código de los helpers es el siguiente:&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;module ApplicationHelper&lt;br /&gt;  def cached_javascript_include_tag(*args)&lt;br /&gt;    cached_args!(*args)&lt;br /&gt;    javascript_include_tag(*args)&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def cached_stylesheet_link_tag(*args)&lt;br /&gt;    cached_args!(*args)&lt;br /&gt;    stylesheet_link_tag(*args)&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def cached_args!(*args)&lt;br /&gt;    options = args.extract_options!.stringify_keys&lt;br /&gt;&lt;br /&gt;    cache_entry = args.map(&amp;:to_s).sort.join('_').gsub!('?', '_')&lt;br /&gt;    options[:cache] = cache_entry&lt;br /&gt;&lt;br /&gt;    args &amp;lt;&amp;lt; options&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-892146013650285857?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/892146013650285857/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=892146013650285857&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/892146013650285857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/892146013650285857'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/03/optimizacion-del-tiempo-de-carga-de.html' title='Optimización del tiempo de carga de webs'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-2782165044622791157</id><published>2009-01-11T11:43:00.002+01:00</published><updated>2009-01-11T11:52:45.762+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Observadores personalizables en rails</title><content type='html'>En rails hay unas clases llamadas observadores que sirven para redefinir los métodos que necesitemos en el ciclo de vida de los objetos (&lt;span style="font-style:italic;"&gt;after&lt;/span&gt;_update, before_&lt;span style="font-style:italic;"&gt;create&lt;/span&gt;, etc...).&lt;br /&gt;&lt;br /&gt;El problema es que en ocasiones con los métodos del ciclo de vida no es suficiente, una situación muy común es redefinir el método after_&lt;span style="font-style:italic;"&gt;save&lt;/span&gt;, y cada vez que se guarde un objeto comprobar una condición y actuar si es necesario.&lt;br /&gt;&lt;br /&gt;Afortunadamente existe un método en ActiveRecord para notificar a los observadores exactamente cuando queramos y de lo que queramos, de la siguiente forma.&lt;br /&gt;&lt;br /&gt;En el modelo:&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;class Post &lt; ActiveRecord::Base&lt;br /&gt; def just_created&lt;br /&gt;  notify :manage_task&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;En el observador:&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;class GlobalObserver &lt; ActiveRecord::Observer&lt;br /&gt; observe Post&lt;br /&gt; &lt;br /&gt; def manage_task(object)&lt;br /&gt;  #do something&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-2782165044622791157?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/2782165044622791157/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=2782165044622791157&amp;isPopup=true' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/2782165044622791157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/2782165044622791157'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/01/observadores-personalizables-en-rails.html' title='Observadores personalizables en rails'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-1601596932754077655</id><published>2009-01-02T18:33:00.002+01:00</published><updated>2009-01-02T18:39:56.300+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Carga de css dinámica</title><content type='html'>Es habitual en webs en las que cada usuario tiene una página personal permitirle cambiar la apariencia mediante temas (skins).&lt;br /&gt;&lt;br /&gt;Para cargar un css nuevo de forma dinámica se puede usar la siguiente función javascript o algún variante.&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;function loadCSS(cssFile){&lt;br /&gt; var cssLink=document.createElement("link");&lt;br /&gt; cssLink.setAttribute("rel", "stylesheet");&lt;br /&gt; cssLink.setAttribute("type", "text/css");&lt;br /&gt; cssLink.setAttribute("href", cssFile);&lt;br /&gt; document.getElementsByTagName("head")[0].appendChild(cssLink);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lo que hace esta función es crear un elemento link que apunta a un css y lo inserta en el head de la página.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-1601596932754077655?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/1601596932754077655/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=1601596932754077655&amp;isPopup=true' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/1601596932754077655'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/1601596932754077655'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2009/01/carga-de-css-dinmica.html' title='Carga de css dinámica'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-4614402475376996117</id><published>2008-12-28T00:03:00.003+01:00</published><updated>2009-07-15T11:21:33.027+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Crear demonios en ruby</title><content type='html'>Para crear un demonio hay que duplicar el proceso actual (hacer un fork) y distinguir a cada una de las instancias para la que instancia hija se encargue del trabajo en background que queremos realizar.&lt;br /&gt;&lt;br /&gt;En ruby todo este proceso se puede realizar automáticamente utilizando el módulo ruby daemon de la siguiente forma.&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;require 'daemons'&lt;br /&gt;&lt;br /&gt;class Demonio&lt;br /&gt; def initialize&lt;br /&gt;  Daemons.daemonize&lt;br /&gt;  loop do&lt;br /&gt;   f = File.new('/tmp/timestamp', 'a')&lt;br /&gt;   f.write("#{Time.now}\n")&lt;br /&gt;   f.close&lt;br /&gt;   sleep(3)&lt;br /&gt;  end&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;Demonio.new&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-4614402475376996117?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/4614402475376996117/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=4614402475376996117&amp;isPopup=true' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/4614402475376996117'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/4614402475376996117'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/12/crear-demonios-en-ruby.html' title='Crear demonios en ruby'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-4003535662469725820</id><published>2008-12-26T23:44:00.004+01:00</published><updated>2008-12-27T00:02:29.580+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Redefinir el método de comparación con expresiones regulares</title><content type='html'>Para mejorar la búsqueda en los datos de los objetos se puede aprovechar que en ruby se puede redefinir el método de comparación con expresiones regulares (=~).&lt;br /&gt;&lt;br /&gt;Un ejemplo.&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;class Persona&lt;br /&gt; attr_accessor :nombre, :apellidos, :direccion&lt;br /&gt; &lt;br /&gt; def initialize(nombre, apellidos, direccion)&lt;br /&gt;  self.nombre = nombre&lt;br /&gt;  self.apellidos = apellidos&lt;br /&gt;  self.direccion = direccion&lt;br /&gt; end &lt;br /&gt; &lt;br /&gt; def =~(re)&lt;br /&gt;  "#{self.nombre} #{self.apellidos} #{self.direccion}" =~ re&lt;br /&gt; end&lt;br /&gt; &lt;br /&gt; def self.search(ciudad, input)&lt;br /&gt;  query = Regexp.new(input, 'i')&lt;br /&gt;  ciudad.select{|p| p =~ query} &lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;p1 = Persona.new('Jose', 'Gonzalez', 'Sol')&lt;br /&gt;p2 = Persona.new('Francisco', 'Martinez', 'Preciados')&lt;br /&gt;p3 = Persona.new('Fernando', 'Solano', 'Serrano')&lt;br /&gt;&lt;br /&gt;Madrid = [p1, p2, p3]&lt;br /&gt;&lt;br /&gt;puts Persona.search(Madrid, 'sol').map(&amp;:nombre)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El modificador &lt;span style="font-style:italic;"&gt;'i'&lt;/span&gt; del constructor de expresiones regulares hace que no distinga entre mayúsculas y minúsculas.&lt;br /&gt;Concatenando los campos en los que queremos buscar se obtiene un &lt;span style="font-style:italic;"&gt;matching&lt;/span&gt; más completo.&lt;br /&gt;&lt;br /&gt;Con el &lt;span style="font-style:italic;"&gt;map&lt;/span&gt; final se imprimen los nombres de las personas que hayan coincidido con la búsqueda.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-4003535662469725820?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/4003535662469725820/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=4003535662469725820&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/4003535662469725820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/4003535662469725820'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/12/redefinir-el-mtodo-de-comparacin-con.html' title='Redefinir el método de comparación con expresiones regulares'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-3561711464856743611</id><published>2008-12-14T13:09:00.004+01:00</published><updated>2008-12-14T13:31:11.886+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Recuperación automática de procesos erlang</title><content type='html'>La mayor ventaja que proporciona erlang es la gran potencia que tiene para la programación de procesos concurrentes.&lt;br /&gt;&lt;br /&gt;Cuando se diseña software que va a ser programado en erlang suele dividirse el trabajo en múltiples procesos, además provee de mecanismos para comprobar fácilmente entre procesos si alguno se ha colgado y restaurarlo o ejecutar cualquier otra acción.&lt;br /&gt;&lt;br /&gt;En el siguiente ejemplo.&lt;br /&gt;&lt;pre class="ruby:nocontrols" name="code"&gt;&lt;br /&gt;-module(lib_misc).&lt;br /&gt;-export([link_p/1, start/0, loop/0]).&lt;br /&gt;&lt;br /&gt;start() -&amp;gt; &lt;br /&gt; Pid = spawn(fun loop/0),&lt;br /&gt; register(divisor, Pid),&lt;br /&gt; spawn(lib_misc, link_p, [Pid]),&lt;br /&gt; Pid.&lt;br /&gt;&lt;br /&gt;loop() -&amp;gt;&lt;br /&gt; receive&lt;br /&gt;  {A, B} -&amp;gt;&lt;br /&gt;   io:format("~nDivision: ~p", [A/B]),&lt;br /&gt;   loop()&lt;br /&gt; end.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;link_p(Pid) -&amp;gt;&lt;br /&gt; process_flag(trap_exit, true),&lt;br /&gt; link(Pid),&lt;br /&gt; receive&lt;br /&gt;  {'EXIT', Pid, _} -&amp;gt; &lt;br /&gt;   NewPid = spawn_link(lib_misc, loop, []),&lt;br /&gt;   register(divisor, NewPid),&lt;br /&gt;   link_p(NewPid)&lt;br /&gt; end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Al compilar la librería y arrancarla se ejecutan dos procesos, el principal &lt;span style="font-style:italic;"&gt;loop&lt;/span&gt; y otro llamado &lt;span style="font-style:italic;"&gt;link_p&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;El bucle principal simplemente recibe una tupla con dos números y los divide. Tiene al menos un error conocido y es que no comprueba cuando el divisor es cero.&lt;br /&gt;&lt;br /&gt;El proceso &lt;span style="font-style:italic;"&gt;link_p&lt;/span&gt; se enlaza al principal, la tarea de este proceso es esperar a que el principal se cuelgue (por ejemplo, división entre cero).&lt;br /&gt;En el momento en el que el programa principal se ha colgado, recibe el mensaje de salida, restaura el proceso y se enlaza al nuevo.&lt;br /&gt;&lt;br /&gt;Hay un detalle más, al iniciar un nuevo proceso el pid del antiguo no nos sirve para mandarle mensajes, así que se registra el átomo &lt;span style="font-style:italic;"&gt;divisor&lt;/span&gt; para tener siempre una referencia global.&lt;br /&gt;&lt;br /&gt;Registrar un nombre global estático no es la mejor solución, ya que no se pueden iniciar múltiples instancias del proceso divisor, debería ser un parámetro, lo he dejado así por que el ejemplo fuera más sencillo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-3561711464856743611?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/3561711464856743611/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=3561711464856743611&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3561711464856743611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3561711464856743611'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/12/recuperacin-automtica-de-procesos.html' title='Recuperación automática de procesos erlang'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-9009091976061616304</id><published>2008-12-08T11:24:00.002+01:00</published><updated>2008-12-08T11:37:03.795+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Funciones anónimas recursivas en Erlang</title><content type='html'>En erlang se pueden definir funciones anónimas con la palabra reservada &lt;span style="font-style:italic;"&gt;fun&lt;/span&gt;, aunque se pueden guardar en una variable para referenciarlas posteriormente.&lt;br /&gt;&lt;br /&gt;La sintaxis es la siguiente:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;IsZero = fun(0) -&gt; 1; (N) -&gt; 0 end.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Esta función devuelve 1 (verdad) si el parámetro es 0, falso en cualquier otro caso.&lt;br /&gt;&lt;br /&gt;El problema es que de esta forma no se pueden definir funciones recursivas, ya que al no estar definida la función dentro de su definición, no podemos referenciarla, para solucionarlo se puede pasar un parámetro extra, que sea la propia función.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Factorial = fun(1, F) -&gt; 1; (N, F) -&gt; N * F(N-1, F) end.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En este último caso habría que llamar a la función como &lt;span style="font-style:italic;"&gt;Factorial(número, Factorial)&lt;/span&gt;, lo que queda poco intuitivo, se puede mejorar creando dos funciones en lugar de una.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Fact = fun(1, F) -&gt; 1; (N, F) -&gt; N * F(N-1, F) end.&lt;br /&gt;Factorial = fun(N) -&gt; Fact(N, Fact) end.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-9009091976061616304?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/9009091976061616304/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=9009091976061616304&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/9009091976061616304'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/9009091976061616304'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/12/funciones-annimas-recursivas-en-erlang.html' title='Funciones anónimas recursivas en Erlang'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-3373393185136560940</id><published>2008-11-30T19:03:00.002+01:00</published><updated>2008-11-30T19:28:09.801+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Terminales virtuales en linux</title><content type='html'>Regularmente cuando administramos equipos unix necesitamos varios terminales, por ejemplo, en el caso de estar administrando un equipo linux remoto por ssh.&lt;br /&gt;&lt;br /&gt;Para poder tener varios terminales podemos loguearnos varias veces y usar cada conexión como un terminal, sin embargo esto está lejos de ser óptimo, una sola conexión es más que suficiente para tener abiertas varios terminales.&lt;br /&gt;&lt;br /&gt;El comando &lt;span style="font-style:italic;"&gt;screen&lt;/span&gt; de linux permite crear terminales virtuales, por lo que este problema lo tendríamos resuelto, su funcionamiento es muy simple.&lt;br /&gt;&lt;br /&gt;Ejecutando:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;sreen bash&lt;/span&gt;, abrimos una instancia nueva de bash en otro terminal.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Ctrl+A "&lt;/span&gt;, muestra una lista de los terminales virtuales.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Ctr+A 2&lt;/span&gt;, va a la tercera consola virtual (numeradas desde 0).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Ctrl+A Ctrl+A&lt;/span&gt;, va a la consola mostrada anteriormente.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Ctrl+A d&lt;/span&gt;, deja ejecutando las tareas en background (detach).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-3373393185136560940?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/3373393185136560940/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=3373393185136560940&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3373393185136560940'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3373393185136560940'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/11/terminales-virtuales-en-linux.html' title='Terminales virtuales en linux'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-2193556984653707423</id><published>2008-11-30T12:49:00.005+01:00</published><updated>2008-11-30T13:07:18.602+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Expiration time en fragment caching</title><content type='html'>Cuando se usa el sistema de caché de rails se pueden elegir donde guardar los items cacheados, la mejor opción es la mayoría de los casos es memcached, pero no siempre tenemos posibilidad de instalar un demonio en la máquina que da hosting.&lt;br /&gt;&lt;br /&gt;Otra posibilidad es guardar los items en el sistema de ficheros, para ello hay que añadir en el environment.rb lo siguiente.&lt;br /&gt;&lt;pre name="code" class="ruby:nocontrols"&gt;&lt;br /&gt;config.cache_store = :file_store, '/path_to_cache'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El problema que tiene usar el sistema de ficheros como soporte para la cache es que no soporta la opción de expiración por tiempo, sin embargo memcached sí que lo hace.&lt;br /&gt;&lt;br /&gt;Se puede solucionar añadiendo el siguiente parche al environment.rb&lt;br /&gt;&lt;pre name="code" class="ruby:nocontrols"&gt;&lt;br /&gt;module ActiveSupport&lt;br /&gt;  module Cache&lt;br /&gt;    class FileStore &amp;lt; Store&lt;br /&gt;      def read(name, options = nil)&lt;br /&gt;        super&lt;br /&gt;        timestamp = File.open("#{real_file_path(name)}.timestamp", 'rb') { |f| f.read } rescue nil&lt;br /&gt;        if !timestamp or (timestamp and timestamp.to_i &amp;gt; Time.now.to_i)&lt;br /&gt;          File.open(real_file_path(name), 'rb') { |f| f.read } rescue nil&lt;br /&gt;        else&lt;br /&gt;          nil&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;      def write(name, value, options = nil)&lt;br /&gt;        super&lt;br /&gt;        ensure_cache_path(File.dirname(real_file_path(name)))&lt;br /&gt;        if options and options[:expires_in] &lt;br /&gt;          File.open("#{real_file_path(name)}.timestamp", "wb+") { |f| f.write(Time.now.to_i + options[:expires_in].to_i) } &lt;br /&gt;        end&lt;br /&gt;        File.open(real_file_path(name), "wb+") { |f| f.write(value) }&lt;br /&gt;      rescue =&gt; e&lt;br /&gt;        RAILS_DEFAULT_LOGGER.error "Couldn't create cache directory: #{name} (#{e.message})" if RAILS_DEFAULT_LOGGER&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;      def delete(name, options = nil)&lt;br /&gt;        super&lt;br /&gt;        File.delete("#{real_file_path(name)}.timestamp") rescue  nil &lt;br /&gt;        File.delete(real_file_path(name)) rescue nil&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Se puede utilizar al igual que la opción expires_in en memcached.&lt;br /&gt;&lt;pre name="code" class="ruby:nocontrols"&gt;&lt;br /&gt;&amp;lt;% cache 'something', :expires_in =&gt; 5.minutes do %&amp;gt;&lt;br /&gt; Hello, just saying hello from cache&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lo he desarrollado teniendo en cuenta la implementación de rails 2.1.2, y solo soporta fragment caching.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-2193556984653707423?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/2193556984653707423/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=2193556984653707423&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/2193556984653707423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/2193556984653707423'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/11/expiration-time-en-fragment-caching.html' title='Expiration time en fragment caching'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-1192810205120246433</id><published>2008-11-26T22:16:00.003+01:00</published><updated>2008-11-26T22:31:06.059+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='memcached'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Fragment caching en rails</title><content type='html'>Usando memcached y fragment caching se puede acelerar mucho la reconstrucción de las vistas en rails.&lt;br /&gt;&lt;br /&gt;Para ello, una vez instalado memcached lo ejecutamos:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;memcached -d&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Por defecto estará en el puerto 11211.&lt;br /&gt;&lt;br /&gt;En el environment.rb hay que configurar la caché para que utilice memcached.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="ruby:nocontrols"&gt;&lt;br /&gt;config.cache_store = :mem_cache_store&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Tras eso en las vistas podemos hacer lo siguiente:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="ruby:nocontrols"&gt;&lt;br /&gt;&amp;lt;% cache 'key', :expires_in =&gt; 300 do %&amp;gt;&lt;br /&gt;  texto html...&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;300 es el número de segundos que este trozo estará vigente en caché, si no se indica nada el fragmento no expirará a no ser que lo indiquemos manualmente con &lt;a href="http://api.rubyonrails.org/classes/ActionController/Caching/Fragments.html"&gt;expire_fragment&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;key&lt;/span&gt; es la clave que tiene el fragmento dentro de la vista, es necesario para distinguir los distintos fragmentos.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-1192810205120246433?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/1192810205120246433/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=1192810205120246433&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/1192810205120246433'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/1192810205120246433'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/11/fragment-caching-en-rails.html' title='Fragment caching en rails'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-6278067135345712775</id><published>2008-11-15T23:37:00.009+01:00</published><updated>2008-11-16T01:18:38.340+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Memoization en ruby</title><content type='html'>La &lt;span style="font-style: italic;"&gt;memoization&lt;/span&gt; es una técnica de optimización que consiste en recordar los resultados para anteriores llamadas a una función.&lt;br /&gt;&lt;br /&gt;Si para una llamada anterior a una función se han pasado los parámetros x=2 e y=3, la próxima vez se devolverá el mismo resultado al obtenido, sin repetir cálculos.&lt;br /&gt;&lt;br /&gt;La implementación es muy sencilla y se pueden obtener mejoras muy significativas en funciones con cálculos pesados, basta con tener una tabla en la que utilicemos los parámetros de entrada como índices y el resultado de la función como valor.&lt;br /&gt;&lt;br /&gt;Si ya de por sí es sencillo implementar la memoization en ruby se puede usar también la gema que hay específica para &lt;a href="http://raa.ruby-lang.org/project/memoize/"&gt;ello&lt;/a&gt;, esta gema implementa la memoization automática, por lo que ni siquiera es necesario mantener una tabla para los valores, la gema se encarga de ello.&lt;br /&gt;&lt;br /&gt;Es necesario recordar que en la memoization solo se tienen en cuenta los parámetros de entrada, por lo que a las funciones que produzcan resultados a partir de otras entradas (ficheros, hora actual...) no se les debe aplicar esta técnica.&lt;br /&gt;&lt;br /&gt;En el siguiente ejemplo se implementa la función de fibonacci recursiva (fib1), la misma función aplicándole la memoization (fib2) y por último la memoization automática a fib1. En mi máquina para este ejemplo se obtienen ganancias de 30 a 40 veces en las versiones memoizadas.&lt;br /&gt;&lt;pre name="code" class="ruby:nocontrols"&gt;&lt;br /&gt;require 'rubygems'&lt;br /&gt;require 'memoize'&lt;br /&gt;&lt;br /&gt;include Memoize&lt;br /&gt;&lt;br /&gt;def fib1(n)&lt;br /&gt; return n if n &lt; 2&lt;br /&gt; return fib1(n-1) + fib1(n-2)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;@mm = {}&lt;br /&gt;def fib2(n)&lt;br /&gt; return @mm[n] if @mm[n]&lt;br /&gt; return n if n &lt; 2&lt;br /&gt; &lt;br /&gt; @mm[n] = fib2(n-1) + fib2(n-2)&lt;br /&gt; return @mm[n]&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;puts fib1(30)&lt;br /&gt;puts fib2(30)&lt;br /&gt;&lt;br /&gt;memoize(:fib1)&lt;br /&gt;puts fib1(30)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-6278067135345712775?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/6278067135345712775/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=6278067135345712775&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/6278067135345712775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/6278067135345712775'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/11/memoization-en-ruby.html' title='Memoization en ruby'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-8764275380724963253</id><published>2008-11-09T15:28:00.002+01:00</published><updated>2008-11-09T15:40:56.143+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Ocultar parámetros del log de rails</title><content type='html'>Al igual que se suelen encriptar las contraseñas al guardarlas en las bases de datos de las aplicaciones con autentificación, también se deberían omitir este tipo de campos en el log de rails.&lt;br /&gt;&lt;br /&gt;Cuando se despliega rails en un entorno en producción normalmente guarda el log de todas las peticiones en &lt;span style="font-style:italic;"&gt;log/production.log&lt;/span&gt;, ahí se pueden ver todos los parámetros que se pasan a la aplicación, incluyendo contraseñas, números de tarjetas de crédito, etc.&lt;br /&gt;&lt;br /&gt;Hay una forma de hacer que determinados parámetros no aparezcan, llamando a &lt;span style="font-style:italic;"&gt;filter_parameter_logging&lt;/span&gt; en el controlador.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid black; padding: 5px; background-color: rgb(51, 102, 153); color: rgb(0, 0, 0);"&gt;&lt;pre wrap=""&gt;class AccountsController &lt; ApplicationController&lt;br /&gt;  filter_parameter_logging :card_number&lt;br /&gt;&lt;br /&gt;  def index&lt;br /&gt;    @accounts = Account.find(:all)&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;En el log aparecerá algo así.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Parameters: {"card_number"=&gt;"[FILTERED]"...&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-8764275380724963253?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/8764275380724963253/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=8764275380724963253&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/8764275380724963253'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/8764275380724963253'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/11/ocultar-parmetros-del-log-de-rails.html' title='Ocultar parámetros del log de rails'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-5515866228815204143</id><published>2008-11-02T21:56:00.002+01:00</published><updated>2008-11-02T22:05:41.599+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Generacion dinámica de métodos en ruby</title><content type='html'>Una característica que puede añadir mucha potencia a los módulos que creamos en ruby es la generación automática de métodos.&lt;br /&gt;&lt;br /&gt;Se usa intensivamente en rails, tanto en la implementación de ActiveRecord como de la mayoría de plugins.&lt;br /&gt;&lt;br /&gt;Para definir métodos de forma dinámica tan solo hay que redefinir &lt;span style="font-style:italic;"&gt;method_missing&lt;/span&gt;, este método se llama cada vez que se llama a una función no definida, recibe como primer parámetro el nombre del método y como segundo parámetro la lista de argumentos.&lt;br /&gt;&lt;br /&gt;Dejo un ejemplo.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid black; padding: 5px; background-color: rgb(51, 102, 153); color: rgb(0, 0, 0);"&gt;&lt;pre wrap=""&gt;#!/usr/bin/ruby&lt;br /&gt;&lt;br /&gt;def method_missing(method, *args)&lt;br /&gt; if method.to_s =~ /saludo_(\w+)/&lt;br /&gt;  generar_saludo($1)&lt;br /&gt; else&lt;br /&gt;  super(method, *args)&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def generar_saludo(msg)&lt;br /&gt; puts "hola #{msg}"&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;saludo_mundo&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-5515866228815204143?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/5515866228815204143/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=5515866228815204143&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/5515866228815204143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/5515866228815204143'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/11/generacion-dinmica-de-mtodos-en-ruby.html' title='Generacion dinámica de métodos en ruby'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-7916707219428312417</id><published>2008-11-01T21:58:00.002+01:00</published><updated>2008-11-01T22:09:16.810+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><title type='text'>Crear clases dinámicas en ruby</title><content type='html'>La sintaxis de ruby para crear clases u objetos es muy sencilla, pero lo es más aún para definirlos dinámicamente.&lt;br /&gt;&lt;br /&gt;Para ello disponemos de la clase &lt;a href="http://www.ruby-doc.org/core/classes/Struct.html"&gt;Struct&lt;/a&gt;, veamos un ejemplo en el siguiente código.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid black; padding: 5px; background-color: rgb(51, 102, 153); color: rgb(0, 0, 0);"&gt;&lt;pre wrap=""&gt;Clase = Struct.new :campo1, :campo2&lt;br /&gt;objeto = Clase.new('valor1', 'valor2')&lt;br /&gt;puts objeto.campo1&lt;br /&gt;puts objeto.campo2&lt;br /&gt;puts objeto.members&lt;br /&gt;puts objeto.values&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;En la primera línea con &lt;span style="font-style:italic;"&gt;Struct&lt;/span&gt; se crea la clase &lt;span style="font-style:italic;"&gt;Clase&lt;/span&gt; con dos campos, esos campos ya están disponibles en los objetos que creemos a partir de la clase. Adicionalmente, &lt;span style="font-style:italic;"&gt;Struct&lt;/span&gt; añade algunos métodos como &lt;span style="font-style:italic;"&gt;members&lt;/span&gt; y &lt;span style="font-style:italic;"&gt;values&lt;/span&gt; para inspeccionar el contenido de los objetos creados.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-7916707219428312417?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/7916707219428312417/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=7916707219428312417&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/7916707219428312417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/7916707219428312417'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/11/crear-clases-dinmicas-en-ruby.html' title='Crear clases dinámicas en ruby'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3499379363161703464.post-3626569721371624764</id><published>2008-10-26T22:05:00.002+01:00</published><updated>2008-10-26T22:32:54.373+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programacion'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Metaprogramación en php</title><content type='html'>En php5 han entrado nuevas posibilidades en lo que a metaprogramación se refiere, revisando el siguiente trozo de código nos encontramos un ejemplo de que construcciones se pueden realizar.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid black; padding: 5px; background-color: rgb(51, 102, 153); color: rgb(0, 0, 0);"&gt;&lt;pre wrap=""&gt;&amp;lt;?php&lt;br /&gt;&lt;br /&gt;function saludo(){&lt;br /&gt;        return "hello";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;$cadena = "saludo";&lt;br /&gt;$cadena_funcion = "cadena";&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;?= $cadena() ?&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;br/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;?= $$cadena_funcion() ?&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Primero podemos ver que si guardamos en una cadena el nombre de una función, y luego llamamos a la cadena con una lista de parámetros (en este caso vacía), se invoca dicha función. Esto puede ser bastante útil para elegir que acción ejecutar ante una entrada por parámetro.&lt;br /&gt;&lt;br /&gt;En segundo lugar se muestra como utilizar las variables con doble '$', es decir, las variables de variables. Por cada '$' delante del nombre de la variable se hace una evaluación de la variable.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;$cadena_funcion&lt;/span&gt; contiene la cadena "cadena", si por el contrario se referencia a &lt;span style="font-style:italic;"&gt;$$cadena_funcion&lt;/span&gt;, se evaluará la variable &lt;span style="font-style:italic;"&gt;$cadena&lt;/span&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3499379363161703464-3626569721371624764?l=javiyu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javiyu.blogspot.com/feeds/3626569721371624764/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3499379363161703464&amp;postID=3626569721371624764&amp;isPopup=true' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3626569721371624764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3499379363161703464/posts/default/3626569721371624764'/><link rel='alternate' type='text/html' href='http://javiyu.blogspot.com/2008/10/metaprogramacin-en-php.html' title='Metaprogramación en php'/><author><name>javiyu</name><uri>http://www.blogger.com/profile/16257073535399021686</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='11306747125837505505'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry></feed>