El próximo miércoles 25 comienza la Conferencia Rails 2009. Este año me han colocado las cosas como a mí me gustan, casi sin colisiones entre temas interesantes. Además hay mucha presencia internacional, tanto en ponentes como en asistentes: Obie Fernández, Yehuda Katz, David Black, Scott Chacon... Aquí podéis ver la nota de prensa oficial.
En CameraWC he subido un vídeo para saludar tanto a los asistentes como a los no asistentes. Y en unvlog hay muchos más.
Bueno, tengo fundadas esperanzas en que este año todo va a ser mucho mejor que el pasado. La semana que viene lo comentamos. Salud y rocanrol!
I was getting kind of tired of her endless chatter.
Nothing I could say ever seemed to matter.
So I took a little drive just to clear my head,
and I saw a flashing neon, up ahead.
It looked like a place to find some satisfaction
with a little less talk and a lot more action.
I paid the man at the door and pushed my way to the bar.
Shouted for a drink over a screaming guitar.
A drunk on a stool tried to mess with my head
but I didn't even listen to a word he said.
I knew somewhere amid all this distraction.
Was a little less talk and a lot more action
sábado, 21 de noviembre de 2009
jueves, 12 de noviembre de 2009
Generando un avatar
Inventemos una palabra en castellano para traducir avatar. Podemos castellanizarla directamente, avatario o ávatar, porque parece que no podemos traducirla y, algo como, imagencilla, imagen de usuario o imagen en twitter, no me convence del todo.
Y dicha la tontería, vamos a lío. Supongamos que tenemos una imagen cuya ruta es C:/Users/danimata/full_body.jpg. Vamos a recortar un trozo de la imagen cuyo vértice superior izquierdo es (x1, y1) e inferior derecho (x2, y2). Además, para que el recorte nos quede bien en nuestra página de perfil de usuario, lo vamos a escalar para que tenga altura h y anchura w. Finalmente, lo guardaremos como C:/Users/danimata/avatar.jpg
Si utilizamos RMagick haremos lo siguiente:
require 'RMagick'
img = Magick::Image.read("C:/Users/danimata/full_body.jpg").first
tmp_avatar = img.crop(x1, y1, (x2 - x1 + 1), (y2 - y1 + 1))
avatar = tmp_avatar.resize(w, h)
temp = Tempfile.new("C:/Users/danimata/avatar.jpg") avatar.write "png:" + temp.path
Sin embargo, si utilizamos image_science haremos lo siguiente:
require 'image_science'
temp = Tempfile.new("C:/Users/danimata/avatar.jpg")
ImageScience.with_image "C:/Users/danimata/full_body.jpg" do |image|
image.with_crop x1, y1, x2, y2 do |cropped|
cropped.resize w, h do |avatar|
avatar.save temp.path
end
end
end
Y listo. Como siempre, salud y rocanrol!
La noche se está cayendo
y con ella cae el tiempo.
El día no sirvió de nada,
tarde de nubes sin agua.
Hoy el cielo es de cemento,
parece que dios está muerto.
Golpean la puerta de casa,
mensajeros de desgracia...
¡malas noticias!
Y dicha la tontería, vamos a lío. Supongamos que tenemos una imagen cuya ruta es C:/Users/danimata/full_body.jpg. Vamos a recortar un trozo de la imagen cuyo vértice superior izquierdo es (x1, y1) e inferior derecho (x2, y2). Además, para que el recorte nos quede bien en nuestra página de perfil de usuario, lo vamos a escalar para que tenga altura h y anchura w. Finalmente, lo guardaremos como C:/Users/danimata/avatar.jpg
Si utilizamos RMagick haremos lo siguiente:
require 'RMagick'
img = Magick::Image.read("C:/Users/danimata/full_body.jpg").first
tmp_avatar = img.crop(x1, y1, (x2 - x1 + 1), (y2 - y1 + 1))
avatar = tmp_avatar.resize(w, h)
temp = Tempfile.new("C:/Users/danimata/avatar.jpg") avatar.write "png:" + temp.path
Sin embargo, si utilizamos image_science haremos lo siguiente:
require 'image_science'
temp = Tempfile.new("C:/Users/danimata/avatar.jpg")
ImageScience.with_image "C:/Users/danimata/full_body.jpg" do |image|
image.with_crop x1, y1, x2, y2 do |cropped|
cropped.resize w, h do |avatar|
avatar.save temp.path
end
end
end
Y listo. Como siempre, salud y rocanrol!
La noche se está cayendo
y con ella cae el tiempo.
El día no sirvió de nada,
tarde de nubes sin agua.
Hoy el cielo es de cemento,
parece que dios está muerto.
Golpean la puerta de casa,
mensajeros de desgracia...
¡malas noticias!
miércoles, 28 de octubre de 2009
Presentación sobre OpenID y OAuth
He creado una presentación para contar a unos amigos lo más básico sobre OpenID y OAuth.
Salud y rocanrol
¿Quién tiró la piedra? ¿Quién escondió la mano?
¡Quién le puso el pie al gordo del "Gran Hermano"?
Quien sabe lo que sabe, sabe que no sabe nada.
El que nada no se ahoga o le dan por la almorrana
¡O le dan por la almorrana!
Y si tiene almorranas es que sufre en silencio.
El silencio pa' los muertos, yo me quedo con un tercio.
Tres tercios son un litro o cinco botellines.
Con cinco botellines hay que echarse un cigarrito.
¡Hay que echarse un cigarrito!
Salud y rocanrol
¿Quién tiró la piedra? ¿Quién escondió la mano?
¡Quién le puso el pie al gordo del "Gran Hermano"?
Quien sabe lo que sabe, sabe que no sabe nada.
El que nada no se ahoga o le dan por la almorrana
¡O le dan por la almorrana!
Y si tiene almorranas es que sufre en silencio.
El silencio pa' los muertos, yo me quedo con un tercio.
Tres tercios son un litro o cinco botellines.
Con cinco botellines hay que echarse un cigarrito.
¡Hay que echarse un cigarrito!
domingo, 25 de octubre de 2009
La absurdez hecha duda
Hoy me ha surgido la duda de cómo hacer una validación de unicidad sobre dos atributos de un modelo. Nunca había tenido que hacer algo similar en Rails.
La mejor opción que he encontrado ha sido
validates_uniqueness_of :family_id, :scope => :user_id
Esto validará que para cada valor de user_id el valor de family_id es único. Seguro que hay una solución mejor y más estándar, pero yo no me la sé.
Weno, pues nada. Ha sido breve pero intenso. Salud y rocanrol.
We all came out to Montereax
On the Lake Geneva shoreline
To make records with a mobile
We didn't have much time
Frank Zappa and the Mothers
Were at the best place around
But some stupid with a flare gun
Burned the place to the ground
Smoke on the water, fire in the sky
Smoke on the water...
La mejor opción que he encontrado ha sido
validates_uniqueness_of :family_id, :scope => :user_id
Esto validará que para cada valor de user_id el valor de family_id es único. Seguro que hay una solución mejor y más estándar, pero yo no me la sé.
Weno, pues nada. Ha sido breve pero intenso. Salud y rocanrol.
We all came out to Montereax
On the Lake Geneva shoreline
To make records with a mobile
We didn't have much time
Frank Zappa and the Mothers
Were at the best place around
But some stupid with a flare gun
Burned the place to the ground
Smoke on the water, fire in the sky
Smoke on the water...
lunes, 5 de octubre de 2009
Git en Windows Vista
Por fin he puesto a funcionar un proyecto que tira de un repositorio git. Para ello he tenido que hacer tres cosas:
Un par de links interesantes por si encontráis otros problemas: un manual http://kylecordes.com/2008/04/30/git-windows-go/ y una lista de posibles errores con su solución http://github.com/guides/addressing-authentication-problems-with-ssh
Salud y rocanrol!
She was a fast machine
She kept her motor clean
She was the best damn woman that I'd ever seen
She had the sightless eyes
Telling me no lies
Knocking me out with those American thighs
Taking more than her share
Had me fighting for air
She told me to come but I was already there
Cause the walls started shaking
The earth was quaking
My mind was aching
And we were making it and you
Shook me all night long
Yeah you
Shook me all night long
- instalar git para windows desde aquí
- confgurar la variable de entorno GIT_SSH para que apunte al ejecutable ssh que viene con el instalador de git, típicamente en C:\Program Files\Git\bin\ssh.exe
- clonar el repositorio y empezar a trabajar
Un par de links interesantes por si encontráis otros problemas: un manual http://kylecordes.com/2008/04/30/git-windows-go/ y una lista de posibles errores con su solución http://github.com/guides/addressing-authentication-problems-with-ssh
Salud y rocanrol!
She was a fast machine
She kept her motor clean
She was the best damn woman that I'd ever seen
She had the sightless eyes
Telling me no lies
Knocking me out with those American thighs
Taking more than her share
Had me fighting for air
She told me to come but I was already there
Cause the walls started shaking
The earth was quaking
My mind was aching
And we were making it and you
Shook me all night long
Yeah you
Shook me all night long
martes, 29 de septiembre de 2009
FasterCSV y Rails 2.3
Pues parece FasterCSV y Rails 2.3 no terminan de llevarse bien. De hecho, en las aplicaciones rails que he migrado a la versión 2.3.2, cualquier exportación de datos a formato CSV ha dejado de funcionar. Lo extraño es que no falla nada salvo que los archivos resultantes están vacíos.
Menos mal que San Google siempre está ahí, cuando se le necesita. Buscando buscando llego aquí donde explican que en Rails 2.3 a ActionController::Response le han quitado el método << con lo que las concatenaciones típicas en la generación de archivos CSV no funcionan. En el link se ofrece una solucioncilla on the fly que funciona estupendamente para código viejuno.
En fin, as usual, salud y rocanrol
Piensas otra vez que ahora es tu momento,
aunque al parecer todo vuelve a cambiar.
Es difícil ser otra vez tú mismo,
y no ver a tiempo que estás acabado.
¿Quién te convenció de cantar así?
¿Quién te prometió la inmortalidad?
Tratan de volver los desertores del rock.
Tratan de volver los desertores del rock.
¡Pero es tarde ya!.
Menos mal que San Google siempre está ahí, cuando se le necesita. Buscando buscando llego aquí donde explican que en Rails 2.3 a ActionController::Response le han quitado el método << con lo que las concatenaciones típicas en la generación de archivos CSV no funcionan. En el link se ofrece una solucioncilla on the fly que funciona estupendamente para código viejuno.
En fin, as usual, salud y rocanrol
Piensas otra vez que ahora es tu momento,
aunque al parecer todo vuelve a cambiar.
Es difícil ser otra vez tú mismo,
y no ver a tiempo que estás acabado.
¿Quién te convenció de cantar así?
¿Quién te prometió la inmortalidad?
Tratan de volver los desertores del rock.
Tratan de volver los desertores del rock.
¡Pero es tarde ya!.
miércoles, 9 de septiembre de 2009
Nota mental
Hay cosas que por más que las leas, las veas y las intentes retener, son imposibles de memorizar. Hay otras, sin embargo, que se quedan ahí contigo para el resto de tu vida, y que no sirven para nada. Como la típica cancioncilla que oyes a alguien y no se te va de la cabeza en todo el día. Probemos con 'dragones y mazmorras, un mundo infernal, se esconde entre las sombras, la fuerza del mal'.
En fin, no sé cuantas veces he buscado cómo se llama la variable local que contiene el número de iteración que pinta en cada momento. Por lógica, se llama contador o counter, pero en estos casos la lógica no aparece por ningún lado. Y para que cada partial tenga la suya, por delante se pone el nombre del archivo. Es decir, si ejecutamos
<%= render :partial => 'line', :collection => @lines %>
cada ejecución del partial _line.html.rb contará con la variable local line_counter cuyo valor irá creciendo en cada ejecución desde 0 hasta el número de items contenidos en @lines menos 1.
Para más información, pregunten por el señor api.
Me juego una caña a que se me olvida que he escrito este post. Salud y rocanrol
Sólo quiero ser más rápido que ellos
y echar todo a perder un día tras otro
y un buen rato después saber llegar a casa
antes de que el sol, me diga que es de día
En fin, no sé cuantas veces he buscado cómo se llama la variable local que contiene el número de iteración que pinta en cada momento. Por lógica, se llama contador o counter, pero en estos casos la lógica no aparece por ningún lado. Y para que cada partial tenga la suya, por delante se pone el nombre del archivo. Es decir, si ejecutamos
<%= render :partial => 'line', :collection => @lines %>
cada ejecución del partial _line.html.rb contará con la variable local line_counter cuyo valor irá creciendo en cada ejecución desde 0 hasta el número de items contenidos en @lines menos 1.
Para más información, pregunten por el señor api.
Me juego una caña a que se me olvida que he escrito este post. Salud y rocanrol
Sólo quiero ser más rápido que ellos
y echar todo a perder un día tras otro
y un buen rato después saber llegar a casa
antes de que el sol, me diga que es de día
viernes, 17 de julio de 2009
Combos dependientes re-visited
Hace tiempo ya conté cómo hacer combos dependientes con observe_field pero debe ser que me hago viejo y que me gusta hacer las cosas cada vez más pedrestres. En fin, que tirando a pelo de prototype, un poco de javascript y adaptando un controlador, conseguimos lo mismo de forma muy sencilla.
Supongamos dos modelos dependientes
class Country < ActiveRecord::Base
has_many :provinces
end
class Province < ActiveRecord::Base
belongs_to :country
end
enlazados en nuestro archivo de rutas como
map.resources :countries do |country|
country.resources :provinces
end
En a vista enlazamos países y provincias en dos combos...
<%= select :person, :country_id, countries_for_select.map{|c| [c.name, c.id]}, {}, {:onchange => "change_provinces('person_province_id', this.value);", :class => 'w150'} %>
<%= select :person, :province_id, provinces_for_select.map{|c| [c.name, c.id]}, {}, {:class => 'w150'} %>
donde countries_for_select y provinces_for_select nos dan los registro a pintar inicialmente en cada uno de ellos.
Sólo falta la función javascript change_provinces que haga la llamada Ajax y el repintado del combo cuyo ide recibe como primer parámetro...
function change_provinces(combo_id, country_id){
var url = '/countries/' + country_id + '/provinces';
var combo = $(combo_id);
for (var i = combo.options.length-1; i >= 0; i--){
combo.options[i] = null;
}
new Ajax.Request(url, {
method: 'get',
onSuccess: function(transport) {
prov_array = eval(transport.responseText);
for (var k = 0; k < prov_array.length; k++){
combo.options[k] = new Option(prov_array[k][1], prov_array[k][0]);
}
combo.options[0].selected = true;
},
onFailure:function(transport) {
}
});
}
... y finalmente adaptar provinces_controller
def index
@provinces = ...
if request.xhr?
render :text => @provinces.map{|province| [province.id, province.name]}.inspect
return
end
end
Y a correr. Ale, salud y rocanrol!
We shall go on to the end, we shall fight in France,
we shall fight on the seas and oceans,
we shall fight with growing confidence and growing strength in the air,
we shall defend our Island, whatever the cost may be,
we shall fight on the beaches,
we shall fight on the landing grounds,
we shall fight in the fields and in the streets,
we shall fight in the hills, we shall never surrender!"
Supongamos dos modelos dependientes
class Country < ActiveRecord::Base
has_many :provinces
end
class Province < ActiveRecord::Base
belongs_to :country
end
enlazados en nuestro archivo de rutas como
map.resources :countries do |country|
country.resources :provinces
end
En a vista enlazamos países y provincias en dos combos...
<%= select :person, :country_id, countries_for_select.map{|c| [c.name, c.id]}, {}, {:onchange => "change_provinces('person_province_id', this.value);", :class => 'w150'} %>
<%= select :person, :province_id, provinces_for_select.map{|c| [c.name, c.id]}, {}, {:class => 'w150'} %>
donde countries_for_select y provinces_for_select nos dan los registro a pintar inicialmente en cada uno de ellos.
Sólo falta la función javascript change_provinces que haga la llamada Ajax y el repintado del combo cuyo ide recibe como primer parámetro...
function change_provinces(combo_id, country_id){
var url = '/countries/' + country_id + '/provinces';
var combo = $(combo_id);
for (var i = combo.options.length-1; i >= 0; i--){
combo.options[i] = null;
}
new Ajax.Request(url, {
method: 'get',
onSuccess: function(transport) {
prov_array = eval(transport.responseText);
for (var k = 0; k < prov_array.length; k++){
combo.options[k] = new Option(prov_array[k][1], prov_array[k][0]);
}
combo.options[0].selected = true;
},
onFailure:function(transport) {
}
});
}
... y finalmente adaptar provinces_controller
def index
@provinces = ...
if request.xhr?
render :text => @provinces.map{|province| [province.id, province.name]}.inspect
return
end
end
Y a correr. Ale, salud y rocanrol!
We shall go on to the end, we shall fight in France,
we shall fight on the seas and oceans,
we shall fight with growing confidence and growing strength in the air,
we shall defend our Island, whatever the cost may be,
we shall fight on the beaches,
we shall fight on the landing grounds,
we shall fight in the fields and in the streets,
we shall fight in the hills, we shall never surrender!"
martes, 16 de junio de 2009
Quiero logarme usando open ID
Lo primero es instalar la gema ruby-openid y el plugin open_id_authentication. Intenté usar directamente el plugin restful_open_id_authentication pero ya tenía montada una autenticación de tó la vida y preferí añadir únicamente la parte de open ID.
Ahora sólo queda seguir las instrucciones que encontramos en los links, pero con un par de matices:
Y a correr.
Salud y rocanrol. (hoy no hay ánimo para canciones, sorry)
Ahora sólo queda seguir las instrucciones que encontramos en los links, pero con un par de matices:
- Generamos las tablas que necesita el plugin
> ruby script/generate open_id_authentication_tables MigrationName
Esto generará una migration con las tablas open_id_authentication_associations y open_id_authentication_nonces, asi que...
> rake db:migrate
Si el modelo User aún no tiene un campo para almacenar el openid, tenemos que crearlo, claro está.
-
En el archivo de rutas nos aseguramos de que exista una ruta root y permitimos el acceso al método create del controlador sessions mediante GET
map.open_id_complete 'session', :controller => "sessions", :action => "create", :requirements => {:method => :get }
-
En la vista correspondiente al login de la aplicación, añadimos un campo que contenga el openid del usuario.
<%= text_field_tag 'identity_url' %>
-
Adaptamos sessions_controller para que ejecute el login contra openID cuando sea necesario. Basta con seguir las instrucciones que nos proporcionan los creadores del plugin, juntarlo con el código que tengamos para autenticar (en este caso, restful_authentication) y retocar un par de cosas.
En el controller sessions_controller.rb
def create
if !params[:identity_url].blank? || request.get?
open_id_authentication(params[:identity_url])
else
password_authentication
end
end
protected
def password_authentication
self.current_user = User.authenticate(params[:login], params[:password])
if logged_in?
if params[:remember_me] == "1"
current_user.remember_me unless current_user.remember_token?
cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
end
successful_login "Logged in successfully"
else
failed_login "User and password do not match"
end
end
def open_id_authentication(identity_url)
authenticate_with_open_id(identity_url) do |status, identity_url|
unless status.successful?
failed_login status.message
else
if @current_user = User.find_by_identity_url(identity_url)
successful_login
else
failed_login "Sorry, no user by that identity URL exists"
end
end
end
end
y en el plugin, en open_id_authentication.rb
def successful?
@code == :successful
end
Y a correr.
Salud y rocanrol. (hoy no hay ánimo para canciones, sorry)
domingo, 7 de junio de 2009
Mensajes de error enviando mails
He encontrado este artículo que explica bastante bien los mensjaes de error más comunes que recibimos al enviar un email de forma errónea.
Divide los mensajes en tres grupos: provocados por errores en el dominio, por errores en el mailbox o por filtros antispam. Para más detalles lo mejor es leer el artículo.
Ha sido corto, pero intenso. Salud y rocanrol!
I've got friends in low places
Where the whiskey drowns
And the beer chases my blues away
And I'll be okay
I'm not big on social graces
Think I'll slip on down to the oasis
Oh, I've got friends in low places
Divide los mensajes en tres grupos: provocados por errores en el dominio, por errores en el mailbox o por filtros antispam. Para más detalles lo mejor es leer el artículo.
Ha sido corto, pero intenso. Salud y rocanrol!
I've got friends in low places
Where the whiskey drowns
And the beer chases my blues away
And I'll be okay
I'm not big on social graces
Think I'll slip on down to the oasis
Oh, I've got friends in low places
Suscribirse a:
Entradas (Atom)