Gastón Ramos

"La mayoría de las gaviotas no se molesta en aprender sino las normas de vuelo más elementales: como ir y volver entre playa y comida. Para la mayoría de las gaviotas, no es volar lo que importa, sino comer. Para esta gaviota, sin embargo, no era comer lo que le importaba, sino volar. Más que nada en el mundo, Juan Salvador Gaviota amaba volar".

Tag: rails

Acts as Rubylit 2010

Este post es para invitarte a las jornadas de este año a realizarse en
la Facultad de Ing. y Ciencias Hídricas en Santa Fe,
en dónde vamos a tener charlas muy interesantes tanto para principiantes
como para los que están en niveles más avanzados, con oradores de reconocimiento
nacional e internacional.
Una vez más el evento es gratuito, pero es necesario que estés
registrado para poder participar del mismo, es por esto que te
invitamos a hacerlo desde acá:

https://eventioz.com/events/actsasrubylit-2010/registrations/new

podés ver la agenda acá:

http://eventioz.com/events/actsasrubylit-2010/agenda

Todos los participantes de las jornadas van a recibir un certificado firmado por las autoridades de La Facultad de Ing. y Ciencias Hídricas.

Contamos con tu presencia!

Advertisements

MongoId y Rails3

Bueno últimamente vengo posteando con bastante delay (espero que eso cambie),
en esta ocasión voy a hacer un pequeño post acerca de mis experiencias migrando
una app de rails 2.3.4 y Active record con Mysql a Rails3, MongoId y Ruby 1.9.1,
sí, así completita la cosa, la aplicación no es demasiado grande así que viene bien
para probar esta nueva arquitectura.
No voy entrar en los detalles de como instalar rails3 y esas cosas por que ya existen
varios posts acerca del tema y no me quiero repetir, pasemos derecho al tema mongoid
Mongoid es un ODM(Object-Document-Mapper) para mongoDB, tiene varias cosas buenas algunas de las que me llamaron
la atención a mí es que no necesitamos migrations para para modificar nuestros documentos
(en sql diríamos tablas) además los campos (columnas) los podemos ver directamente abriendo
el modelo.


class Order
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::XmlBuilder

  field :external_id, :type => Integer
  field :account_id, :type => Integer
  field :order_type, :type => String
  field :posted, :type => Boolean
  field :send_result_notification, :type => Boolean
  field :file_duration_ms, :type => Integer
  field :created_at, :type => DateTime
  field :updated_at, :type => DateTime

  embeds_one :result, :class_name => "OrderResult"

  #  -----------------------------------------------------------------------------------------------
  #  Viejo named_scope reemplazado por mongoid queries
  #
  #  named_scope :pendings, :conditions => "NOT EXISTS (SELECT * FROM
  #   order_results WHERE
  #    order.id = order_results.transcription_id)
  #    AND posted_id IS NOT NULL AND
  #    orders.posted = 1"
  #
  # ------------------------------------------------------------------------------------------------

  def self.without_results
    result_ids = TranscriptionResult.criteria.only(:id).map{|id| }
    Order.where(:_id.nin => result_ids)
  end

  def self.pendings
    without_results.where(:posted => true)
  end
end

Otra de las ventajas es que los criterios son anidables con lo cual funcionarían
como una especie de scope de Active Record, fijensé como quedó el viejo named_scope
con mongoid, mucho más claro que antes para mi gusto.
Otra de las ventajas de mongoID en particular es que incluye ActiveModel, con lo
cual tenemos las mismas validaciones que en Rails, también podemos definir callbacks de forma similar que en Active Record.

Pasemos a explicar un poquito:
Lo que yo necesito son los transcriptions que no tengan un transcription_result asociado y dónde posted sea true.


result_ids =OrderResult.criteria.only(:id)
= > #[:id]}, @klass=OrderResult, @documents=[]>


con esto saco la lista de results, sólo me traigo los ids por que no necesito
otra cosa y le aplico un map para obtener una array con la lista de ids (cómo pueden ver eso devuelve un objeto Mongoid::Criteria) con esto ya tengo los transcriptions que no tienen results (el método without_results). Después una vez que tengo esto ya es más fácil
generar los pendings (transcriptions sin results y con posted == true).
Nota: :_id.nin quiere decir “los _id que no estén incluidos en (not included in)”
para más detalles pueden ver la doc de queries de mongoid

PD: MongoId también tiene named_scopes dejo pendiente para el próximo refactoring pasar esto a named_scope de mongoid.

Nos vemos en el próximo post.

Acts as state machine y metaprogramming

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!

Aguante named_scope !

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}, 
                    :order => '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.

Rails edge: Nuevo método Enumerable#many?

Rails agrega un nuevo método al módulo Enumerable. Vamos al código para explicar cómo funciona este nuevo método:

>> Post.all.many?
=> false
>> Post.create({:title => "hola", :post => "hola"})
>> Post.all.many?
=> false
>> Post.create({:title => "otro hola", :post => "otro hola"})
>> Post.all.many?
=> true
>> Post.all.size
=> 2
>> posts = Post.find(:all, :conditions => {:post => "hola"})
>> posts.many?
=> false
>> posts.size
=> 1

La función de este nuevo método many? es básicamente encapsular collection.size > 1, es un detalle bastante cómodo que opinan uds?