domingo, 30 de noviembre de 2008

Terminales virtuales en linux

Regularmente cuando administramos equipos unix necesitamos varios terminales, por ejemplo, en el caso de estar administrando un equipo linux remoto por ssh.

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.

El comando screen de linux permite crear terminales virtuales, por lo que este problema lo tendríamos resuelto, su funcionamiento es muy simple.

Ejecutando:

sreen bash, abrimos una instancia nueva de bash en otro terminal.

Ctrl+A ", muestra una lista de los terminales virtuales.

Ctr+A 2, va a la tercera consola virtual (numeradas desde 0).

Ctrl+A Ctrl+A, va a la consola mostrada anteriormente.

Ctrl+A d, deja ejecutando las tareas en background (detach).

Expiration time en fragment caching

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.

Otra posibilidad es guardar los items en el sistema de ficheros, para ello hay que añadir en el environment.rb lo siguiente.

config.cache_store = :file_store, '/path_to_cache'

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.

Se puede solucionar añadiendo el siguiente parche al environment.rb

module ActiveSupport
module Cache
class FileStore < Store
def read(name, options = nil)
super
timestamp = File.open("#{real_file_path(name)}.timestamp", 'rb') { |f| f.read } rescue nil
if !timestamp or (timestamp and timestamp.to_i > Time.now.to_i)
File.open(real_file_path(name), 'rb') { |f| f.read } rescue nil
else
nil
end
end

def write(name, value, options = nil)
super
ensure_cache_path(File.dirname(real_file_path(name)))
if options and options[:expires_in]
File.open("#{real_file_path(name)}.timestamp", "wb+") { |f| f.write(Time.now.to_i + options[:expires_in].to_i) }
end
File.open(real_file_path(name), "wb+") { |f| f.write(value) }
rescue => e
RAILS_DEFAULT_LOGGER.error "Couldn't create cache directory: #{name} (#{e.message})" if RAILS_DEFAULT_LOGGER
end

def delete(name, options = nil)
super
File.delete("#{real_file_path(name)}.timestamp") rescue nil
File.delete(real_file_path(name)) rescue nil
end
end
end
end

Se puede utilizar al igual que la opción expires_in en memcached.

<% cache 'something', :expires_in => 5.minutes do %>
Hello, just saying hello from cache
<% end %>

Lo he desarrollado teniendo en cuenta la implementación de rails 2.1.2, y solo soporta fragment caching.

miércoles, 26 de noviembre de 2008

Fragment caching en rails

Usando memcached y fragment caching se puede acelerar mucho la reconstrucción de las vistas en rails.

Para ello, una vez instalado memcached lo ejecutamos:

memcached -d

Por defecto estará en el puerto 11211.

En el environment.rb hay que configurar la caché para que utilice memcached.


config.cache_store = :mem_cache_store


Tras eso en las vistas podemos hacer lo siguiente:


<% cache 'key', :expires_in => 300 do %>
texto html...
<% end %>


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 expire_fragment.

key es la clave que tiene el fragmento dentro de la vista, es necesario para distinguir los distintos fragmentos.

sábado, 15 de noviembre de 2008

Memoization en ruby

La memoization es una técnica de optimización que consiste en recordar los resultados para anteriores llamadas a una función.

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.

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.

Si ya de por sí es sencillo implementar la memoization en ruby se puede usar también la gema que hay específica para ello, 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.

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.

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.

require 'rubygems'
require 'memoize'

include Memoize

def fib1(n)
return n if n < 2
return fib1(n-1) + fib1(n-2)
end


@mm = {}
def fib2(n)
return @mm[n] if @mm[n]
return n if n < 2

@mm[n] = fib2(n-1) + fib2(n-2)
return @mm[n]
end

puts fib1(30)
puts fib2(30)

memoize(:fib1)
puts fib1(30)

domingo, 9 de noviembre de 2008

Ocultar parámetros del log de rails

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.

Cuando se despliega rails en un entorno en producción normalmente guarda el log de todas las peticiones en log/production.log, 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.

Hay una forma de hacer que determinados parámetros no aparezcan, llamando a filter_parameter_logging en el controlador.

class AccountsController < ApplicationController
filter_parameter_logging :card_number

def index
@accounts = Account.find(:all)
end
end

En el log aparecerá algo así.

Parameters: {"card_number"=>"[FILTERED]"...

domingo, 2 de noviembre de 2008

Generacion dinámica de métodos en ruby

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.

Se usa intensivamente en rails, tanto en la implementación de ActiveRecord como de la mayoría de plugins.

Para definir métodos de forma dinámica tan solo hay que redefinir method_missing, 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.

Dejo un ejemplo.

#!/usr/bin/ruby

def method_missing(method, *args)
if method.to_s =~ /saludo_(\w+)/
generar_saludo($1)
else
super(method, *args)
end
end

def generar_saludo(msg)
puts "hola #{msg}"
end


saludo_mundo

sábado, 1 de noviembre de 2008

Crear clases dinámicas en ruby

La sintaxis de ruby para crear clases u objetos es muy sencilla, pero lo es más aún para definirlos dinámicamente.

Para ello disponemos de la clase Struct, veamos un ejemplo en el siguiente código.

Clase = Struct.new :campo1, :campo2
objeto = Clase.new('valor1', 'valor2')
puts objeto.campo1
puts objeto.campo2
puts objeto.members
puts objeto.values

En la primera línea con Struct se crea la clase Clase con dos campos, esos campos ya están disponibles en los objetos que creemos a partir de la clase. Adicionalmente, Struct añade algunos métodos como members y values para inspeccionar el contenido de los objetos creados.