Jornadas de software libre en Junin

Wednesday, 04 November , 2009

Hace poco me invitaron para dar una charla en las Jornadas de Software libre de Junin, y el viernes pasado llegué allí con tal motivo, comimos un asado *muy* bueno con coca cola por supuesto :) , al otro día tuve que abrir el día con mi charla de Ruby (todos sabemos lo que significa comenzar a las 9:00 AM un sábado!) la charla salió muy bien para mi gusto y la gente parecía bastante entusiasmada,hicieron varias preguntas, después vino “El Profe” Román con una charla acerca de como participar del proyecto Open Office, y más tarde y para cerrar el día, Javier hablando acerca Asterix, quiero destacar que la organización fué excelente todo muy lindo.Al final hubo regalos!!! Muchas gracias a todos los organizadores por esta gran jornada!.

Para los que quiera ver los slides:

Ruby-un-lenguaje-simple-natural-y-productivo-gaston-ramos

Acá les dejo algunas fotos:

 

Este es el primer post de una serie que he decidido comenzar titulada “Analizando commits de rails edge”. Aclaro que los post no van a tener orden alguno y que voy a comentar los que me llaman la atención a _mí_ :) .
En este caso el commit se trata de agregar a rails la nueva forma de interpolación de strings que trae ruby 1.9 ( la idea es que se pueda usar con versiones menores también), pueden ver el código en este archivo:

activesupport/lib/active_support/core_ext/string/interpolation.rb

Cómo uds sabrán en Ruby se pueden interpolar strings de la siguiente manera:

>>"%s, %s" % ["Masao", "Mutoh"]
=> "Masao, Mutoh"

Cómo lo explica el comentario en el código:

 # call-seq:
  #   %(arg)
  #   %(hash)
  #
  # Format - Uses str as a format specification, and returns the result of applying it to arg.
  # If the format specification contains more than one substitution, then arg must be
  # an Array containing the values to be substituted. See Kernel::sprintf for details of the
  # format string. This is the default behavior of the String class.
  #   * arg: an Array or other class except Hash.
  #   * Returns: formatted String
  # Example:
  #    "%s, %s" % ["Masao", "Mutoh"]
  #
  # Also you can use a Hash as the "named argument". This is recommended way so translators
  # can understand the meanings of the msgids easily.
  #   * hash: {:key1 => value1, :key2 => value2, ... }
  #   * Returns: formatted String
  # Example:
  #   For strings.
  #   "%{firstname}, %{familyname}" % {:firstname => "Masao", :familyname => "Mutoh"}
  #
  #   With field type to specify format such as d(decimal), f(float),...
  #   "%d, %.1f" % {:age => 10, :weight => 43.4}

Es decir que ahora podemos interpolar strings de la siguiente manera:

>> "%{firstname}, %{familyname}" % {:firstname => "Masao", :familyname => "Mutoh"}
ArgumentError: malformed format string - %{
	from (irb):2:in `%'
	from (irb):2
>>

Auuuchh! estoy probando con un irb con ruby 1.8.6, así que necesito requerir el archivo:

>> require 'activesupport/lib/active_support/core_ext/string/interpolation.rb'
=> true
>> "%{firstname}, %{familyname}" % {:firstname => "Masao", :familyname => "Mutoh"}
=> "Masao, Mutoh"

Ahora sí! podemos interpolar string nombrados pasando un hash como argumento, Hermoso!
Estuve mirando en los fuentes de Ruby 1.9 y no encuentro dónde está escrita esta nueva funcionalidad, el método que define la interpolación con”%” es rb_str_format_m y está en la línea número 1202 del archivo string.c en el trunk de ruby, pero sólo está la vieja forma de interpolación. Entonces dónde está ? Alguien sabe?
Es más hice esta prueba:

$ irb1.9
require 'activesupport/lib/active_support/string/interpolation.rb'
=> true
irb(main):002:0> "%{firstname}, %{familyname}" % {:firstname => "Masao", :familyname => "Mutoh"}
ArgumentError: malformed format string - %{
	from (irb):2:in `%'
	from (irb):2
	from /usr/bin/irb1.9:12:in `'

Y la versión que tengo de ruby 1.9 es:
ruby 1.9.0 (2008-06-20 revision 17482) [i486-linux]
Un poco vieja, probemos con ruby 1.9 compilado desde trunk
ruby 1.9.2dev (2009-07-11 trunk 24027) [i686-linux]

./ruby  -e 'puts "%{firstname}, %{familyname}" % {:firstname => "Masao", :familyname => "Mutoh"}'
Masao, Mutoh

Funcionó! entonces en algún lugar tiene que estar.

Rails 2.3, rake y reenable method

Wednesday, 08 July , 2009

Estoy terminando de hacer una migration a Rails 2.3 de la
app con la que estoy trabajando, resulta que me encontré con el siguiente tema:
Luego de instalar rails y migrar la app, llegó el momento de hacer un deploy a
staging, entonces me econtré con el siguiente error:

undefined method `reenable' for <Rake::Task db:schema:dump =>
[environment]>:Rake::Task

googleando un poco llegué a este link que dice que hay que instalar
la nueva versión de rake, investigando un poco más, llegué a este PATCH
que hace uso del método Rake::Task#reenable, el caso es que este método
se agrega e con la versión de rake 0.8.2 y como el pibe dice en el comentario
del patch, no hay problemas por que rails 2.3 require rake 0.8.3…. pero
que pasa cuando:

1- Instalo rails 2.3
2- Hago un ‘rake rails:freeze’
3- hago ‘cap staging deploy:migrations’

Y no tengo rails 2.3 instalado en el server y por eso justamente hice un freeze.

Si leemos la task que arma las dependencias de rails:

  s.add_dependency('rake', '>= 0.8.3')
  s.add_dependency('activesupport',    '= 3.0.pre' + PKG_BUILD)
  s.add_dependency('activerecord',     '= 3.0.pre' + PKG_BUILD)
  s.add_dependency('actionpack',       '= 3.0.pre' + PKG_BUILD)
  s.add_dependency('actionmailer',     '= 3.0.pre' + PKG_BUILD)
  s.add_dependency('activeresource',   '= 3.0.pre' + PKG_BUILD)

uando hacemos el freeze nos copia todas estas cosas menos el rake,
lo cual parece razonable. Pero creo debería agregar en config/environment.rb

config.gem “rake”, :version => ‘0.8.3′

Que opinan uds?

Instalando Mod_Ruby

Wednesday, 22 April , 2009

Bueno dada la escasa y fea documentación que mod_ruby tiene me decidí a escribir un pequeño tutorial de instalación del mismo en debian, que paso a detallar:

Primero instalamos el modulo de apache para ruby (mod_ruby)

apt-get install libapache2-mod-ruby

Luego debemos crear el siguiente archivo:
vi /etc/apache2/mods-available/ruby.conf

y ponemos lo siguiente:

<IfModule mod_ruby.c>
  RubyRequire apache/ruby-run

  <Files *.rbx>
  SetHandler ruby-object
  RubyHandler Apache::RubyRun.instance
  </Files>
</IfModule>

Entre otras cosas le decimos que debe tratar los archivos .rbx como
archivos de ruby.

Luego de esto ya tenemos los dos archivos necesarios para habilitar el módulo en apache:

ruby.load (que secopia al instalar el módulo) y ruby.conf generado por nosotros. Ahora cómo mod_ruby ejecuta los scrips de ruby como CGI necesitamos poner la directiva Options +ExecCGI en el folder dónde vamos a poner nuestros scripts, en mi caso particular necesito que todos los usuarios del sistema puedan ejecutar sus scripts desde su ~/public_html entonces para lograr esto tenemos que editar la configuración del módulo user_dir, editamos el archivo

vi /etc/apache2/mods-available/userdir.conf
y agregamos lo siguiente:
 <IfModule mod_userdir.c>
        UserDir public_html
        UserDir disabled root

        <Directory /home/*/public_html>
                AllowOverride FileInfo AuthConfig Limit Indexes
                Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
                # Para poder ejecutar scripts en ruby agregamos la linea de abajo
                Options +ExecCGI
                <Limit GET POST OPTIONS>
                        Order allow,deny
                        Allow from all
                </Limit>
                <LimitExcept GET POST OPTIONS>
                        Order deny,allow
                        Deny from all
                </LimitExcept>
        </Directory>
</IfModule>

No sé si esto muy seguro o tiene alguna consecuencia no deseada, si es así comenten!
Bueno ahora sólo necesitamos habilitar el móludo para esto ejecutamos el siguiente comando:

a2enmod ruby

y hacemos un reload del apache

/etc/init.d apache2 reload

despues de esto para probarlo ponemos un archivo en nuestro home

vi /home/gaston/test-ruby.rb

con un simple:

puts "hello world"

vamos al navegador a la siguiente url:

http://aca-va-la-url-de-tuserver/~gaston/test-ruby.rb

y vamos a ver un hermoso “hello world” si todo salió bien.

Bueno, con mod_ruby solo para escribir una página web dinámica vamos a tener que hacer algo cómo esto:

puts “<h1>Mi Título</h1>”

lo cual es muy molesto, para hacer las cosas un póco más lindas y más fácil debemos usar eruby que nos permite ejecutar código ruby dentro de archivos de text (como por ejémplo un arhivo html)

Entonces comencemos por instalar eruby:

apt-get install eruby

y luego tenemos que modificar nuevamente el archivo /etc/apache2/mods-available/ruby.conf de esta manera:

<IfModule mod_ruby.c>
  RubyRequire apache/ruby-run
  RubyRequire apache/eruby-run

  <Files *.rbx>
  SetHandler ruby-object
  RubyHandler Apache::RubyRun.instance
  </Files>

 <Files *.rhtml>
  SetHandler ruby-object
  RubyHandler Apache::ERubyRun.instance
 </Files>

</IfModule>

Y luego debemos agregar la siguiente directiva al archivo de configuración de apache /etc/apache2/apache2.conf

AddType text/html .rhtml

Con esto recargamos el apache y ya podemos escribir nuestros .rhtml
y poner código ruby entre los tags <%= %>

Links útiles:

http://en.wikipedia.org/wiki/ERuby
http://wiki.modruby.net/ja/?InstallGuide
http://modruby.net/
http://ubuntuforums.org/archive/index.php/t-356350.html
http://www.ruby-doc.org/docs/ProgrammingRuby/html/web.html

Fe de erratas: En el artículo de abajo dónde expliqué cómo generar un named_scope por cada estado declarado con acts_as_state_machine hay un BUG el tema es así, los named_scope se definían en un método (self.define_named_scopes) que era llamado desde el constructor de la clase User, por consiguiente si queremos usar uno de estos named_scopes antes de que se instancie algún objeto de esta clase, el scope no existe, cómo era de esperar.

Entonces la solución rápida sería hacer directamente así:

class User   < ActiveRecord::Base
   acts_as_state_machine :initial => :inactive
   state :inactive
   state :active

   event :active do
     transitions :from => :inactive, :to => :active
   end

  ########################################################
  # define all state as named_scopes
  #
  self.states.each{|st|
    self.named_scope st, :conditions => { :state => st.to_s },
                      : order => 'created_at DESC'
  }
end

Hace bastante que vengo usando la genial biblioteca “Acts as state machine” que nos permite tener un máquina de estados finita (o autómata finito) en un modelo Active Record y a partir de las últimas versiones en cualquier objeto Ruby, y desde hace un tiempito que vengo pensando “Qué bueno sería que ASSM te genere un named_scope por cada estado posible”, entonces si por ejemplo tenemos 2 estados: active, inactive podríamos hacer algo así:

User.inactive # Esto retorna todos los usuarios con estado inactive
User.active # Esto retorna todos los usuarios con estado active

Bueno, acá está la solución que se me ocurrió:

class User   < ActiveRecord::Base
   acts_as_state_machine :initial => :inactive
   state :inactive
   state :active

   event :active do
     transitions :from => :inactive, :to => :active
   end

   def initialize(*args)
      self.class.define_named_scopes
      super *args
    end

  def self.define_named_scopes
    self.states.each{|st|
    self.named_scope st, :conditions => { :state => st.to_s },
                      : order => 'created_at DESC'
    }
  end
end

Nice eh! tenemos un método que escribe los named_scope por nosotros y seguro que se puede mejorar o quizás agregar una opción a AASM para que lo haga cuando lo deseemos, el tema es que cómo necesitamos la lista de los estados, debemos ejecutar esto después de especificar los mismos, aunque una solución más elegante sería modificar AASM y hacer que los named_scope se genere cada vez que especificamos un estado.

Hasta la próxima!

Tirar el código

Saturday, 14 February , 2009

Estoy leyendo un libro de Smalltalk y la verdad leí una frase que me encantó, así que quería dejar plasmada acá:
“El código escrito es importante, pero mucho más importante es el conocimiento que vamos obteniendo conforme programamos. Si aprendemos día a día, probablemente el código viejo no sea bueno. Tirar código no es malo, lo malo es no aprender a diario.”

Red de blogs de la NFL en Rails muy pronto!

Tuesday, 03 February , 2009

Hace mucho que no escribía un post, esta vez es para comunicarles que han instalado una red de más de 30 blogs de la NFL con el sistema de blog desarrollado por mí completamente en Rails, los blogs aún no fueron lanzados oficialmente (esto será en breve). Además del sistema de blog a medida hice un script para facilitar la instalación, para que una persona con pocos concimientos pueda setear un nuevo site completo (esto incluye el virtal server, base de datos, etc). Realmente quería postear esto por que me parece un buen ejemplo para mostrar lo productivo que es Ruby y Ruby on Rails, les cuento algunos números. El sistema de blog fue desarrollado aproximadamente en 5 meses contando la instalación completa del servidor, me parece un muy buen tiempo teniendo en cuenta que había único desarrollador dedicado a full a este proyecto. En un principio se hizo el blog para el equipo de la NFL Giants (http://www.bigbluepass.com/) luego estuve un mes escribiendo el script para automatizar la instalación y haciendo que el blog sea “genérico” es decir se pueda utilizar para cualquier equipo y tener la posibilidad de configurarlo por completo (colores, imágenes, título, css, etc.).
Vamos a lo que nos interesa:

El sistema: Un sisema de blog a medida para fanáticos de equipos de la NFL
Plugins Utilizados:

acts_as_state_machine
acts_as_taggable_on_steroids
attachment_fu
colorpicker
dnsbl_check
easy-fckeditor
meteor_strike
permalink_fu
recaptcha
restful_authentication
rspec
rspec-rails
selenium-on-rails
timed_fragment_cache
will_paginate

Rake stats:

+----------------------+-------+-------+---------+---------+-----+-------+
| Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Controllers          |  1142 |   824 |      21 |     137 |   6 |     4 |
| Helpers              |   452 |   325 |       0 |      46 |   0 |     5 |
| Models               |   767 |   508 |      18 |      64 |   3 |     5 |
| Libraries            |   370 |   230 |       2 |      45 |  22 |     3 |
| Model specs          |   668 |   519 |       0 |      10 |   0 |    49 |
| View specs           |   200 |   142 |       0 |      10 |   0 |    12 |
| Controller specs     |  1671 |  1272 |       0 |      41 |   0 |    29 |
| Helper specs         |   191 |   169 |       0 |       2 |   0 |    82 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total                |  5461 |  3989 |      41 |     355 |   8 |     9 |
+----------------------+-------+-------+---------+---------+-----+-------+
  Code LOC: 1887     Test LOC: 2102     Code to Test Ratio: 1:1.1

rake spec:models: 79 examples, 0 failures

rake spec:controllers: 180 examples, 0 failures

El instalador: Un script para automatizar y facilitar la instalación de los blogs (Ruby + Mod_Rails)

Ambiente de producción:Apache + Mod_Rails + Ruby Enterprise Edition

Desarrolladores: 1 (yo hice casi todo menos los css, algunas cosas en javascript y el módulo que muestra los slides en la portada que lo hizo César Díaz )

Cómo autocrítica puedo decir que me hubiera gustado que tenga más cobertura de tests, pero bueno será para la próxima.

Esta es la red de blogs:

http://www.atlrumble.com
http://www.baltimoreregister.com
http://www.bigbluepass.com
http://www.bigeasyfootball.com
http://www.bluestardaily.com
http://www.boltsnotes.com
http://www.bucreport.com
http://www.buffaloregister.com
http://www.carolinarules.com
http://www.catspass.com
http://www.cincyrecord.com
http://www.dawgpoundlive.com
http:/www.fanpublic.com
http://www.ganggreenwire.com
http://www.greenbayreport.com
http://www.greennationreport.com
http://www.hawkscountry.com
http://www.kclocker.com
http://www.milehightimes.com
http://www.motorcitypress.com
http://www.musiccitywire.com
http://www.ninerpride.com
http://www.oaklandnotes.com
http://www.phinpass.com
http://www.redbirdcrew.com
http://www.skinsreport.com
http://www.steelcurtaintimes.com
http://www.stlouisnotes.com
http://www.texalley.com
http://www.truebluegang.com
http://www.vikespeak.com
http://www.wickedpats.com
http://www.windycitynation.com

Esto fue todo, hasta el póximo post ;)

Hace un tiempo que me enteré que existe mod_rails y que tengo ganas de implementarlo en mi vps (dónde está alojado el sitio de rubylit entre otros) El vps tiene 300 MB de RAM en total, y hasta ahora tenía nada más que 30MB libres ( cada instancia de mongrel me consumia unos 40MB aprox.)

Así que aquí vamos con la instalación de mod_rails.

Primero vamos a instalar Ruby Enterprise Edition cómo recomiendan los chicos de passenger (mod_rails), también podemos usar el ruby convencional.

Para esto nos bajamos el .tar.gz con los sources y lo descomprimimos:

wget -c http://rubyforge.org/frs/download.php/47937/ruby-enterprise-1.8.6-20081205.tar.gz
tar -xvvzf ruby-enterprise-1.8.6-20080810.tar.gz

Yo lo voy a instalar en /opt que es dónde se instalan los paquetes que no vienen con la distro entonces quedaría:

/opt/ruby-enterprise-1.8.6-20080810/

Instalamos las libs necesarias para compilar ruby EE:

apt-get install g++ zlib1g-dev libssl-dev build-essential

Al intentar instalarlo me tiró el siguiente error, pero leyendo algunos mails de la lista de Ruby EE recomendaban ejecutar el instalador de la siguiente manera:

cd /opt/ruby-enterprise-1.8.6-20080810/
./installer --no-tcmalloc

y funcionó…

Ahora tenemos que crear una serie de links simbólicos para que nos quede la versión de Ruby EE instalada correctamente:

ln -fs /opt/ruby-enterprise-1.8.6-20080810/ /opt/ruby-enterprise
ln -fs /opt/ruby-enterprise/bin/gem /usr/bin/gem
ln -fs /opt/ruby-enterprise/bin/irb /usr/bin/irb
ln -fs /opt/ruby-enterprise/bin/rake /usr/bin/rake
ln -fs /opt/ruby-enterprise/bin/rails /usr/bin/rails
ln -fs /opt/ruby-enterprise/bin/ruby /usr/bin/ruby
ln -fs /opt/ruby-enterprise/lib/ruby/ /usr/lib/ruby

Ahora instalamos la gema de mysql

apt-get install libmysqlclient15-dev

/opt/ruby-enterprise-1.8.6-20080810/bin/ruby /opt/ruby-enterprise-1.8.6-20080810/bin/gem install mysql

Ya tenemos instalado Ruby EE entonces ahora vamos a instalar mod_rails.

bajamos los sources de passenger:

wget -c http://rubyforge.org/frs/download.php/47928/passenger-2.0.5.tar.gz
tar -xvvzf passenger-2.0.5.tar.gz

Instalamos las lib necesarias para compilar:

apt-get install ruby-dev libopenssl-ruby rubygems  apache2-prefork-dev build-essential

Instalamos apache prefork:

apt-get install apache2-mpm-prefork

Ahora tenemos que instalar mod_rails:

cd /opt/passenger-2.0.5
./bin/passenger-install-apache2-module

luego editamos el archivo /etc/apache2/mods-available/passenger.conf
y ponemos lo soguiente:

PassengerRoot /opt/passenger-2.0.5
PassengerRuby /opt/ruby-enterprise-1.8.6-20080810/bin/ruby

editamos el archivo /etc/apache2/mods-available/passenger.load
y ponemos lo siguiente:

LoadModule passenger_module /opt/passenger-2.0.5/ext/apache2/mod_passenger.so

luego tenemos que activar el modulo entonces ejecutamos el siguiente comando:

a2enmod passenger

Listo ahora lo único que tenemos que hacer cada vez que agregamos una app
es agregar un virtual host en apache.
Editamos el archivo /etc/apache2/sites-available/nueva_app.com
y ponemos lo siguiente:

    <VirtualHost *:80>
    ServerName www.nueva_app.com
    ServerAlias nueva_app.com
    DocumentRoot /var/www/apps/nueva_app.com/public
    RailsBaseURI /
    CustomLog /var/log/apache2/nueva_app.com.log "%h %l %u %t \"%r\" %>s %b"
   </VirtualHost>

Como pueden ver puse el archivo de log separado del lo general de apache.
luego de esto habilitamos el nuevo virtual host

a2ensite nueva_app.com

y reiniciamos apache:

/etc/init.d/apache2 reload

y listo.

Ahora en mi vps tengo 165MB de RAM libres, así que la migración fue productiva, cabe destacar que tuve que migrar de nginx a apache entonces todos los virtual hosts (státicos y dinámicos) que tenía creados en nginx los tuve que migrar a apache, pero no fue algo complicado.

Aguante named_scope !

Tuesday, 14 October , 2008

Hace un tiempito que vengo usando named_sope en mis modelos y la verdad que cada vez me sorprendo más, en esta ocasión me hice uno que utilizo con el plugin acts_as_taggable_on_steroids, que retorna los posts que poseen cierto tag:


#############################################################
# Scopes

 named_scope :tagged_with, lambda{|tags|
    conditions =   {:conditions => { :published => true},
                    :o rder => 'created_at DESC',
                    :match_all => true}
    find_options_for_find_tagged_with(tags, conditions)
  }

Además acá les dejo los specs que escribí para este scope:


describe Post, "tagged_with" do
  fixtures :posts

  before(:each) do
    @tag = "ruby"

    1.upto(4) {
      p = create_post
      p.tag_list << @tag
      p.published = true
      p.save
    }
  end

  it "should have all tagged with #{@tag}" do
    Post.tagged_with(@tag).each{ |p| p.tag_list.should include(@tag) }
  end

  it "should have at least 3 posts tagged with #{@tag}" do
    Post.tagged_with(@tag).should have_at_least(3).posts
  end

end

Los specs no me convencen mucho así que si alguien tiene alguna forma mejor de hacer que me avise.

Felíz día del programador!

Friday, 12 September , 2008


Hoy es el día número 256 del año, día elegido como el día del programador, saludos para todos los que soportamos las presiones de los deadlines y demás.