Skip to content

Commit

Permalink
refactoring of searchkick extra
Browse files Browse the repository at this point in the history
  • Loading branch information
ddnexus committed Feb 22, 2019
1 parent 22afe14 commit 7bb34e3
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 108 deletions.
96 changes: 65 additions & 31 deletions docs/extras/searchkick.md
Expand Up @@ -3,60 +3,94 @@ title: Searchkick
---
# Searchkick Extra

Paginate `Searchkick::Results` objects efficiently avoiding expensive oject-wrapping and without overriding.

The `Searchkick::Results` object has already done pagination calculations, we just need to tell Pagy how to read page and total values. It is expected that you let Searchkick handle the `page` and `per_page` variables instead of providing those values as options to Pagy.
This extra deals with the pagination of `Searchkick::Results` objects either by creating a `Pagy` object out of an (already paginated) `Searchkick::Results` object or by creating the `Pagy` and `Searchkick::Results` objects from the backend params.

## Synopsis

See [extras](../extras.md) for general usage info.

In the `pagy.rb` initializer:
Require the extra in the `pagy.rb` initializer:

```ruby
require 'pagy/extras/searchkik'
```

### Passive mode

If you have an already paginated `Searchkick::Results` object, you can get the `Pagy` object out of it:

```ruby
require 'pagy/extras/searchkick'
@results = Model.search('*', from: 0; size: 10, ...)
@pagy = Pagy.new_from_searchkik(@results, ...)
```

In a controller:
### Active Mode

If you want Pagy to control the pagination, getting the page from the params, and returning both the `Pagy` and the `Searchkick::Results` objects automatically (from the backend params):

Extend your model:

```ruby
@pagy_s, @models = pagy_searchkick(
Model.search("*",
{
where: {
...
},
fields: [...],
select: [...],
load: true,
page: params[:page],
per_page: 25
}
)
,
...
)

# independently paginate some other collections as usual
@pagy_b, @records = pagy(some_scope, ...)
extend Pagy::Search
```

In a controller use `pagy_search` in place of `search`:

```ruby
records = Article.pagy_search(params[:q]).results
@pagy, @records = pagy_searchkik(records, items: 10)
```

## Files

This extra is composed of 1 file:

- [searchkick.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/searchkick.rb)
- [searchkik.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/searchkik.rb)

## Pagy.new_from_searchkick

This constructor accepts an `Searchkick::Results` as the first argument, plus the usual optional variable hash. It sets the `:items`, `:page` and `:count` pagy variables extracted/calculated out of the `Searchkick::Results` object.

```ruby
@results = Model.search('*', page: 2; per_page: 10, ...)
@pagy = Pagy.new_from_searchkik(@results, ...)
```

**Notice**: you have to take care of managing all the params manually. If you prefer to manage the pagination automatically, see below.

## Pagy::Search

Extend your model with the `Pagy::Search` micro-moudule (see [pagy_search.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/pagy_search.rb))

```ruby
extend Pagy::Search
```

The `Pagy::Search` adds the `pagy_search` class method that you must use in place of the standard `search` method when you want to paginate the search response.

### pagy_search(query_or_payload, options={})

This method accepts the same arguments of the `search` method and you must use it in its place. This extra uses it in order to capture the arguments, automatically merging the calculated `:page` and `:per_page` options before passing them to the standard `search` method internally.

## Methods

This extra adds the `pagy_searchkick` method to the `Pagy::Backend` to be used in place (or in parallel) of the `pagy` method when you have to paginate a `Searchkick::Results` object. It also adds a `pagy_searchkick_get_variables` sub-method, used for easy customization of variables by overriding.
This extra adds the `pagy_searchkick` method to the `Pagy::Backend` to be used when you have to paginate a `Searchkick::Results` object. It also adds a `pagy_searchkik_get_variables` sub-method, used for easy customization of variables by overriding.

### pagy_searchkik(Model.pagy_search(...), vars={}})

**Notice**: there is no `pagy_searchkick_get_items` method to override, since the items are fetched directly by Searchkick.
This method is similar to the generic `pagy` method, but specialized for Searchkick. (see the [pagy doc](../api/backend.md#pagycollection-varsnil))

### pagy_searchkick(Model.search(...), vars=nil)
It expects to receive a `Model.pagy_search(...)` result and returns a paginated response. You can use it in a couple of ways:

This method is the same as the generic `pagy` method, but specialized for Searchkick. (see the [pagy doc](../api/backend.md#pagycollection-varsnil))
```ruby
@pagy, @results = pagy_searchkik(Model.pagy_search(params[:q]), ...)
...
@records = @results.results

# or directly with the collection you need (e.g. records)
@pagy, @records = pagy_searchkik(Model.pagy_search(params[:q]).results, ...)
```

### pagy_searchkick_get_vars(array)

This sub-method is the same as the `pagy_get_vars` sub-method, but it is called only by the `pagy_searchkick` method. (see the [pagy_get_vars doc](../api/backend.md#pagy_get_varscollection-vars)).
This sub-method is similar to the `pagy_get_vars` sub-method, but it is called only by the `pagy_searchkik` method. (see the [pagy_get_vars doc](../api/backend.md#pagy_get_varscollection-vars)).
14 changes: 7 additions & 7 deletions lib/pagy/extras/items.rb
Expand Up @@ -19,16 +19,16 @@ def pagy_with_items(vars)
end

# add the pagy*_get_vars alias-chained methods for frontend, and defined/required extras
[nil, 'countless', 'elasticsearch_rails'].each do |name|
[nil, 'countless', 'elasticsearch_rails', 'searchkick'].each do |name|
prefix, if_start, if_end = "_#{name}", "if defined?(Pagy::#{name.upcase})", "end" if name
module_eval <<-RUBY
#{if_start}
alias_method :pagy#{prefix}_get_vars_without_items, :pagy#{prefix}_get_vars
def pagy#{prefix}_get_vars_with_items(collection, vars)
pagy_with_items(vars)
pagy#{prefix}_get_vars_without_items(collection, vars)
end
alias_method :pagy#{prefix}_get_vars, :pagy#{prefix}_get_vars_with_items
alias_method :pagy#{prefix}_get_vars_without_items, :pagy#{prefix}_get_vars
def pagy#{prefix}_get_vars_with_items(collection, vars)
pagy_with_items(vars)
pagy#{prefix}_get_vars_without_items(collection, vars)
end
alias_method :pagy#{prefix}_get_vars, :pagy#{prefix}_get_vars_with_items
#{if_end}
RUBY
end
Expand Down
37 changes: 29 additions & 8 deletions lib/pagy/extras/searchkick.rb
Expand Up @@ -2,21 +2,42 @@
# encoding: utf-8
# frozen_string_literal: true

require 'pagy/extras/pagy_search'

class Pagy

# used by the items extra
SEARCHKICK = true

# create a Pagy object from a Searchkick::Results object
def self.new_from_searchkick(results, vars={})
vars[:items] = results.options[:per_page]
vars[:page] = results.options[:page]
vars[:count] = results.total_count
new(vars)
end

# Add specialized backend methods to paginate Searchkick::Results
module Backend ; private
# Return Pagy object and items
def pagy_searchkick(results, vars={})
pagy = Pagy.new(pagy_searchkick_get_vars(results, vars))
return pagy, results

# Return Pagy object and results
def pagy_searchkick(search_args, vars={})
model, term, options, block, *called = search_args
vars = pagy_searchkick_get_vars(nil, vars)
options[:per_page] = vars[:items]
options[:page] = vars[:page]
results = model.search(term, options, &block)
vars[:count] = results.total_count
return Pagy.new(vars), called.empty? ? results : results.send(*called)
end

# Sub-method called only by #pagy_searchkick: here for easy customization of variables by overriding
def pagy_searchkick_get_vars(results, vars)
vars[:count] ||= results.total_count
vars[:page] ||= results.options[:page]
vars[:items] ||= results.options[:per_page]
# the _collection argument is not available when the method is called
def pagy_searchkick_get_vars(_collection, vars)
vars[:items] ||= VARS[:items]
vars[:page] ||= (params[ vars[:page_param] || VARS[:page_param] ] || 1).to_i
vars
end

end
end
66 changes: 37 additions & 29 deletions test/pagy/extras/items_test.rb
Expand Up @@ -4,6 +4,7 @@
require_relative '../../test_helper'
require 'pagy/extras/countless'
require_relative '../../test_helper/elasticsearch_rails'
require_relative '../../test_helper/searchkick'
require 'pagy/extras/items'

SingleCov.covered! unless ENV['SKIP_SINGLECOV']
Expand Down Expand Up @@ -40,8 +41,10 @@
pagy, _ = backend.send method, @collection, vars
pagy.items.must_equal 15
end
pagy, _ = backend.send :pagy_elasticsearch_rails, ElasticsearchRailsModel.pagy_search('a').records, vars
pagy.items.must_equal 15
[[:pagy_elasticsearch_rails, ElasticsearchRailsModel], [:pagy_searchkick, SearchkickModel]].each do |meth, mod|
pagy, _ = backend.send meth, mod.pagy_search('a').records, vars
pagy.items.must_equal 15
end
end

it 'uses the params' do
Expand All @@ -54,9 +57,10 @@
pagy, _ = backend.send method, @collection, vars
pagy.items.must_equal 12
end

pagy, _ = backend.send :pagy_elasticsearch_rails, ElasticsearchRailsModel.pagy_search('a').records, vars
pagy.items.must_equal 12
[[:pagy_elasticsearch_rails, ElasticsearchRailsModel], [:pagy_searchkick, SearchkickModel]].each do |meth, mod|
pagy, _ = backend.send meth, mod.pagy_search('a').records, vars
pagy.items.must_equal 12
end
end

it 'overrides the params' do
Expand All @@ -69,9 +73,10 @@
pagy, _ = backend.send method, @collection, vars
pagy.items.must_equal 21
end

pagy, _ = backend.send :pagy_elasticsearch_rails, ElasticsearchRailsModel.pagy_search('a').records, vars
pagy.items.must_equal 21
[[:pagy_elasticsearch_rails, ElasticsearchRailsModel], [:pagy_searchkick, SearchkickModel]].each do |meth, mod|
pagy, _ = backend.send meth, mod.pagy_search('a').records, vars
pagy.items.must_equal 21
end
end

it 'uses the max_items default' do
Expand All @@ -84,9 +89,10 @@
pagy, _ = backend.send method, @collection, vars
pagy.items.must_equal 100
end

pagy, _ = backend.send :pagy_elasticsearch_rails, ElasticsearchRailsModel.pagy_search('a').records, vars
pagy.items.must_equal 100
[[:pagy_elasticsearch_rails, ElasticsearchRailsModel], [:pagy_searchkick, SearchkickModel]].each do |meth, mod|
pagy, _ = backend.send meth, mod.pagy_search('a').records, vars
pagy.items.must_equal 100
end
end

it 'doesn\'t limit the items from vars' do
Expand All @@ -99,9 +105,10 @@
pagy, _ = backend.send method, @collection, vars
pagy.items.must_equal 1000
end

pagy, _ = backend.send :pagy_elasticsearch_rails, ElasticsearchRailsModel.pagy_search('a').records, vars
pagy.items.must_equal 1000
[[:pagy_elasticsearch_rails, ElasticsearchRailsModel], [:pagy_searchkick, SearchkickModel]].each do |meth, mod|
pagy, _ = backend.send meth, mod.pagy_search('a').records, vars
pagy.items.must_equal 1000
end
end

it 'doesn\'t limit the items from default' do
Expand All @@ -115,10 +122,10 @@
pagy, _ = backend.send method, @collection, vars
pagy.items.must_equal 1000
end

pagy, _ = backend.send :pagy_elasticsearch_rails, ElasticsearchRailsModel.pagy_search('a').records, vars
pagy.items.must_equal 1000

[[:pagy_elasticsearch_rails, ElasticsearchRailsModel], [:pagy_searchkick, SearchkickModel]].each do |meth, mod|
pagy, _ = backend.send meth, mod.pagy_search('a').records, vars
pagy.items.must_equal 1000
end
Pagy::VARS[:max_items] = 100 # reset default
end

Expand All @@ -132,9 +139,10 @@
pagy, _ = backend.send method, @collection, vars
pagy.items.must_equal 14
end

pagy, _ = backend.send :pagy_elasticsearch_rails, ElasticsearchRailsModel.pagy_search('a').records, vars
pagy.items.must_equal 14
[[:pagy_elasticsearch_rails, ElasticsearchRailsModel], [:pagy_searchkick, SearchkickModel]].each do |meth, mod|
pagy, _ = backend.send meth, mod.pagy_search('a').records, vars
pagy.items.must_equal 14
end
end

it 'uses items_param from default' do
Expand All @@ -148,10 +156,10 @@
pagy, _ = backend.send method, @collection, vars
pagy.items.must_equal 15
end

pagy, _ = backend.send :pagy_elasticsearch_rails, ElasticsearchRailsModel.pagy_search('a').records, vars
pagy.items.must_equal 15

[[:pagy_elasticsearch_rails, ElasticsearchRailsModel], [:pagy_searchkick, SearchkickModel]].each do |meth, mod|
pagy, _ = backend.send meth, mod.pagy_search('a').records, vars
pagy.items.must_equal 15
end
Pagy::VARS[:items_param] = :items # reset default
end

Expand Down Expand Up @@ -200,10 +208,10 @@

html.must_equal \
%(<span id="test-id">) +
%(<a href="/foo?page=#{Pagy::Frontend::MARKER}-page-&items=#{Pagy::Frontend::MARKER}-items-"></a>) +
%(Show <input type="number" min="1" max="100" value="20" style="padding: 0; text-align: center; width: 3rem;"> items per page) +
%(</span>) +
%(<script type="application/json" class="pagy-json">["items","test-id","#{Pagy::Frontend::MARKER}",41]</script>)
%(<a href="/foo?page=#{Pagy::Frontend::MARKER}-page-&items=#{Pagy::Frontend::MARKER}-items-"></a>) +
%(Show <input type="number" min="1" max="100" value="20" style="padding: 0; text-align: center; width: 3rem;"> items per page) +
%(</span>) +
%(<script type="application/json" class="pagy-json">["items","test-id","#{Pagy::Frontend::MARKER}",41]</script>)
end

end
Expand Down

0 comments on commit 7bb34e3

Please sign in to comment.