Rails

Rails

Una gema de Rails que tuve que usar recientemente es ActsAsList. Esta extensión de Rails provee la capacidad de clasificar y ordenar objetos en una lista.

La clase con esta necesidad específica debe tener una columna position (posición) definida como Integer en la tabla de base de datos mapeada.

Es bastante sencilla de usar, pueden agregar a su Gemfile la gema:

gem 'acts_as_list'

Voy a mostrar un ejemplo bastante sencillo en Rails a efectos de explicar el concepto. Tenemos una aplicación Rails con dos modelos: Book y Bookshelf. Generamos los modelos correspondientes:

$ rails generate model Bookshelf name:string
$ rails generate model Book title:string author:string description:text position:integer bookshelf:references

Y mandamos a generar las tablas:

$ rake db:migrate

Editamos el código de app/models/bookshelf.rb para agregar el orden:

class Bookshelf < ActiveRecord::Base
  has_many :books, :order => 'position'
end

Y el libro:

class Book < ActiveRecord::Base
  belongs_to :book_shelf
  acts_as_list :scope => :bookshelf
end

Hecho esto ya estamos en condiciones de tener una Bookshelf con una colección de libros, y manipular el orden de los libros dentro de esta colección a través del índice "position". Podemos probar el código en la consola de Rails con:

$ rails console

Empezamos por crear un bookshelf para guardar nuestros libros:

bookshelf = Bookshelf.new(:name => "My bookshelf")

Hecho esto, creamos algunos libros asignándoles nuestro objeto bookshelf:

> book1 = Book.new(:title => "1984", :author => "George Orwell", :description => "Dystopian future", :bookshelf => bookshelf)
> book2 = Book.new(:title => "A princess of Mars", :author => "Edgar Rice Burroughs", :description => "John Carter is in this book", :bookshelf => bookshelf)
> book3 = Book.new(:title => "Ready Player One", :author => "Ernest Cline", :description => "Online virtual world / videogame", :bookshelf => bookshelf)
> book4 = Book.new(:title => "Foundation", :author => "Isac Aasimov", :description => "Blow-your-mind-Sci-fi", :bookshelf => bookshelf)

Ya armada la estructura y guardada podemos ver que los libros tienen su posición según fueron siendo guardados:

> bookshelf.books
  Book Load (0.3ms)  SELECT "books".* FROM "books" WHERE "books"."bookshelf_id" = 1 ORDER BY position
=> [#<Book id: 1, title: "1984", author: "George Orwell", description: "Dystopian future", position: 1, bookshelf_id: 1, created_at: "2012-04-03 02:46:36", updated_at: "2012-04-03 02:46:36">, 
#<Book id: 2, title: "A princess of Mars", author: "Edgar Rice Burroughs", description: "John Carter is in this book", position: 2, bookshelf_id: 1, created_at: "2012-04-03 02:46:38", updated_at: "2012-04-03 02:46:38">, 
#<Book id: 3, title: "Ready Player One", author: "Ernest Cline", description: "Online virtual world / videogame", position: 3, bookshelf_id: 1, created_at: "2012-04-03 02:46:40", updated_at: "2012-04-03 02:46:40">, 
#<Book id: 4, title: "Foundation", author: "Isac Aasimov", description: "Blow-your-mind-Sci-fi", position: 4, bookshelf_id: 1, created_at: "2012-04-03 02:46:42", updated_at: "2012-04-03 02:46:42">]

El cuarto libro por ejemplo, podemos moverlo a la primera posición, y tras recargar los objetos, vemos que automáticamente los demás libros sumarán una posición más:

> book4.position
=> 4
> book4.move_to_top #Mover a la primera posición
   (0.1ms)  begin transaction
  SQL (0.3ms)  UPDATE "books" SET position = (position + 1) WHERE ("books"."bookshelf_id" = 1 AND position < 4)
   (0.3ms)  UPDATE "books" SET "position" = 1, "updated_at" = '2012-04-03 02:47:55.303110' WHERE "books"."id" = 4
   (105.5ms)  commit transaction
=> true
> book4.position
=> 1

Podemos comprobar las posiciones de los demás libros:

book1.position
=> 2
> book2.position
=> 3

Y mover un libro al final de la lista:

> book2.move_to_bottom
   (0.1ms)  begin transaction
  SQL (0.3ms)  UPDATE "books" SET position = (position - 1) WHERE ("books"."bookshelf_id" = 1 AND position > 3)
  Book Load (0.3ms)  SELECT "books".* FROM "books" WHERE ("books"."bookshelf_id" = 1 AND id != 2) ORDER BY position DESC LIMIT 1
   (0.3ms)  UPDATE "books" SET "position" = 4, "updated_at" = '2012-04-03 02:50:13.740681' WHERE "books"."id" = 2
   (107.7ms)  commit transaction
=> true
> book2 = Book.find 2
  Book Load (0.2ms)  SELECT "books".* FROM "books" WHERE "books"."id" = ? LIMIT 1  [["id", 2]]
=> #<Book id: 2, title: "A princess of Mars", author: "Edgar Rice Burroughs", description: "John Carter is in this book", position: 4, bookshelf_id: 1, created_at: "2012-04-03 02:46:38", updated_at: "2012-04-03 02:50:13">
> book2.position
=> 4

Y la posición final de todos los libros quedó:

> bookshelf.books
  Book Load (0.3ms)  SELECT "books".* FROM "books" WHERE "books"."bookshelf_id" = 1 ORDER BY position
=> [#<Book id: 4, title: "Foundation", author: "Isac Aasimov", description: "Blow-your-mind-Sci-fi", position: 1, bookshelf_id: 1, created_at: "2012-04-03 02:46:42", updated_at: "2012-04-03 02:47:55">, 
#<Book id: 1, title: "1984", author: "George Orwell", description: "Dystopian future", position: 2, bookshelf_id: 1, created_at: "2012-04-03 02:46:36", updated_at: "2012-04-03 02:46:36">, 
#<Book id: 3, title: "Ready Player One", author: "Ernest Cline", description: "Online virtual world / videogame", position: 3, bookshelf_id: 1, created_at: "2012-04-03 02:46:40", updated_at: "2012-04-03 02:46:40">, 
#<Book id: 2, title: "A princess of Mars", author: "Edgar Rice Burroughs", description: "John Carter is in this book", position: 4, bookshelf_id: 1, created_at: "2012-04-03 02:46:38", updated_at: "2012-04-03 02:50:13">]

Una herramienta bastante útil con otros métodos como move_higher, move_lower, y más que pueden leer en el código fuente. Si quieren leer más al respecto visiten acts_as_list.

No hay comentarios en este post

Feed de comentarios

Dejar un comentario

Toasty!