Tag: Pagination

Mongoid, Kaminari i Padrino (Sinatra)

Część z Was już pewnie zauważyła, że w nowej wersji Mongoida (2.0.2) zrezygnowano z will_paginate. Will_paginate okazał się niezbyt efektywny, ponieważ nie obsługiwał opcji limit i kilku innych rzeczy, w skutek czego paginacja dużych zbiorów stawała się bardzo powolna. Zamiast tego jako podstawową metodę paginacji dla nowych wersji Mongoida zaleca się Kaminari. O Kaminari już parokrotnie wspominałem. Jest świetne. Niestety - nie jest aż tak wesoło kiedy trzeba je połączyć z aplikacją która nie jest napisana w Railsach. Zwykłe:

gem 'kaminari'
# i ew. gdzieś tam w kodzie jakby kogoś naszło
require 'kaminari'

na niewiele się zda.

Brak Kaminari skutkuje tego typu błędami:

NoMethodError: undefined method `page' for Note:Class
	/home/mencio/Rails/app/webui/app/controllers/notes.rb:7:in `GET /notes'
	/home/mencio/.rvm/gems/ruby-1.8.7-p299@app/gems/padrino-core-0.9.19/lib/padrino-core/application/routing.rb:340:in `call'
	/home/mencio/.rvm/gems/ruby-1.8.7-p299@app/gems/padrino-core-0.9.19/lib/padrino-core/application/routing.rb:340:in `route'
	/home/mencio/.rvm/gems/ruby-1.8.7-p299@app/gems/sinatra-1.1.3/lib/sinatra/base.rb:649:in `instance_eval'
	/home/mencio/.rvm/gems/ruby-1.8.7-p299@app/gems/sinatra-1.1.3/lib/sinatra/base.rb:649:in `route_eval'
	/home/mencio/.rvm/gems/ruby-1.8.7-p299@app/gems/padrino-core-0.9.19/lib/padrino-core/application/routing.rb:605:in `route!'
	/home/mencio/.rvm/gems/ruby-1.8.7-p299@app/gems/padrino-core-0.9.19/lib/padrino-core/application/routing.rb:605:in `catch'
	/home/mencio/.rvm/gems/ruby-1.8.7-p299@app/gems/padrino-core-0.9.19/lib/padrino-core/application/routing.rb:605:in `route!'
	/home/mencio/.rvm/gems/ruby-1.8.7-p299@app/gems/padrino-core-0.9.19/lib/padrino-core/application/routing.rb:596:in `catch'
	/home/mencio/.rvm/gems/ruby-1.8.7-p299@app/gems/padrino-core-0.9.19/lib/padrino-core/application/routing.rb:596:in `route!'
        # cd ...

Kaminari "z pudełka" średnio współpracuje z (całkiem fajną) mieszanką Padrino (frameworku zbudowanego na Sinatrze) oraz Mongoida. Rozwiązanie jest jednak bajecznie proste (chociaż niezbyt eleganckie). Tworzymy sobie plik config/preload/kaminari.rb a w nim umieszczamy poniższy kod:

require 'kaminari/railtie'
require 'kaminari/engine'
require 'kaminari/config'
require 'kaminari/helpers/action_view_extension'
require 'kaminari/helpers/paginator'
require 'kaminari/models/page_scope_methods'
require 'kaminari/models/configuration_methods'
require 'kaminari/models/mongoid_extension'
::Mongoid::Document.send :include, Kaminari::MongoidExtension::Document
::Mongoid::Criteria.send :include, Kaminari::MongoidExtension::Criteria

Następnie w pliku config/boot.rb umieszczamy (w sekcji Padrino.before_load):

  require File.expand_path("config/preload/kaminari", Padrino.root)

Paginacja (pagination) z pomocą Kaminari a paginacja tablic (Array)

Korzystając z 254 Railscasta dot. paginacji z pomocą Kaminari, natknąłem się na problem paginacji tablic. Dawniej korzystałem z WillPagination i tworzyłem sobie instancję tego obiektu, wrzucając co mi tam trzeba. Paginowało ładnie. Niestety czasy się zmieniają i przyszło mi korzystać z Kaminari (które nawiasem mówiąc jest dużo wygodniejsze). Kaminari dodaje tylko scope'y - tak więc z definicji nie obsługuje niczego innego niż ActiveRecord.

Aby obejść ten problem, przygotowałem małe rozszerzenie klasy Array. Z racji tego, że paranoją byłoby ładować do tablicy wszystko (załóżmy że danych jest duuuużo), rozszerzenie to, działa na już "skróconym" (czy też zpaginowanym) zbiorze. Przykład:

Mamy 100 linijek tekstu, chcemy wyświetlać po 10 i skorzystać z paginacji. Wybieramy więc interesującą nas stronę:

@pagin_lines = @lines[20, 10]
@total_lines = @lines.count
@total_lines.paginable(3, 10, @total_lines)

I tyle. Parametry są następujące:

  1. page - na której stronie jesteśmy (którą stronę wyświetlamy),
  2. per_page - ile elementów na stronie,
  3. total_elements_amount - ile jest łącznie elementów.

I to by było na tyle. Cały kod też nie jest skomplikowany:

# Rozszerzenie klasy Array umożliwiające korzystanie z paginacji za pomocą
# pluginu Kaminaro
class Array
  def paginable(page, per_page, total_elements_amount)
    @paginate_page = page
    @paginate_per_page = per_page
    @paginate_total_elements_amount = total_elements_amount
    self.instance_eval <<-EVAL
      def current_page
        @paginate_page.to_i
      end

      def num_pages
        (@paginate_total_elements_amount/@paginate_per_page).floor.to_i
      end

      def limit_value
        @paginate_per_page.to_i
      end

    EVAL
  end
end

Od teraz możemy normalnie wykorzystywać tablicę@total_lines do paginacji:

<%= paginate @total_lines %>

Copyright © 2024 Closer to Code

Theme by Anders NorenUp ↑