2011-02-09 10 views
6

Mi aplicación tiene algunos informes y estoy intentando hacer un método de ayuda para group_by para todas estas colecciones.Rails - group_by

Ejemplo:

def group_collection(collection, options = {}) 
    column = options[:column] 
    group_count = collection.group_by{ |item| item.column.strftime('%b %y')} 
end 

Éste es cómo va a utilizarlo

@user_groups = group_collection(@users, :column => "created_at") 

Por desgracia, esto no funciona.

undefined method `column' for... [CollectionObject] 

Alguna pista sobre cómo hacer que la "columna" variable de un tipo de columna real en tiempo de ejecución por lo que se considera como la columna activerecord y no un método de instancia?

Respuesta

21

Haciendo caso omiso de algunos de los otros problemas en su código, lo que estamos tratando de hacer con column se puede hacer así:

collection.group_by { |item| item.send(column).strftime('%b %y') } 

Esto funciona porque en Ruby la forma de acceder a las variables de instancia es a través de métodos de acceso, generalmente nombrados después de la variable a la que intenta acceder, por lo que @item.foobar llama al método foobar en @item.

Ahora, volvamos a esos "otros problemas". Es genial que intentes mover el comportamiento repetido a un solo lugar, y muestra que estás pensando en la extensibilidad cuando haces las cosas menos explícitas a favor de ser flexible. Sin embargo, hay un par de cosas que no funcionarán muy bien aquí que me siento obligado a señalar.

  1. Agrupación trabaja en una gran cantidad de tipos de datos, la mayoría de los cuales no responden a strftime. Si codifica la llamada de manera dura, está introduciendo un comportamiento inesperado que significa que no puede ejecutar group_collection(@users, :column => 'phone_number'). En cambio, solo ejecute eso después de probar que los datos de la columna pueden responderle.

    collection.group_by do |item| 
        data = item.send(column) 
        data.respond_to?(:strftime) ? data.strftime('%b %y') : data 
    end 
    
  2. Si clavo por el comportamiento de este método de ayuda es agrupar en una columna arbitraria, puede deshacerse de la complejidad adicional de aceptar un hash de opciones, sólo para evitarlo.

    def group_by_column(collection, column) 
        collection.group_by { ... } 
    end 
    group_by_column(@users, :column) 
    
  3. Puede agrupar por una columna arbitraria con mucha más facilidad, siempre y cuando estés usando Rubí 1.9+ y no es necesario hacer ningún formato adicional ..

    @users.group_by &:created_at 
    
+0

Gracias . Eso funciono. ¿Qué otros problemas ves en el código? Tendré que esperar otros 7 minutos para marcar esto como respondí. :) – AMIT

+0

Actualizado con "otros problemas". :) – coreyward

+0

¡Impresionante! Gracias por los detalles. Tienes razón. La agrupación también podría estar en otros tipos de datos. Y voy a deshacerme de las opciones hash también. Gracias de nuevo. – AMIT

2
def group_collection(collection, options = {}) 
    column = options[:column] 
    group_count = collection.group_by{ |item| item.send(column).strftime('%b %y')} 
end