<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Vía Rails</title>
	<atom:link href="http://viarails.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://viarails.wordpress.com</link>
	<description>Ruby on Rails a Máxima Velocidad - en Español</description>
	<lastBuildDate>Mon, 27 Jun 2011 07:27:46 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='viarails.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Vía Rails</title>
		<link>http://viarails.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://viarails.wordpress.com/osd.xml" title="Vía Rails" />
	<atom:link rel='hub' href='http://viarails.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Buscando con Searchlogic</title>
		<link>http://viarails.wordpress.com/2010/01/05/buscando-con-searchlogic/</link>
		<comments>http://viarails.wordpress.com/2010/01/05/buscando-con-searchlogic/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 18:26:39 +0000</pubDate>
		<dc:creator>Gabriel Somoza</dc:creator>
				<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Refactorización]]></category>
		<category><![CDATA[búsqueda]]></category>
		<category><![CDATA[named_scopes]]></category>
		<category><![CDATA[permisos]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[searchlogic]]></category>
		<category><![CDATA[Usuarios]]></category>

		<guid isPermaLink="false">http://viarails.wordpress.com/?p=168</guid>
		<description><![CDATA[Searchlogic es un plugin muy útil que nos permite buscar en modelos de forma mucho más ágil, por medio de named scopes (introducidos en Rails 2.x). Los named scopes (la mejor traducción no-literal que se me ocurre es: &#8220;filtros de alcance&#8220;) son básicamente filtros SQL con nombre, pero esto es más fácil de mostrar que [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=168&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><a title="Searchlogic - Repositorio Git" href="http://github.com/binarylogic/searchlogic" target="_blank">Searchlogic</a> es un plugin muy útil que nos permite buscar en modelos de forma mucho más ágil, por medio de <em>named scopes</em> (introducidos en Rails 2.x). Los <em>named scopes</em> (la mejor traducción no-literal que se me ocurre es: <em>&#8220;filtros de alcance</em>&#8220;) son básicamente filtros SQL <em>con nombre</em>, pero esto es más fácil de mostrar que de explicar. La principal ventaja de estos filtros es que nos permiten hacer nuestro código más DRY (<em>Don&#8217;t Repeat Yourself</em>, es decir: menos repetitivo), lo que a su vez acelera muchísimo el tiempo de desarrollo. <span id="more-168"></span></p>
<h3>1- Instalación de Searchlogic</h3>
<p>Por supuesto, la instalación de Searchlogic es muy simple:</p>
<pre class="brush: plain; light: true;">
sudo gem install searchlogic
</pre>
<p>Una vez instalado el gem agregamos la siguiente línea a nuestro archivo de configuración de entorno:</p>
<pre class="brush: ruby;">
#config/environment.rb
config.gem &quot;searchlogic&quot;
</pre>
<p>Aunque es más recomendable el método anterior, también se puede instalar como plugin:</p>
<pre class="brush: plain; light: true;">
script/plugin install git://github.com/binarylogic/searchlogic.git
</pre>
<h3>2- Conceptos Básicos</h3>
<p>Searchlogic es un plugin muy completo y flexible: nos brinda filtros y una clase para encapsularlos, métodos de ordenamiento, helpers para nuestras vistas, integración con otros plugins (como WillPaginate), etc. No vamos a cubrir todos sus aspectos, por lo menos no en esta ocasión; pero sí conoceremos algunos conceptos básicos que nos darán el puntapié necesario para animarnos a usarlo en nuestras aplicaciones. Comenzaremos viendo su concepto central: filtros.</p>
<h4>Filtros (Named Scopes)</h4>
<p>Una vez instalado, Searchlogic nos permite utilizar cierto número de filtros predeterminados, que en realidad son los famosos <em>named_scopes</em> de Rails. En la <a title="Documentación de Searchlogic" href="http://rdoc.info/projects/binarylogic/searchlogic" target="_blank">documentación</a> hay una <a title="Lista de Filtros" href="http://rdoc.info/rdoc/binarylogic/searchlogic/blob/dbec136d866c2aa5b9dd58bd33e32c57ce8997f8/Searchlogic/NamedScopes/Conditions.html" target="_blank">lista completa</a> de todas las opciones que podemos usar; aquí conoceremos sólo algunas.</p>
<p>Searchlogic no necesita nada de configuración: podemos utilizar sus filtros directamente. Supongamos que tenemos un modelo llamado <em>User</em> con algunos de los campos más comunes: <em>username, email, password </em>y<em> age</em> (nombre de usuario, email, contraseña y edad, respectivamente). También ya tenemos varios usuarios registrados, y lo que queremos es ver cuáles de los usuarios registrados menores a 20 años tienen una cuenta de email dentro del Reino Unido (termina en &#8220;<em>.co.uk</em>&#8220;). Para ello podemos levantar nuestra consola rails y simplemente ingresar lo siguiente:</p>
<pre class="brush: ruby;">
User.age_lt(20).email_ends_with(&quot;.co.uk&quot;)
</pre>
<p>Searchlogic nos devuelve una lista con los usuarios que cumplen con dichos criterios. Podemos concatenar toda la cantidad de filtros que sea necesario, uno tras otro. El método <em>age_lt</em> significa <em>age_less_than</em> (edad menor a); y como ésta, existen también otras formas de comparación similares, junto con sus abreviaturas. En la documentación podemos encontrar todas las posibilidades.</p>
<p>Es importante notar que podemos usar estos filtros con cualquier campo. Si por ejemplo tuviéramos un campo que sea <em>login_count</em> para almacenar la cantidad de veces que el usuario inició sesión en nuestro sitio, podemos buscar todos los usuarios del Reino Unido que hayan entrado más de 100 veces <em>o más</em> de la siguiente forma:</p>
<pre class="brush: ruby;">
User.email_ends_with(&quot;.co.uk&quot;).login_count_gte(100)
# gte es la abreviatura de &quot;greater than or equal&quot;
# y significa &quot;mayor o igual a&quot;.
</pre>
<h4>Negando Filtros</h4>
<p>De la misma forma que podemos buscar los usuarios cuyos emails terminen con &#8220;.co.uk&#8221;, también podemos buscar aquellos cuyos emails <em>no</em> terminen con dicha cadena:</p>
<pre class="brush: ruby;">
User.email_does_not_end_with(&quot;.co.uk&quot;)

#Otro ejemplo:
User.username_not_like(&quot;juan&quot;)
#devuelve todos aquellos cuyo nombre de usuario no contiene la cadena &quot;juan&quot;
</pre>
<p>Podemos hacer lo mismo con cualquier otro filtro.</p>
<h4>Combinando Filtros</h4>
<p>Puede ser el caso que querramos buscar a todos los usuarios cuyo nombre (name) o nombre de usuario (username) contengan la cadena &#8220;juan&#8221;. Para ello podemos combinar los filtros en un mismo llamado por medio de la palabra &#8220;or&#8221;, de la siguiente forma:</p>
<pre class="brush: ruby;">
User.name_or_username_like(&quot;juan&quot;)
</pre>
<h4>Todos o Cualquiera</h4>
<p>Los filtros aceptan también más de un elemento, pero para ello debemos usar alguno de los modificadores especiales para múltiples parámetros: <em>all</em> (todos) o <em>any</em> (cualquiera). Veamos algunos ejemplos:</p>
<pre class="brush: ruby;">
#Busca los usuarios con cuenta de email del Reino Unido o de Argentina:
User.email_ends_with_any(&quot;.co.uk&quot;, &quot;.ar&quot;)

#Busca los usuarios que tengan &quot;Juan&quot; y &quot;Perez&quot; dentro de su nombre:
User.name_like_all(&quot;Juan&quot;, &quot;Perez&quot;)
#Algunos resultados pueden ser:
#  Juan Perez Aguilar
#  Juan Marcos Perez

#También podemos pasar un Array:
User.name_like_all([&quot;Juan&quot;, &quot;Perez&quot;])
</pre>
<h4>Ordenamiento</h4>
<p>En SQL es posible ordenar los resultados que obtenemos, y Searchlogic no se ha olvidado de ello. Para ordenar los resultados anteriores por nombre de usuario podemos hacer lo siguiente:</p>
<pre class="brush: ruby;">
#Ordena por nombre de usuario desde la A a la Z
User.email_ends_with(&quot;.co.uk&quot;).login_count_gte(100).ascend_by_username
#Ordena por nombre de usuario desde la Z a la A
User.email_ends_with(&quot;.co.uk&quot;).login_count_gte(100).descend_by_username
</pre>
<p>De forma similar a los demás filtros, <em>&#8220;ascend_by</em>&#8221; y &#8220;<em>descend_by</em>&#8221; son filtros dinámicos, y en su caso particular deben ser precedidos por el nombre del campo a ordenar (<em>username</em>).</p>
<h4>Rendimiento</h4>
<p>De aquí nos puede surgir la duda de si Searchlogic implementa todos los named scopes posibles para cada modelo de forma predeterminada. Por suerte esto no es así, sino que los crea a medida que los vamos utilizando. Además, una vez creado el named scope, Searchlogic <em>no lo vuelve a crear</em> si lo necesitamos de nuevo. Por lo tanto, no hay que temer por nuestros recursos: el rendimiento con Searchlogic es muy similar al rendimiento que obtendríamos si usáramos nuestros propios <em>named scopes</em>.</p>
<p>Algo más que vale destacar en cuanto a rendimiento: Searchlogic no ejecuta ningún query en la base de datos sino hasta que llamemos al último filtro en la cadena. En el caso anterior, por ejemplo, la siguiente consulta SQL se ejecuta sólo <em>luego</em> de aplicarse el filtro &#8220;<em>login_count_gte</em>&#8220;:</p>
<pre class="brush: sql;">
SELECT * FROM `users` WHERE ((users.login_count &gt;= 100) AND (users.email LIKE '%.co.uk'))
</pre>
<h3>3- La clase Search</h3>
<p>Searchlogic también nos permite buscar utilizando una clase llamada <em>Search</em>. Esta clase nos sirve para añadir o quitar filtros a una búsqueda, cuyas condiciones quedan almacenadas en la instancia. Igual que en el caso anterior, Searchlogic ejecuta un sólo query a la hora de buscar. En este caso, sin embargo, lo ejecuta luego de que llamemos a algunos de los métodos de búsqueda. Veamos un ejemplo:</p>
<pre class="brush: ruby;">
@search = User.search #Crea el objeto Search para el modelo User
@search.age_lt(20)
@search.email_ends_with(&quot;.co.uk&quot;)
# Hasta aquí todavía no se ha hecho ningún query a la base de datos

@search.all #ejecuta el query y devuelve todos los registros
@search.first #ejecuta el query y devuelve sólo el primer registro
@search.count #devuelve el número de registros encontrados
#etc.
</pre>
<p>Otra posibilidad menos atractiva pero potencialmente más flexible es acceder directamente al Hash de filtros, de la siguiente manera:</p>
<pre class="brush: ruby;">
@search = User.search #Crea el objeto Search
@search.conditions[:age_lt] = 20 # notar la diferencia de sintaxis
@search.conditions[:name_begins_with] = &quot;a&quot;
# Otra forma que puede ser útil en algunos casos:
@search.conditions.merge!({:email_ends_with =&gt; &quot;.co.uk&quot;})
#Ahora @search contiene el siguiente hash:
# {:age_lt=&gt;20, :email_ends_with=&gt;&quot;.co.uk&quot;, :name_begins_with=&gt;&quot;a&quot;}
@search.all #devuelve todos los resultados filtrados
</pre>
<p>Para quitar un filtro del objeto Search, basta con llamar su método <em>delete</em> y pasarle la condición que queremos borrar:</p>
<pre class="brush: ruby;">
@search.delete(:name_begins_with)
#=&gt; {:age_lt=&gt;20, :email_ends_with=&gt;&quot;.co.uk&quot;}
</pre>
<h3>4- Búsquedas Avanzadas</h3>
<p>Hay varias formas de búsqueda que requieren utilizar conceptos un poco más avanzados, como los JOINS o INCLUDES de SQL. Sin embargo, Searchlogic nuevamente nos facilita la tarea con sus potentes herramientas. Para esta sección vamos a suponer que los usuarios de la tabla <em>Users</em> &#8220;tienen muchos&#8221; <em>Permissions</em> (permisos) a través de una relación <em>habtm</em> (explicar los conceptos básicos de este tipo de relación escapa a nuestro propósito en este momento).</p>
<h4>Buscando en clases Asociadas</h4>
<p>De acuerdo con la relación que tenemos entre los usuarios y sus permisos, supongamos que queremos buscar a todos los usuarios que tengan permiso para <em>comentar</em>:</p>
<pre class="brush: ruby;">
@search = User.search
@search.permissions_name_is(&quot;comment&quot;)
@search.all # ejecuta la consulta
</pre>
<p>La consula SQL generada es:</p>
<pre class="brush: sql;">
SELECT `users`.* FROM `users` INNER JOIN `permissions_users` ON `permissions_users`.user_id = `users`.id INNER JOIN `permissions` ON `permissions`.id = `permissions_users`.permission_id WHERE (permissions.name = 'comment')
</pre>
<p>En otras palabras, podemos acceder a la descendencia de clases simplemente concatenándola al llamar al filtro. Otro ejemplo, con más niveles de descendencia, en donde los usuarios tienen un rol que a su vez tiene muchos permisos, y en donde queremos encontrar todos los usuarios que puedan comentar:</p>
<pre class="brush: ruby;">
@search = User.search
@search.role_permissions_name_is(&quot;comment&quot;)
@search.all # ejecuta la consulta
</pre>
<p>Si queremos resolver un problema de consultas N+1, también podemos especificar un INCLUDE en alguno de los métodos de búsqueda:</p>
<pre class="brush: ruby;">
@results = @search.all(:include =&gt; :permissions) # ejecuta dos consultas SQL
@results.first.permissions # no ejecuta ninguna consulta SQL
</pre>
<p>Las consultas SQL ejecutadas por la primera línea:</p>
<pre class="brush: sql;">
SELECT `users`.* FROM `users` INNER JOIN `permissions_users` ON `permissions_users`.user_id = `users`.id INNER JOIN `permissions` ON `permissions`.id = `permissions_users`.permission_id WHERE (permissions.name = 'comment')
SELECT `permissions`.*, t0.user_id as the_parent_record_id FROM `permissions` INNER JOIN `permissions_users` t0 ON `permissions`.id = t0.permission_id WHERE (t0.user_id IN (1,200))
</pre>
<h4>Procedimientos de Filtro (Scoped Procedures)</h4>
<p>En nuestra aplicación queremos filtrar a los usuarios que <em>no</em> sean del Reino Unido (UK) y cuya edad se encuentre entre los 12 y 20 años inclusive (es decir, que sean adolescentes). Supongamos que queremos hacer esto en muchos lugares de nuestra aplicación, pero volver a llamar a ambos filtros uno por uno, definitivamente, no es muy DRY.</p>
<p>En casos como este, podemos recurrir a los <em>Scope Procedures</em>, que básicamente significa que podemos darle un nombre a esa combinación de filtros y añadirla como un procedimiento de nuestro modelo User. Para el ejemplo, vamos a nombrar esa combinación de filtros <em>uk_teens</em> (&#8220;adolescentes del Reino Unido&#8221; en inglés):</p>
<pre class="brush: ruby;">
#Para crear el Scope Procedure
@search = User.search
@search.email_does_not_end_with(&quot;.co.uk&quot;)
@search.age_lt(20).age_gt(12)
User.scope_procedure :uk_teens, lambda{ @search }

#Otra forma de hacerlo, pero no tan limpia:
User.scope_procedure :uk_teens, lambda { User.email_does_not_end_with(&quot;.co.uk&quot;).age_lt(20).age_gt(12) }

# Finalmente, para utilizar el Scope Procedure
User.uk_teens.all # devuelve todos los resultados
User.search(:uk_teens =&gt; true) # lo mismo que lo anterior
</pre>
<p>Es importante notar el uso del método <em>lambda</em>, que lo que hace es recibir un bloque que va a ser ejecutado <em>cada vez</em> que llamamos al Scoped Procedure. Explicar esto a fondo escapa del ámbito de este tutorial. Otro elemento importante es el parámetro que le pasamos al método <em>search</em>, que le dice a Searchlogic que quiere utilizar el método <em>uk_teens</em> como si fuera un filtro más. Podemos utilizar la misma convención para llamar a cualquier <em>named_scope </em>que hayamos declarado manualmente.</p>
<p>También es importante advertir que el ejemplo en la documentación oficial <em>no funciona</em> (tal como está a la fecha de publicación de este artículo). El ejemplo en este artículo demuestra el uso correcto, a mi entender, de los Scoped Procedures.</p>
<h3>Conclusión</h3>
<p>En este tutorial, basado en la documentación oficial de Searchlogic, cubrimos los aspectos básicos de Searchlogic y algunos de los avanzados. Como fue mencionado antes, el plugin es muy flexible, y los usos que le podemos dar son tan variados como nuestra imaginación. Decidí no cubrir la parte de búsqueda con formularios, explicada en la documentación y en el <a title="Railscasts - Searchlogic" href="http://railscasts.com/episodes/176-searchlogic" target="_blank">Railscast de Searchlogic</a>, porque es útil sólo para casos muy específicos (vistas de administrador) y se va del eje central de este artículo.</p>
<p>Por favor, no dudes en dejar un rápido comentario ante cualquier duda, corrección o sugerencia! También podés <a href="../feed/" target="_blank">subscribirte al feed</a> o por email (en la barra de navegación) para recibir notificaciones de los últimos artículos.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/viarails.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/viarails.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/viarails.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/viarails.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/viarails.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/viarails.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/viarails.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/viarails.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/viarails.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/viarails.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/viarails.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/viarails.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/viarails.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/viarails.wordpress.com/168/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=168&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://viarails.wordpress.com/2010/01/05/buscando-con-searchlogic/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4a3fbfe8b5b31e7003508bc5ee947290?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Gabriel Somoza</media:title>
		</media:content>
	</item>
		<item>
		<title>Cómo Adjuntar Archivos a un Modelo en Rails</title>
		<link>http://viarails.wordpress.com/2009/11/29/como-adjuntar-archivos-a-un-modelo-en-rails/</link>
		<comments>http://viarails.wordpress.com/2009/11/29/como-adjuntar-archivos-a-un-modelo-en-rails/#comments</comments>
		<pubDate>Sun, 29 Nov 2009 13:11:56 +0000</pubDate>
		<dc:creator>Gabriel Somoza</dc:creator>
				<category><![CDATA[Plugins]]></category>
		<category><![CDATA[adjuntos]]></category>
		<category><![CDATA[archivos]]></category>
		<category><![CDATA[imágenes]]></category>
		<category><![CDATA[paperclip]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[thumbnails]]></category>
		<category><![CDATA[uploads]]></category>
		<category><![CDATA[Validaciones]]></category>

		<guid isPermaLink="false">http://viarails.wordpress.com/?p=122</guid>
		<description><![CDATA[Paperclip es un plugin muy fácil de instalar que nos permite adjuntar imágenes a nuestros modelos. En este tutorial veremos cómo permitirle a un usuario subir una foto para su perfil: Paperclip se encargará no sólo de guardar la foto en nuestro servidor, sino también de crear automáticamente un thumbnail (vista en miniatura) de esa imagen.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=122&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><a title="Paperclip en GitHub" href="http://github.com/thoughtbot/paperclip" target="_blank">Paperclip</a> es un plugin que permite adjuntar archivos <em>de todo tipo</em> a un modelo de ActiveRecord. Fue pensado para que su implementación sea lo más fácil posible y para que los archivos puedan ser tratados simplemente como un atributo más del modelo. Esto además significa que los cambios en el archivo son efectuados sólo una vez que <em>ActiveRecord::Base#save</em> es ejecutado, y que de la misma forma que con los demás atributos de ActiveRecord, también podemos realizar todo tipo de validaciones sobre el archivo.<span id="more-122"></span></p>
<p>En este tutorial veremos cómo permitirle a un usuario subir una foto para su perfil. Paperclip se encargará no sólo de guardar la foto en nuestro servidor, sino también de crear automáticamente un <em>thumbnail</em> (vista en miniatura) de esa imagen.</p>
<p>Para el procesar imágenes, Paperclip  utilizar una librería muy popular conocida como <em>ImageMagick</em>. Es esta librería la que nos permitirá generar los <em>thumbnails</em> para nuestros usuarios. Comencemos con los requisitos necesarios:</p>
<h2>1- Instalación de ImageMagick</h2>
<p>Este paso es necesario solamente para aquellos usuarios que <em>no tengan</em> ImageMagick instalado en su equipo, que por lo general son los usuarios de Windows. Las distribuciones de Linux y Mac tienen el paquete instalado por defecto, pero para evitar problemas en el futuro no estaría de más cerciorar que el paquete está presente en el sistema.</p>
<h3>Windows</h3>
<p>La instalación en Windows es muy simple: hay que descargar <a title="Instaladores de ImageMagick para Windows" href="http://www.imagemagick.org/script/binary-releases.php#windows" target="_blank">uno de los instaladores</a> (ante la duda, el <em>Q16-windows-dll</em> funciona en la mayoría de los sistemas) y ejecutarlo. Una vez dentro del instalador, activar la opción &#8220;<em>Update executable search path</em>&#8221; para que la librería pueda ser accedida desde todo el sistema.</p>
<h3>Ubuntu</h3>
<p>Para instalar ImageMagick en Ubuntu o similares (si aún no está instalado), correr el siguiente comando en la consola: <em>sudo apt-get install imagemagick. </em>Si alguien necesita información de cómo hacerlo en otra distribución, dejen un comentario y voy a tratar de responder lo más pronto posible.</p>
<h2>2- Instalación de Paperclip</h2>
<p>Instalamos Paperclip de la misma forma que con cualquier otro plugin:</p>
<pre class="brush: plain;">
script/plugin install git://github.com/thoughtbot/paperclip.git
</pre>
<p>Para aquellos que prefieran instalarlo como un <em>gem</em>, agregamos en nuestro <em>environment.rb</em>:</p>
<pre class="brush: ruby;">
config.gem 'paperclip', :source =&gt; 'http://gemcutter.org'
</pre>
<p>Y luego corremos en la consola <em>rake gems:install</em>, para instalar Paperclip en el entorno.</p>
<h2>3- Preparar la Base de Datos</h2>
<p>Supongamos que ya tenemos un modelo <em>User</em> en donde reside la información sobre un usuario de nuestro sitio. Lo que queremos hacer es brindarle al usuario la posibilidad de subir una imagen de perfil (foto, avatar, etc) a nuestro sitio. Esta imagen va a ser accedida por medio de un atributo de <em>User</em> que llamaremos <em>picture</em>. Por suerte, Paperclip nos ayuda con un <em>generador</em>, que facilitará mucho nuestra tarea.</p>
<p>En la consola:</p>
<pre class="brush: plain;">
script/generate paperclip user picture
</pre>
<p>El generador toma dos parámetros: el primero  indica el modelo al que le queremos adjuntar una imagen, en nuestro caso <em>user.</em> El segundo indica el nombre que le daremos (dentro del modelo) a esta imagen adjunta, y aquí utilizamos <em>picture</em>. Lo que hace este generador es crear un nuevo archivo de migración para agregar cuatro campos nuevos a nuestro modelo <em>User</em>:</p>
<pre class="brush: ruby;">
#db/migrate/20091129014122_add_attachments_picture_to_user.rb
class AddAttachmentsPictureToUser &lt; ActiveRecord::Migration
  def self.up
    add_column :users, :picture_file_name, :string
    add_column :users, :picture_content_type, :string
    add_column :users, :picture_file_size, :integer
    add_column :users, :picture_updated_at, :datetime
  end

  def self.down
    remove_column :users, :picture_file_name
    remove_column :users, :picture_content_type
    remove_column :users, :picture_file_size
    remove_column :users, :picture_updated_at
  end
end
</pre>
<p>A esta altura debemos correr <em>rake db:migrate</em> para que los cambios se apliquen a la base de datos.</p>
<h2>4- Adjuntar Paperclip al Modelo</h2>
<p>Una vez hechos los cambios en la base de datos, necesitamos indicarle a nuestro modelo <em>User</em> que queremos un archivo adjunto con nombre <em>picture</em>. Paperclip detectará y utilizará automáticamente los campos que acabamos de agregar a la base de datos. Para hacerlo, agregamos lo siguiente en nuestro modelo:</p>
<pre class="brush: ruby;">
#app/models/user.rb
class User &lt; ActiveRecord::Base
  # Paperclip
  has_attached_file :picture,
    :styles =&gt; {
      :thumb=&gt; &quot;100x100#&quot;,
      :small  =&gt; &quot;150x150&gt;&quot; }
end
</pre>
<p>Es importante mencionar que Paperclip aún no sabe que sólo queremos permitir al usuario subir imágenes. Pero más allá de eso, hay un par de parámetros que nos interesan.</p>
<p>El primero es <span style="color:#ff1493;">:picture</span>, que le indica a Paperclip el nombre del archivo adjunto. Es importante que coincida con el nombre que decidimos darle en la base de datos en el paso anterior.</p>
<p>El segundo parámetro es <span style="color:#ff1493;">:styles</span>. Es utilizado sólamente cuando estamos tratando con imágenes, porque no tiene efecto sobre ningún otro tipo de archivos. Este parámetro toma un <em>Hash</em> de claves y sus correspondientes valores. Cada clave define una nueva imagen que se va a generar a partir de la imagen original, y su valor indica qué formato va a tener. Para ver qué formas puede tomar el valor, referirse a la documentación  de ImageMagick. En este caso en particular, se guardarán tres imágenes: la original, una  <em>thumb</em> (de tamaño 100&#215;100 y recortada en los bordes) y otra <em>small</em> (de tamaño máximo 150&#215;150). Para ver cómo modificar el nombre y la ruta donde las imágenes son almacenadas del lado servidor, referirse a la documentación de <a title="Paperclip Interpolations" href="http://wiki.github.com/thoughtbot/paperclip/interpolations" target="_blank">Paperclip Interpolations</a>.</p>
<p>Si queremos evitar que se guarde la imagen original, podemos definir un estilo que se llame <span style="color:#ff1493;">:original</span> y Paperclip guardará la imagen con este nuevo formato, descartando la imagen original. Esto es especialmente útil si queremos evitar guardar imágenes muy pesadas en nuestro servidor.</p>
<p>Por último, los estilos también pueden ser <em>Procs</em> (una de las características interesantes de Ruby). Por ejemplo, si le queremos permitir al usuario definir un ancho y un alto para su imagen (y tenemos dichos valores guardados en los atributos <em>picture_width </em>y<em> picture_height</em> de nuestro modelo, respectivamente):</p>
<pre class="brush: ruby;">
has_attached_file :picture,
  :styles =&gt; {
    :thumb=&gt; &quot;100x100#&quot;,
    :small  =&gt; &quot;150x150&gt;&quot; }
    :custom =&gt; Proc.new {
      |user| # devuelve la instancia actual del usuario
      &quot;#{user.photo_width}x#{user.photo_height}&gt;&quot; }
  }
</pre>
<p>Sin embargo, en nuestro ejemplo no necesitamos hacer esto, así que nos quedaremos con los estilos <em>thumb</em> y <em>small</em>.</p>
<h2>5- Definir Validaciones</h2>
<p>Las validaciones de Paperclip son una de sus  características más útiles. El plugin nos provee métodos especiales para algunas de las validaciones más comunes, aunque también podemos agregar manualmente las que nos parezca conveniente a través del método <em>validate </em>de ActiveRecord.</p>
<p>Todos las validaciones toman dos parámetros: (<em>nombre</em>, <em>opciones</em> = {}). El primero especifica a qué archivo adjunto queremos aplicar la validación. Esto es sobre todo útil si el modelo que utilizamos debe tener más de un archivo adjunto. El segundo parámetro es un <em>Hash </em>en donde debemos, en la mayoría de los casos, especificar ciertos valores que le darán sentido a la validación. El Hash de opciones admite algunas opciones que son comunes tanto para este tipo de validación de Paperclip como para el resto de las validaciones de ActiveRecord:</p>
<p style="padding-left:30px;"><span style="color:#ff1493;">:if</span> =&gt; Acepta una condición para la validación (importante recordar: de la misma forma que lo hacen las validaciones de ActiveRecord). Ejecuta la validación si la condición evalúa a verdadero.</p>
<p style="padding-left:30px;"><span style="color:#ff1493;">:unless</span> =&gt; Lo mismo que lo anterior, sólo que la validación <em>no</em> <em>se ejecuta </em>si la condición evalúa a verdadero.</p>
<p style="padding-left:30px;"><span style="color:#ff1493;">:message</span> =&gt; Define el mensaje que se mostrará si el archivo adjunto no pasa la validación. Es útil si necesitamos traducir los mensajes de error al Español (u otro idioma), pero si estamos trabajando en inglés los mensajes suelen ser bastante descriptivos.</p>
<p>Los métodos que nos provee Paperclip para validar los archivos adjuntos son:</p>
<p style="padding-left:30px;"><strong>validates_attachment_presence</strong><em> </em>(nombre, opciones = {})<br />
Funciona igual que el <em>validates_presence_of</em> de ActiveRecord: el modelo no se podrá guardar siempre que no haya un archivo adjunto (al campo especificado por <em>nombre</em>).</p>
<p style="padding-left:30px;"><strong>validates_attachment_size</strong><em> </em>(nombre, opciones = {})<br />
Valida el tamaño del archivo adjunto. Debemos indicar cómo queremos validarlo a través de las <em>opciones.</em> Algunas opciones de ejemplo son (hay que utilizar sólo una de ellas):</p>
<pre class="brush: ruby;">
:less_than =&gt; 5.megabytes # menos de 5 Mb
:greater_than =&gt; 500.kilobytes # más de 5 Mb
:in =&gt; (100..5.megabytes) # de 100 bytes hasta 5 Mb
</pre>
<p style="padding-left:30px;"><strong>validates_attachment_content_type </strong>(nombre, opciones = {})<br />
Valida que el archivo adjunto pertenezca a los tipos MIME que especificaremos en las <em>opciones </em>a través de la clave <span style="color:#ff1493;">:content_type</span>. Un ejemplo:</p>
<pre class="brush: ruby;">
:content_type =&gt; ['image/jpeg', 'image/png']
</pre>
<p>Con esto en mente, podemos agregarle las validaciones que necesitamos a nuestro modelo <em>User.</em> No utilizaremos <em>validates_attachment_presence</em> porque queremos que el usuario pueda decidir tener una imagen de perfil o no.</p>
<pre class="brush: ruby;">
#app/models/user.rb
class User &lt; ActiveRecord::Base
  # Paperclip
  has_attached_file :picture,
    :styles =&gt; {
      :thumb=&gt; &quot;100x100#&quot;,
      :small  =&gt; &quot;150x150&gt;&quot; }
  # Validaciones de Paperclip
  validates_attachment_size :picture, :less_than =&gt; 2.megabytes
  validates_attachment_content_type :picture, :content_type =&gt; ['image/jpeg', 'image/png']
end
</pre>
<h2>6- Las Vistas</h2>
<p>Ya casi terminando, veremos que tanto en las Vistas como en los Controladores podemos tratar al modelo como estamos acostumbrados. Un formulario de ejemplo que permite a un usuario subir su foto de perfil:</p>
<pre class="brush: ruby;">
&lt;!-- app/views/user/new.html.erb --&gt;
&lt;% form_for @user, :html =&gt; { :multipart =&gt; true } do |form| %&gt;
  &lt;ol&gt;
    &lt;!-- Otros campos del usuario ... --&gt;
    &lt;li&gt;
      &lt;%= form.label :picture, &quot;Picture&quot; %&gt;
      &lt;%= form.file_field :picture %&gt;
    &lt;li&gt;
      &lt;%= form.submit &quot;Save Picture!&quot; %&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;% end %&gt;
</pre>
<p>Como se puede observar, tratamos a nuestro campo <em>picture</em> de Paperclip como si fuera un <em>file_field</em>. Cuando el usuario seleccione una imagen y envíe el formulario al servidor, Paperclip sabrá qué hacer con la imagen y hará todo el trabajo por nosotros. Lo único que tenemos que hacer que puede ser diferente a lo que hayamos hecho hasta ahora, es decirle a Rails que queremos un formulario <em>multipart</em>. Esto es necesario <em>siempre</em> que queremos subir imágenes o archivos (además de datos comunes) en un formulario HTML.</p>
<p>Para mostrar la imagen en cualquier vista podemos utilizar el método <em>url</em> de nuestro objeto Paperclip. Este método devuelve una ruta a la imagen original (sin parámetros) o a las secundarias (con el parámetro apropiado):</p>
<pre class="brush: ruby;">
&lt;%= image_tag @user.picture.url %&gt;
&lt;%= image_tag @user.picture.url(:thumb) %&gt;
&lt;%= image_tag @user.picture.url(:small) %&gt;
</pre>
</pre>
<h2>7- El Controlador</h2>
<p>El controlador es lo más fácil, porque <em>si lo hicimos correctamente</em>, no tenemos que cambiar absolutamente nada de código para que Paperclip funcione:</p>
<pre class="brush: ruby;">
#app/controllers/users_controller.rb
class UsersController &lt; ApplicationController
  #...

  def new
    @user = User.new
  end

  def create
    @user = User.create(params[:user])
  end

  def update
    @user = current_user
    if @user.update_attributes(params[:user])
      flash[:info] = &quot;Sus datos fueron actualizados con éxito&quot;
      redirect_to user_path(@user)
    else
      render :action =&gt; :edit
    end
  end

  def edit
    @user = current_user
  end

  private

  def current_user
    User.find_by_id(session['user_id'])
  end
end
</pre>
</pre>
<p>Es importante destacar nuevamente que este código es genérico y puede ser utilizado con o sin Paperclip. Tanto en la acción <em>create</em> como en <em>update</em>, nuestros archivos Paperclip son validados y guardados dentro de <em>ActiveRecord::Base#save!</em>. Esto ocurre tanto dentro del método <em>create</em> como del método <em>update_attributes</em>.</p>
<h2>Conclusión</h2>
<p>Vimos un ejemplo sencillo de cómo utilizar Paperclip para adjuntar imágenes a nuestros modelos. Vale la pena repetir que Paperclip puede manejar no sólo imágenes, sino cualquier tipo de archivos. Para usos más avanzados de este plugin, referirse a la <a href="http://dev.thoughtbot.com/paperclip/" target="_blank">documentación</a>.</p>
<p>En el futuro seguramente estaré escribiendo más artículos sobre Paperclip. Si le gustó este artículo, puede subscribirse a mi <a title="RSS Feed" href="http://viarails.wordpress.com/feed/" target="_blank">RSS Feed</a> para ser notificado en cuanto salga uno nuevo. También hay un formulario al costado de esta página en donde puede subscribir su email para que las notificaciones le lleguen a su casilla de correo.</p>
<p>Si hay algún error en este artículo, sugerencias, o simplemente ha sido útil, no dude de comentar!</p>
<h2>Recursos</h2>
<p>Parte de este artículo está basado en los siguientes recursos disponibles en Inglés:</p>
<ul>
<li><a href="http://github.com/thoughtbot/paperclip/" target="_blank">Repositorio Git de Paperclip</a></li>
<li><a href="http://jimneath.org/2008/04/17/paperclip-attaching-files-in-rails/" target="_blank">Paperclip: Attaching Files in Rails</a> (de principios del 2008)</li>
<li><a href="http://thewebfellas.com/blog/2008/11/2/goodbye-attachment_fu-hello-paperclip" target="_blank">Goodbye Attachment Fu, hello Paperclip</a></li>
</ul>
<p>&nbsp;</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/viarails.wordpress.com/122/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/viarails.wordpress.com/122/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/viarails.wordpress.com/122/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/viarails.wordpress.com/122/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/viarails.wordpress.com/122/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/viarails.wordpress.com/122/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/viarails.wordpress.com/122/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/viarails.wordpress.com/122/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/viarails.wordpress.com/122/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/viarails.wordpress.com/122/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/viarails.wordpress.com/122/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/viarails.wordpress.com/122/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/viarails.wordpress.com/122/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/viarails.wordpress.com/122/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=122&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://viarails.wordpress.com/2009/11/29/como-adjuntar-archivos-a-un-modelo-en-rails/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4a3fbfe8b5b31e7003508bc5ee947290?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Gabriel Somoza</media:title>
		</media:content>
	</item>
		<item>
		<title>Cómo Autenticar Usuarios con Authlogic</title>
		<link>http://viarails.wordpress.com/2009/11/19/como-autenticar-usuarios-con-authlogic/</link>
		<comments>http://viarails.wordpress.com/2009/11/19/como-autenticar-usuarios-con-authlogic/#comments</comments>
		<pubDate>Thu, 19 Nov 2009 22:40:47 +0000</pubDate>
		<dc:creator>Gabriel Somoza</dc:creator>
				<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Usuarios]]></category>
		<category><![CDATA[autenticación]]></category>
		<category><![CDATA[authlogic]]></category>
		<category><![CDATA[permisos]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[usarios]]></category>

		<guid isPermaLink="false">http://viarails.wordpress.com/?p=96</guid>
		<description><![CDATA[El plugin Authlogic (de BinaryLogic) es un excelente recurso a la hora de facilitar la autenticación de usuarios en nuestros sitios web. Se caracteriza por ser muy fácil de configurar, utilizar y extender (existen otros plugins que le agregan funcionalidad, como Lockdown). Por supuesto, tiene un muy buen nivel de seguridad y muchas funcionalidades extremadamente [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=96&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p style="text-align:justify;">El plugin <a href="http://github.com/binarylogic/authlogic" target="_blank">Authlogic</a> (de <a href="http://www.binarylogic.com/" target="_blank">BinaryLogic</a>) es un excelente recurso a la hora de facilitar la autenticación de usuarios en nuestros sitios web. Se caracteriza por ser muy fácil de configurar, utilizar y extender (existen otros plugins que le agregan funcionalidad, como <a href="http://github.com/stonean/lockdown" target="_blank">Lockdown</a>). Por supuesto, tiene un muy buen nivel de seguridad y muchas funcionalidades extremadamente útiles que, de hacerlas a mano, nos tomaría mucho tiempo implementar.<span id="more-96"></span></p>
<p style="text-align:justify;">Este tutorial está basado en el <a href="http://github.com/binarylogic/authlogic_example" target="_blank">sitio de ejemplo oficial</a> de Authlogic, en GitHub.</p>
<h3 style="text-align:justify;">1- Instalación</h3>
<p style="text-align:justify;">Authlogic se puede instalar como gem o como plugin.</p>
<p style="text-align:justify;">A) Para instalar como plugin (recomendado por ellos mismos):</p>
<pre class="brush: plain;">
$ ./script/plugin install git://github.com/binarylogic/authlogic.git
</pre>
<p style="text-align:justify;">B) Para instalar como gem, basta agregar lo siguiente a environment.rb</p>
<pre class="brush: ruby;">
# config/environment.rb
config.gem &quot;authlogic&quot;
</pre>
<p style="text-align:justify;">Luego, ejecutar en la consola (si usás linux quizás necesites correrlo con <em>sudo</em>):</p>
<pre class="brush: plain;">
rake gems:install
</pre>
<h3 style="text-align:justify;">2- Crear el modelo UserSession</h3>
<p style="text-align:justify;">El modelo UserSession es aquel que se va a encargar de administrar todo lo que tiene que ver con una sesión en nuestro sitio: su creación (login), su actualización, y su destrucción (logout). Como ellos mismos comentan en su sitio de ejemplo, de la misma forma que ActiveRecord se sitúa entre nosotros y la base de datos, el modelo UserSession se situá entre nosotros y las cookies del usuario.</p>
<p style="text-align:justify;">Asumiendo que lo que queremos autenticar es un modelo nombrado <em>User</em>, vamos a crear nuestro modelo UserSession. En la consola:</p>
<pre class="brush: plain;">
script/generate session user_session
</pre>
<p style="text-align:justify;">Esto va a crear un archivo similar al siguiente:</p>
<pre class="brush: ruby;">
#app/models/user_session.rb
class UserSession &lt; Authlogic::Session::Base
  # aquí va la configuración
end
</pre>
<p style="text-align:justify;">Es importante destacar que este modelo hereda de <span style="font-family:Consolas,'Bitstream Vera Sans Mono','Courier New',Courier,monospace!important;">Authlogic::Session::Base</span>, lo cual nos permitirá configurarlo muy fácilmente. Por ejemplo, es aquí donde podemos proteger a nuestros usuarios de ataques de fuerza bruta. Para más información, ver la <a href="http://rdoc.info/projects/binarylogic/authlogic" target="_blank">documentación</a> de los submódulos de <span style="font-family:Consolas,'Bitstream Vera Sans Mono','Courier New',Courier,monospace!important;">Authlogic::Session</span>.</p>
<h3 style="text-align:justify;">3- Crear el modelo User</h3>
<p style="text-align:justify;">El modelo <em>User</em> es el modelo que guarda la información de registro de los usuarios. Se pueden incluir todos los campos que sean necesarios para nuestra aplicación, pero existen algunos campos que Authlogic administra de forma automática, facilitando muchísimo nuestro trabajo. Pero primero, creemos el modelo: en la consola,</p>
<pre class="brush: plain;">
script/generate model user login:string crypted_password:string
</pre>
<p style="text-align:justify;">Authlogic utilizará este modelo para autenticar. El tutorial oficial (en inglés) incluye una lista que muestra cuáles son los campos administrados por Authlogic. Todos los campos son opcionales excepto el que se utiliza para que la sesión persista: <em>persistence_token</em>. Para este tutorial utilizaremos una combinación de nombre de usuario y contraseña para autenticar. Como fueron especificados como parámetros, ambos campos (<em>login</em> y <em>crypted_password</em>, respectivamente) fueron añadidos automáticamente por Rails a nuestro archivo de migración cuando corrimos el generador anterior. Por lo tanto, podemos directamente ejecutar <span style="font-family:Consolas,'Bitstream Vera Sans Mono','Courier New',Courier,monospace!important;">rake db:migrate</span> para crear la tabla <em>users</em> junto con dichos campos en nuestra base de datos.</p>
<h3 style="text-align:justify;">4- Configurar el modelo</h3>
<p style="text-align:justify;">Configurar el modelo es extremadamente simple:</p>
<pre class="brush: ruby;">
class User &lt; ActiveRecord::Base
  acts_as_authentic do |c|
    c.opcion = valor # ver documentación
  end # el bloque es opcional
end
</pre>
<p style="text-align:justify;">A partir de ahora el modelo intentará validar automáticamente los campos <em>nombre de usuario</em>, <em>email</em>, <em>contraseña</em>, etc. La configuración en bloque nos permite personalizar cómo queremos que se validen esos campos, definir nombres distintos para los campos o incluso cambiar el algoritmo de encriptación para la contraseña.</p>
<h3 style="text-align:justify;">5- Crear el Controlador para UserSessions</h3>
<p style="text-align:justify;">Es a través de este controlador que el usuario iniciará y cerrará su sesión. Es importante aclarar que este controlador <em>no debería </em>ser usado para registrar usuarios u otras acciones similares que tienen que ver con la administración de usuarios; lo utilizaremos simplemente para iniciar o cerrar sesión. En la consola:</p>
<pre class="brush: plain;">
script/generate controller user_sessions
</pre>
<p style="text-align:justify;"><a href="http://github.com/binarylogic/authlogic_example/blob/5819a13477797d758cb6871f475ed1c54bf8a3a7/app/controllers/user_sessions_controller.rb" target="_blank">Aquí</a> hay un ejemplo del contenido básico de un controlador de UserSessions. También existen ejemplos paras las <a href="http://github.com/binarylogic/authlogic_example/tree/5819a13477797d758cb6871f475ed1c54bf8a3a7/app/views/user_sessions" target="_blank">vistas</a> correspondientes. Sin cambiar lo básico, podemos agregar toda la funcionalidad que queramos, tanto a las vistas como al controlador.</p>
<p style="text-align:justify;">En cuanto a las rutas, hace falta conectar el modelo UserSession con el controlador. Para lograrlo:</p>
<pre class="brush: ruby;">
#config/routes.rb
map.resource :user_session
</pre>
<h3 style="text-align:justify;">6- Persistiendo Sesiones</h3>
<p style="text-align:justify;">Para persistir sesiones utilizaremos el ApplicationController. Agregaremos algunos métodos al controlador y a dos de ellos los convertiremos en métodos <em>helper</em>, para poder accederlos desde las vistas. Estos métodos fueron extraídos del sitio ejemplo, son simplemente ilustrativos y pueden ser personalizados según la necesidad.</p>
<pre class="brush: ruby;">
# app/controllers/application.rb
class ApplicationController &lt; ActionController::Base
  helper :all
  filter_parameter_logging :password, :password_confirmation
  helper_method :current_user_session, :current_user

  private
  def current_user_session
    return @current_user_session if defined?(@current_user_session)
    @current_user_session = UserSession.find
  end

  def current_user
  return @current_user if defined?(@current_user)
    @current_user = current_user_session &amp;&amp; current_user_session.user
  end

  def require_user
     unless current_user
       store_location
       flash[:notice] = &quot;You must be logged in to access this page&quot;
       redirect_to new_user_session_url
       return false
     end
   end

   def require_no_user
     if current_user
       store_location
       flash[:notice] = &quot;You must be logged out to access this page&quot;
       redirect_to account_url
       return false
     end
   end

   def store_location
     session[:return_to] = request.request_uri
   end

   def redirect_back_or_default(default)
     redirect_to(session[:return_to] || default)
     session[:return_to] = nil
   end
end
</pre>
<p style="text-align:justify;">Ahora tendremos acceso al usuario y su sesión en toda nuestra aplicación, con sólo llamar a <span style="font-family:Consolas,'Bitstream Vera Sans Mono','Courier New',Courier,monospace!important;">current_user</span> y <span style="font-family:Consolas,'Bitstream Vera Sans Mono','Courier New',Courier,monospace!important;">current_user_session</span>, respectivamente.</p>
<h3 style="text-align:justify;">7- Restringiendo Acceso</h3>
<p style="text-align:justify;">Authlogic se encarga simplemente de la autenticación, pero nos permite restringir acceso en nuestros controladores de forma muy fácil. Lo podemos hacer a mano o utilizando algún otro plugin que agregue esta funcionalidad. <a href="http://github.com/stonean/lockdown" target="_blank">Lockdown</a> es un plugin muy bueno que permite restringir acceso según los roles que tenga un usuario, y se integra de forma excelente con Authlogic.</p>
<p style="text-align:justify;">Sin embargo, aquí incluiremos un ejemplo muy simple de cómo restringir acceso manualmente dentro de uno de nuestros controladores. En nuestro ApplicationControler:</p>
<pre class="brush: ruby;">
# app/controllers/application.rb
class ApplicationController &lt; ActionController::Base

  #... (código anterior)

  def require_user
     unless current_user
       store_location
       flash[:notice] = &quot;You must be logged in to access this page&quot;
       redirect_to new_user_session_url
       return false
     end
   end

   def require_no_user
     if current_user
       store_location
       flash[:notice] = &quot;You must be logged out to access this page&quot;
       redirect_to account_url
       return false
     end
   end

   def store_location
     session[:return_to] = request.request_uri
   end

   def redirect_back_or_default(default)
     redirect_to(session[:return_to] || default)
     session[:return_to] = nil
   end
end
</pre>
<p style="text-align:justify;">Y luego utilizamos estos nuevo métodos en nuestros controladores. Por ejemplo, si queremos que una lista de productos pueda ser vista por cualquier persona, pero que sólo usuarios logueados puedan administrarla (crear productos, actualizarlos, borrarlos, etc.), hacemos lo siguiente en nuestro ProductsController (asumiendo que existe):</p>
<pre class="brush: ruby;">
# app/controllers/products_controller.rb
class ProductsController &lt; ActionController::Base
  before_filter :require_user, :except =&gt; [:index, :show]

  #GET /products/new
  def new
    #...
  end

  # ... (resto del código)
end
</pre>
<p style="text-align:justify;">De esta forma, si una visita intenta añadir un producto accediendo a <em>&#8220;sitio.com/products/new&#8221;</em>, será redirigida automáticamente al formulario de <em>login</em> para que se identifique con su usuario antes de poder continuar. Esta lógica se encuentra en el ApplicationController, dentro del método <span style="font-family:Consolas,'Bitstream Vera Sans Mono','Courier New',Courier,monospace!important;">require_user</span>.</p>
<h3 style="text-align:justify;">8- Registrando Usuarios</h3>
<p style="text-align:justify;">Registrar usuarios es muy simple con Authlogic. De hecho, funciona exactamente igual que si no lo usáramos, siempre y cuando utilicemos las acciones CRUD estándar. Esto es debido a que Authlogic se inyecta automáticamente en el modelo, y por lo tanto no nos tenemos que preocupar en absoluto de nada que tenga que ver con lógica que hay detrás de registrar un usuario. Es más, tampoco es necesario preocuparnos de encriptar la contraseña antes de almacenarla en la base de datos. Authlogic se encarga de <em>todo</em>. Y si lo que buscamos es un poco más avanzado, Authlogic es muy fácil de configurar y extender (ver la documentación).</p>
<h3 style="text-align:justify;">9- Próximos Pasos</h3>
<p style="text-align:justify;">La gente de BinaryLogic incluye una lista de próximos pasos que pueden o no ser implementados. Todos remiten a una sección en particular dentro de la <a href="http://rdoc.info/projects/binarylogic/authlogic" target="_blank">documentación</a> de Authlogic. Aquí la incluyo traducida:</p>
<ol style="text-align:justify;">
<li style="text-align:left;">Querés usar otro algoritmo de encriptación, como BCrypt? Ver Authlogic::ActsAsAuthentic::Password::Config</li>
<li style="text-align:left;">Migrando de autenticación REST? Ver Authlogic::ActsAsAuthentic::RestfulAuthentication::Config</li>
<li style="text-align:left;">Querés que las sesiones expiren automáticamente después de un período de inactividad? Ver Authlogic::Session::Timeout</li>
<li style="text-align:left;">Necesitás restringir tus sesiones a una cuenta o modelo en particular? Ver Authlogic::AuthenticatesMany</li>
<li style="text-align:left;">Necesitás muchos tipos diferentes de sesiones? Ver Authlogic::Session::Id</li>
<li style="text-align:left;">Querés permitir que los usuarios reestablezcan sus contraseñas o confirmen su subscripción? Utilize una clave de expiración automática. Ver Authlogic::ActsAsAuthentic::PerishableToken</li>
<li style="text-align:left;">Querés dar acceso privado a una API o  canal RSS? Utilice autenticación básica por HTTP o por parámetros. Ver Authlogic::Session::HttpAuth o Authlogic::Session::Params</li>
<li style="text-align:left;">Necesitás ayuda con tus pruebas? Consulta Authlogic::TestCase</li>
</ol>
<h3 style="text-align:justify;">Créditos</h3>
<p style="text-align:justify;">Este tutorial está estructuralmente basado en el <a href="http://github.com/binarylogic/authlogic_example" target="_blank">tutorial oficial</a> de Authlogic (en inglés). Mucho del código lo extraje de allí directamente, aunque me tomé libertades en cuanto a su presentación y en algunos casos le cambié en formas que me parecieron más convenientes.</p>
<p style="text-align:justify;">Si este tutorial fue de provecho, no dudes en comentar! Lo mismo si encontraste errores o quisieras dar alguna sugerencia.</p>
<p style="text-align:justify;">También podés <a href="http://viarails.wordpress.com/feed/" target="_blank">subscribirte al feed</a> para mantenerte al tanto de los artículos más frescos.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/viarails.wordpress.com/96/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/viarails.wordpress.com/96/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/viarails.wordpress.com/96/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/viarails.wordpress.com/96/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/viarails.wordpress.com/96/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/viarails.wordpress.com/96/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/viarails.wordpress.com/96/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/viarails.wordpress.com/96/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/viarails.wordpress.com/96/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/viarails.wordpress.com/96/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/viarails.wordpress.com/96/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/viarails.wordpress.com/96/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/viarails.wordpress.com/96/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/viarails.wordpress.com/96/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=96&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://viarails.wordpress.com/2009/11/19/como-autenticar-usuarios-con-authlogic/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4a3fbfe8b5b31e7003508bc5ee947290?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Gabriel Somoza</media:title>
		</media:content>
	</item>
		<item>
		<title>Optimizando el uso de la Base de Datos: Relaciones</title>
		<link>http://viarails.wordpress.com/2009/11/10/optimizando-el-uso-de-la-base-de-datos-relaciones/</link>
		<comments>http://viarails.wordpress.com/2009/11/10/optimizando-el-uso-de-la-base-de-datos-relaciones/#comments</comments>
		<pubDate>Tue, 10 Nov 2009 16:30:31 +0000</pubDate>
		<dc:creator>Gabriel Somoza</dc:creator>
				<category><![CDATA[Base de Datos]]></category>
		<category><![CDATA[Optimización]]></category>
		<category><![CDATA[permisos]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[relaciones]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[Usuarios]]></category>

		<guid isPermaLink="false">http://viarails.wordpress.com/?p=77</guid>
		<description><![CDATA[Ayer estuve tratando de optimizar el uso de la base de datos en uno de nuestros CMS. Observamos que cierta sección del sitio estaba haciendo alrededor de 60 queries a la base de datos por cada pedido de página. Aunque el volumen de la información era relativamente pequeño, el tiempo para encontrarla representaba el 44% [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=77&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Ayer estuve tratando de optimizar el uso de la base de datos en uno de nuestros CMS. Observamos que cierta sección del sitio estaba haciendo alrededor de 60 queries a la base de datos <em>por cada pedido</em> de página. Aunque el volumen de la información era relativamente pequeño, el tiempo para encontrarla representaba el 44% del tiempo que el servidor tardaba en responder a cada pedido, que en ese momento promediaba los 0.6 segundos.<span id="more-77"></span></p>
<p>En poco tiempo pudimos detectar el origen del problema: un layout en el lado del administrador que mostraba unos 15 links. El administrador tenía permisos asignados a su usuario, de modo que si no tenía permiso para acceder a cierta sección, el link no se mostraba. El layout hacía un query a la base de datos para determinar los permisos para cada link! El código original era algo como lo siguiente:</p>
<pre class="brush: ruby;">
# app/views/layouts/admin.html.erb
&lt;% if User.find(session['user_id']).permissions.include?(Permission.find(ACCESS_PRODUCTS)) %&gt;
   &lt;li&gt;&lt;%= link_to 'Products', admin_products_path %&gt;&lt;/li&gt;
&lt;% end %&gt;
# ... y esto se repetía 20 veces, una por cada link.
</pre>
<p>En la condición anterior sólamente se ejecutan 3 queries en la base de datos: una para buscar el usuario, otra para buscar el permiso ACCESS_PRODUCTS, y otra para ver si el usuario tiene ese permiso. Eso acababa generando 60 queries por los 20 links que había en el menú.</p>
<p>Lo que hicimos para mejorarlo:<br />
1) Cargar al usuario, juntamente con sus condiciones, en un sólo query.<br />
2) Utilizar otro método para saber si el usuario tiene o no cierto permiso.</p>
<p>Lo importante de este caso no es analizar la implementación de la funcionalidad de permisos, ni la de usuarios; definitivamente se puede utilizar un acercamiento más efectivo a este problema. Por esta razón, aprovecho para recomendar los plugins <em><a href="http://github.com/binarylogic/authlogic">authlogic</a></em> y <em><a href="http://github.com/stonean/lockdown">lockdown</a></em>, que solucionan muy elegante y eficientemente todo lo que tiene que ver con la autenticación y permisos de usuarios.</p>
<p>El resultado:</p>
<pre class="brush: ruby;">
# en un before_filter del controlador
@current_user = User.find(session['user_id'], :include =&gt; :permissions)

# app/views/layouts/admin.html.erb
&lt;% if @current_user.permissions.exists?(ACCESS_PRODUCTS) %&gt;
   &lt;li&gt;&lt;%= link_to 'Products', admin_products_path %&gt;&lt;/li&gt;
&lt;% end %&gt;
</pre>
<p>Esta solución hace un sólo query a la base de datos antes de cargar el layout. La técnica se conoce en inglés como <em>Eager Loading</em>, que significa algo como &#8220;Carga Entusiasta&#8221; (de los datos pertenecientes a una relación). Lo que logramos es que el usuario y los permisos se carguen al mismo tiempo utilizando un JOIN en la llamada SQL. Una vez en memoria, toma poco y nada comprobar si existe un permiso en particular para determinado link.</p>
<p>El tiempo de respuesta mejoró en un 20%, y se hicieron 59 queries menos a la base de datos.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/viarails.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/viarails.wordpress.com/77/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/viarails.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/viarails.wordpress.com/77/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/viarails.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/viarails.wordpress.com/77/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/viarails.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/viarails.wordpress.com/77/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/viarails.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/viarails.wordpress.com/77/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/viarails.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/viarails.wordpress.com/77/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/viarails.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/viarails.wordpress.com/77/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=77&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://viarails.wordpress.com/2009/11/10/optimizando-el-uso-de-la-base-de-datos-relaciones/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4a3fbfe8b5b31e7003508bc5ee947290?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Gabriel Somoza</media:title>
		</media:content>
	</item>
		<item>
		<title>Emails con Adjuntos</title>
		<link>http://viarails.wordpress.com/2009/08/03/emails-con-adjuntos/</link>
		<comments>http://viarails.wordpress.com/2009/08/03/emails-con-adjuntos/#comments</comments>
		<pubDate>Mon, 03 Aug 2009 16:04:11 +0000</pubDate>
		<dc:creator>Gabriel Somoza</dc:creator>
				<category><![CDATA[Mailing]]></category>
		<category><![CDATA[adjuntos]]></category>
		<category><![CDATA[archivo]]></category>
		<category><![CDATA[attachment_fu]]></category>
		<category><![CDATA[emails]]></category>
		<category><![CDATA[mailer]]></category>
		<category><![CDATA[paperclip]]></category>

		<guid isPermaLink="false">http://viarails.wordpress.com/?p=65</guid>
		<description><![CDATA[Mandar emails con archivos adjuntos es muy facil con Rails y attachment_fu (o PaperClip). Hace poco tuve que hacerlo para una nueva sección en uno de nuestros sitios. La idea era que el usuario subiera una foto al servidor, donde la información se procesa y se manda por email a un administrador, con la foto [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=65&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p style="text-align:justify;">Mandar emails con archivos adjuntos es muy facil con Rails y <a title="Attachment Fu" href="http://clarkware.com/cgi/blosxom/2007/02/24" target="_blank">attachment_fu</a> (o PaperClip). Hace poco tuve que hacerlo para una nueva sección en uno de nuestros sitios. La idea era que el usuario subiera una foto al servidor, donde la información se procesa y se manda por email a un administrador, con la foto adjunta.</p>
<p><span id="more-65"></span></p>
<p style="text-align:justify;">El código en nuestro modelo Mailer, que llamamos <code>mailer.rb</code>:</p>
<pre class="brush: ruby;">
  # app/models/mailer.rb

  def photo(photo, attachment_data)
    subject       &quot;New Photo from #{photo.name}&quot;
    body          :photo =&gt; photo
    recipients    &quot;destinatario@host.com&quot;
    from          &quot;remitente@host.com&quot;
    sent_on       Time.now

    attachment :content_type =&gt; photo.content_type,
               :filename =&gt; &quot;photo.jpg&quot;,
               :body =&gt; attachment_data.read if photo.respond_to?(:content_type)
  end
</pre>
<p style="text-align:justify;">El campo <em>photo</em> es simplemente uno de nuestros modelos, y tiene información extra sobre la imagen.</p>
<p style="text-align:justify;">El parámetro más importante aquí es <em>attachment_data</em>,<em> </em>que<em> </em>en la vista debe ser un campo <em>file_field</em>. Aquí vemos un ejemplo en la<em> línea 3</em> del siguiente extracto de la vista:</p>
<pre class="brush: ruby;">
&lt;!-- ... (código) ... --&gt;
&lt;div class=&quot;form-row&quot;&gt;
  &lt;%= label :file, :uploaded_data, &quot;Upload your photo &lt;span&gt;(JPG or GIF format, 1MB max)&lt;/span&gt;&quot; %&gt;
  &lt;%= file_field :file, :uploaded_data %&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;!-- ... (más código)... --&gt;
</pre>
<p style="text-align:justify;">La documentación en inglés es muy escasa. Creo que probablemente <a title="PaperClip en GitHub" href="http://github.com/thoughtbot/paperclip/tree/master" target="_blank">PaperClip </a>sea una mejor opción para adjuntar archivos a un modelo, pero indiferentemente de qué gem o plugin se utilize, la lógica dentro del Mailer es muy similar:</p>
<p style="text-align:justify;"><code><span style="color:#ffcc00;">:content_type</span></code> =&gt; Debe reflejar el tipo de contenido del archivo adjunto. En este caso, un ejemplo puede ser &#8220;image/jpeg&#8221;.</p>
<p style="text-align:justify;"><code><span style="color:#ffcc00;">:filename</span></code> =&gt; El nombre de archivo para el archivo adjunto.</p>
<p style="text-align:justify;"><code><span style="color:#ffcc00;">:body</span></code> =&gt; El contenido del archivo. Aquí utilizamos el método <code>read</code>, que devuelve el contenido del archivo subido por el usuario, listo para ser adjunto, pero otra forma de hacerlo es utilizando <code>File.read("ruta/al/archivo.pdf")</code> o simplemente utilizando un método, como <code>generate_pdf().</code></p>
<p style="text-align:justify;">Es posible también mandar emails con múltiples archivos adjuntos, pero no lo probé. Según algunos autores (en inglés), basta con llamar más de una vez al método <code>attachment</code> dentro de <code>mailer.rb</code>.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/viarails.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/viarails.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/viarails.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/viarails.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/viarails.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/viarails.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/viarails.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/viarails.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/viarails.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/viarails.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/viarails.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/viarails.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/viarails.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/viarails.wordpress.com/65/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=65&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://viarails.wordpress.com/2009/08/03/emails-con-adjuntos/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4a3fbfe8b5b31e7003508bc5ee947290?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Gabriel Somoza</media:title>
		</media:content>
	</item>
		<item>
		<title>Admin Namespaces</title>
		<link>http://viarails.wordpress.com/2009/07/30/admin-namespaces/</link>
		<comments>http://viarails.wordpress.com/2009/07/30/admin-namespaces/#comments</comments>
		<pubDate>Thu, 30 Jul 2009 16:20:27 +0000</pubDate>
		<dc:creator>Gabriel Somoza</dc:creator>
				<category><![CDATA[Estructura]]></category>
		<category><![CDATA[Refactorización]]></category>
		<category><![CDATA[espacios de nombres]]></category>
		<category><![CDATA[namespaces]]></category>
		<category><![CDATA[recursos]]></category>
		<category><![CDATA[resources]]></category>
		<category><![CDATA[REST]]></category>

		<guid isPermaLink="false">http://viarails.wordpress.com/?p=35</guid>
		<description><![CDATA[Los namespaces (&#8220;espacios de nombres&#8221; en español) en Ruby (y otros lenguajes de programación) son una utilidad que permite organizar nuestro código dentro de, justamente, espacios a los que les podemos &#8220;dar un nombre&#8221;. Muchas veces es posible encontrar modelos y controladores que tienen un namespace implícito en sus nombres, sólo que no lo implementan [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=35&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Los <em>namespaces</em> (&#8220;<em>espacios de nombres</em>&#8221; en español) en Ruby (y otros lenguajes de programación) son una utilidad que permite organizar nuestro código dentro de, justamente, <em>espacios</em> a los que les podemos &#8220;dar un nombre&#8221;. Muchas veces es posible encontrar modelos y controladores que tienen un <em>namespace</em> implícito en sus nombres, sólo que no lo implementan más que ahí &#8211; en donde es de muy poca utilidad. <span id="more-35"></span> El ejemplo más común que conozco es en este caso de los CMS:</p>
<h2>Problema</h2>
<p>Supongamos, como caso de estudio, que creamos un modelo User y su controlador, UsersController. Este controlador normalmente lo ubicamos en <em>app/controllers</em>, y sus acciones REST se encargarían de mostrarle al usuario su Perfil (acción show), un formulario para editar sus datos (acción edit), un formulario de registro (acción new), etc. Sin embargo, también queremos agregar otro controlador que se encargue de las acciones que pueda ejecutar el <em>administrador</em> del sitio: ver la lista de usuarios, editar sus campos, eliminar usuarios, etc. Para esto tambien queremos usar las acciones REST predeterminadas (index, edit, destroy, etc).</p>
<p>En casos como estos es fácil caer en la tentación de crear otro controlador llamado UsersAdminController (o AdminUsersController). El problema de esto es que no es muy limpio, las rutas van a ser más difíciles de configurar, y podemos acabar con decenas de controladores difíciles de encontrar en el directorio <em>app/contollers</em>. Si no identificamos este problema a tiempo (idealmente antes de empezar a programar), en poco tiempo nos encontraremos con que tenemos controladores organizados al estilo Users + UsersAdmin, Books + BooksAdmin, Photos + PhotosAdmin, etc.</p>
<h2>Solución</h2>
<p>La solución es extremadamente simple y efectiva: utilizar <em>namespaces.</em> Teniendo este caso de estudio en mente, por ejemplo, podemos crear nuestra aplicación de la siguiente forma:</p>
<p><strong>1) </strong>En la consola ruby:</p>
<pre class="brush: bash;">
script/generate controller 'admin/users' index new create edit update destroy
</pre>
<p>Esto genera el siguiente controlador con las acciones especificadas ya definidas:<br />
app/controllers/admin/users_controller.rb</p>
<p><strong>2)</strong> Luego, editamos routes.rb para crear el namespace <em>admin </em>y los recursos <em>users</em> dentro de él, lo que nos dará direcciones del tipo <em>localhost/admin/users. </em>Como queremos administrar <em>muchos</em> usuarios, y no sólo uno, creamos <em>resources</em> (en plural).</p>
<pre class="brush: ruby;">
# config/routes.rb
#...
map.namespace :admin do |admin|
  admin.resources :users
end
</pre>
<p>Esto también genera las rutas REST correspondientes, como por ejemplo <em>admin_users_path </em>(<em>&#8220;admin/users</em>&#8220;), <em>new_admin_user_path</em> (&#8220;<em>admin/users/new</em>&#8220;), etc.</p>
<p><strong>3) </strong>Dentro del controlador podemos agregar el código que tenga que ver con la administración de usuarios utilizando un modelo <em>Users</em>.</p>
<p><strong>4) </strong>Ahora podemos crear un controlador con el mismo nombre pero ubicado en <em>&#8220;app/controllers</em>&#8220;, para adminitrar la creación de cuentas, actualización de la información, etc., del lado del cliente. Aquí queremos administrar a un sólo usuario, así que utilizamos <em>map.resource </em>(singular).</p>
<pre class="brush: ruby;">
# config/routes.rb
#...
map.namespace :admin do |admin|
  # controlado por &quot;controllers/admin/users_controller.rb&quot;
  admin.resources :users
end

# controlado por &quot;controllers/users_controller.rb&quot;
map.resource :user
</pre>
<p><strong>5) Listo! </strong>Ahora es posible administrar los usuarios del sitio utilizando <em>ejemplo.com/admin/users</em> y además permitir a cada uno de ellos acceder a sus cuentas individuales a través de <em>ejemplo.com/user</em> (y sus rutas derivadas).</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/viarails.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/viarails.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/viarails.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/viarails.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/viarails.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/viarails.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/viarails.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/viarails.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/viarails.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/viarails.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/viarails.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/viarails.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/viarails.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/viarails.wordpress.com/35/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=35&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://viarails.wordpress.com/2009/07/30/admin-namespaces/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4a3fbfe8b5b31e7003508bc5ee947290?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Gabriel Somoza</media:title>
		</media:content>
	</item>
		<item>
		<title>Mejoras para attachment_fu</title>
		<link>http://viarails.wordpress.com/2009/07/23/mejoras-para-attachment_fu/</link>
		<comments>http://viarails.wordpress.com/2009/07/23/mejoras-para-attachment_fu/#comments</comments>
		<pubDate>Thu, 23 Jul 2009 00:59:55 +0000</pubDate>
		<dc:creator>Gabriel Somoza</dc:creator>
				<category><![CDATA[Validaciones]]></category>
		<category><![CDATA[adjuntos]]></category>
		<category><![CDATA[attachment_fu]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[validación]]></category>

		<guid isPermaLink="false">http://viarails.wordpress.com/?p=3</guid>
		<description><![CDATA[Últimamente estuve trabajando en un proyecto que utiliza attachment_fu para adjuntar imágenes a un modelo y luego mandarlas por email. Este plugín puede ser muy últil, pero todavía le hace falta ser pulido un poco más. Uno de los problemas con los que me enfrenté es que, si un modelo se valida con validates_as_attachment pero [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=3&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Últimamente estuve trabajando en un proyecto que utiliza <span style="color:#ff6600;"><em>attachment_fu</em></span> para adjuntar imágenes a un modelo y luego mandarlas por email. Este plugín puede ser muy últil, pero todavía le hace falta ser pulido un poco más. Uno de los problemas con los que me enfrenté es que, si un modelo se valida con <span style="color:#993366;"><code>validates_as_attachment</code></span> pero el usario no escogió ninguna imagen para subir, los errores de validación devueltos por el plugin son muy poco comprensibles, además de ser innecesariamente numerosos (uno por cada uno de los campos: <em>content_type, filename, size</em> y otros). <span id="more-3"></span></p>
<p>No tuve que buscar mucho antes de encontrar <a title="Cheer Factory" href="http://scoop.cheerfactory.co.uk/2007/11/28/attachment-fu-validates-as-attachment-hack/" target="_blank">un artículo</a> que utiliza una técnica ingeniosa conocida como <em>Evil Twin </em>(<em>&#8220;Gemelo Malvado&#8221; </em>en inglés) para lograr lo que necesitaba. Con algunos cambios, pude adaptar el código a mis necesidades y hacerlo más flexible (por lo menos para mi gusto). Mis objetivos eran dos:</p>
<p>1) Devolver un solo mensaje de error si ningún archivo era seleccionado por el usuario.<br />
2) Tener la opción de personalizar los mensajes de error cuando el usuario seleccionaba un archivo inválido.</p>
<p>Para crear este plugin primero hay que crear una carpeta en <code><span style="color:#993366;">vendor/plugins</span></code> que se llame <code><span style="color:#993366;">attachment_fu_hack</span></code>. No es estrictamente necesario que tenga ese mismo nombre, pero lo que sí importa es que el contenido sea leído después del plugin <span style="color:#ff6600;"><em>attachment_fu</em></span>, porque lo que queremos es extender su funcionalidad. La razón por la cual agregamos <code><span style="color:#993366;">_hack</span></code> al final del nombre para nuestra nueva carpeta es porque Rails carga los plugins en orden alfabético, leyendo primero el plugin <span style="color:#ff6600;"><em>attachment_fu</em></span> y luego nuestro código.</p>
<p>Luego creamos el archivo <span style="color:#993366;">init.rb</span>. Este archivo sí debe ser llamado de esa forma, porque es el inicializador de nuestro &#8220;plugin&#8221;. A partir de ahora voy a incluir el código que va dentro de <span style="color:#993366;">init.rb</span> en secuencia, pero lo separo en partes para poder explicarlo. Pueden <a title="Código completo de init.rb" href="http://gabrielsomoza.pastebin.com/f3643d796" target="_blank">ver el código completo</a> de <span style="color:#993366;">init.rb</span> en mi subdominio de PasteBin. También está disponible para descargar en <a href="http://viarails.files.wordpress.com/2009/07/001-attachmentfu-hack.pdf">versión PDF</a>.</p>
<pre class="brush: ruby;">
klass = Technoweenie::AttachmentFu::ClassMethods
klass.module_eval do
  def validates_as_attachment(error_messages = {})
    validate :uploaded_data_is_present
    validates_presence_of :size, :content_type, :filename, :if =&gt; :uploaded_data?
    validate do |this|
      this.attachment_attributes_valid?(error_messages)
    end
  end
end
</pre>
<p><strong>Línea 01:</strong> asignamos a la variable <code style="color:#993366;">klass</code> los <em>métodos de clase</em> (dentro de ClassMethods) de <span style="color:#ff6600;"><em>attachment_fu</em></span>. Luego utilizamos el método <code style="color:#993366;">module_eval</code> para “abrir” la clase <code style="color:#993366;">ClassMethods </code>y sobrecargar el método <code style="color:#993366;">validates_as_attachment</code>, que es el que buscamos mejorar.</p>
<p><strong>Línea 03:</strong> agregamos al método un parámetro opcional, <code style="color:#993366;">error_messages</code>. En las siguientes líneas utilizamos el mismo código del artículo citado arriba.</p>
<p><strong>Línea 06:</strong> aquí comienzan los cambios. Llamamos al método <code style="color:#993366;">validate</code> utilizando un bloque para poder pasar parámetros a la función <code style="color:#993366;">attachment_attributes_valid?</code>, que ya veremos en un momento.</p>
<pre class="brush: ruby;">
methods_klass = Technoweenie::AttachmentFu::InstanceMethods
methods_klass.module_eval do
  def uploaded_data? # llamado por el método validates_as_attachment de arriba
    return false unless filename # filename debería ser nil si no hay datos
    true
  end

  # Valida los atributos &quot;size&quot; y &quot;content_type&quot; de acuerdo a la configuración
  # del modelo.
  def attachment_attributes_valid?(error_messages) #recibe mensajes como parámetro
    @attachment_error_messages = error_messages # guarda los mensajes
    if uploaded_data?
      [:size, :content_type].each do |attr_name|
        enum = attachment_options[attr_name] # valores posibles para el atributo
        errors.add_to_base attachment_error_message_for(attr_name) unless enum.nil? || enum.include?(send(attr_name))
      end
    end
  end

  def uploaded_data_is_present # valida si hay datos subidos o no
    unless uploaded_data? # si no hay datos...
      errors.add_to_base(&quot;Debe seleccionar una imagen para subir.&quot;) #...agrega el error al objeto
    end
  end

  # Devuelve el mensaje de error para un atributo
  def attachment_error_message_for(attribute)
    @attachment_error_messages[attribute] || ActiveRecord::Errors.default_error_messages[:inclusion]
  end
end
</pre>
<p><strong>Línea 13: </strong>ejecutamos el bloque para cada uno de los atributos en el Array. En este caso sólo necesitaba validar <em>size</em> y <em>content_type</em>.</p>
<p><strong>Línea 14: </strong>guardamos en <code style="color:#993366;">enum</code> la lista de valores posibles para el atributo que estamos validando.</p>
<p><strong>Línea 15: </strong>si <code style="color:#993366;">enum</code> es nulo o no incluye al <em>valor actual</em> del atributo, agrega un error a la clase con la descripción devuelta por <code style="color:#993366;">attachment_error_message_for</code>.</p>
<p><strong>Línea 19: </strong>esto logra nuestro principal objetivo, que es devolver <em>un solo</em> mensaje de error si el usuario se olvidó de subir un archivo.</p>
<p><strong>Línea 27: </strong>devuelve el error definido para el atributo pasado como parámetro. Si no se encuentra uno, devuelve un error de inclusión estándar de ActiveRecord (&#8220;<code style="color:#993366;">{{attribute}} is not in the list</code>&#8220;). Este método es el que nos permite devolver errores customizados cuando el usuario <em>sí</em> sube un archivo pero es del tipo incorrecto o está fuera de los límites de tamaño establecidos.</p>
<p>Utilizar la nueva funcionalidad es muy fácil. En el modelo:</p>
<pre class="brush: ruby;">
# app/models/photo.rb
class Photo &lt; ActiveRecord::Base
 has_attachment # ... resto del código código
 # ...
 validates_as_attachment	{ # pasamos un Hash con los atributos como &quot;keys&quot;
     :size =&gt; &quot;La imagen es demasiado grande&quot;, # y los mensajes de error
     :content_type =&gt; &quot;Formato desconocido&quot; }  # como &quot;values&quot;
end
</pre>
<p>Y vemos cómo ahora podemos pasarle los errores personalizados al método <code style="color:#993366;">validates_as_attachment</code>. Además, lo que es más importante, ahora <em><span style="color:#ff6600;">attachment_fu</span></em> devolverá un solo error cuando el usuario no suba el archivo que necesita el modelo.</p>
<p>Seguramente el código puede ser mejorado. Si a alguien se le ocurre alguna mejora o forma más eficiente de hacer lo mismo, por favor no duden en compartirlo en los comentarios!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/viarails.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/viarails.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/viarails.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/viarails.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/viarails.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/viarails.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/viarails.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/viarails.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/viarails.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/viarails.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/viarails.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/viarails.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/viarails.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/viarails.wordpress.com/3/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=viarails.wordpress.com&amp;blog=8688884&amp;post=3&amp;subd=viarails&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://viarails.wordpress.com/2009/07/23/mejoras-para-attachment_fu/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4a3fbfe8b5b31e7003508bc5ee947290?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Gabriel Somoza</media:title>
		</media:content>
	</item>
	</channel>
</rss>
