Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added headers extra #141

Merged
merged 3 commits into from
Mar 12, 2019
Merged

added headers extra #141

merged 3 commits into from
Mar 12, 2019

Conversation

ddnexus
Copy link
Owner

@ddnexus ddnexus commented Mar 10, 2019

Add RFC-8288 compilant HTTP response headers (and other helpers) useful for API pagination.

  • No need for an extra dependency
  • No need to learn yet another interface
  • It saves quite a lot of memory and CPU
  • It works with any pagy object (including Pagy::Countless) regardless the type of collection
  • It offers more explicit flexibility and simplicity

@ddnexus
Copy link
Owner Author

ddnexus commented Mar 10, 2019

@grosser, @bquorning, @gamafranco, @excid3, @cseelus, @MarieS1415, @NARKOZ, @twnaing...

You feedback would be appreciated!
Thanks

| `semantic` | Add nav, responsive and compact helpers for the Semantic UI CSS [pagination component](https://semantic-ui.com/collections/menu.html) | [semantic.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/semantic.rb), [documentation](extras/semantic.md) |
| `support` | Extra support for features like: incremental, infinite, auto-scroll pagination | [support.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/support.rb), [documentation](extras/support.md) |
| `trim` | Remove the `page=1` param from links | [trim.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/trim.rb), [documentation](extras/trim.md) |
| Extra | Description | Links |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could avoid the giant diff / make blame simler by not aligning the |

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true, but a non-aligned source makes me sick :)

pagy, records = pagy(collection, vars) # any pagy_* backend constructor works
pagy_headers_merge(pagy)
render json: records
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be more universal to do

def render(*args, &block)
  pagy_headers_merge(@pagy) if @pagy
  super
end

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true! This one is a nice minimalist way to just add the headers automatically that works also with existing code. The other one is more a suggestion about encapsulating/decluttering. I will use both suggestions. Thanks


## Files

This extra is composed of 1 small file:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment is kinda redundant

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true. I will remove the stupid comments from all the extras and leave only the links to the files. Thanks


def pagy_headers(pagy)
hash = pagy_headers_hash(pagy)
{ 'Links' => hash[:links].map{|rel, link| %(<#{link}>; rel="#{rel}")}.join(', ') }.tap do |h|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more readable/performant to not use .tap

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably... I will try

rels = { first: 1, prev: pagy.prev, next: pagy.next }.tap{ |r| r[:last] = pagy.last unless countless }
a, b = pagy_url_for(Frontend::MARKER, pagy, :url).split(Frontend::MARKER, 2)
links = Hash[rels.map{|rel, n|[rel, "#{a}#{n}#{b}"] if n}.compact]
{ links: links }.tap do |h|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, prefer no tap

def pagy_headers_hash(pagy)
countless = defined?(Pagy::Countless) && pagy.is_a?(Pagy::Countless)
rels = { first: 1, prev: pagy.prev, next: pagy.next }.tap{ |r| r[:last] = pagy.last unless countless }
a, b = pagy_url_for(Frontend::MARKER, pagy, :url).split(Frontend::MARKER, 2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got some more descriptive names than a/b ?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like part_a and part_b? 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was hoping they have a meaning like url/query "MARKER" is also vague :D

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, MARKER means... it's just a MARKER 😄 ... used to split the string into 2 parts and wrap them around the page number. That was faster than using sub to replace MARKER with the page number (at least in pagy_link_proc), but the 2 parts are just 2 strings without any logical meaning. It's difficult to give them a meaningful name.

countless = defined?(Pagy::Countless) && pagy.is_a?(Pagy::Countless)
rels = { first: 1, prev: pagy.prev, next: pagy.next }.tap{ |r| r[:last] = pagy.last unless countless }
a, b = pagy_url_for(Frontend::MARKER, pagy, :url).split(Frontend::MARKER, 2)
links = Hash[rels.map{|rel, n|[rel, "#{a}#{n}#{b}"] if n}.compact]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if none of them is nil this will be nil ?

[].compact!
=> nil

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure to understand "If none of them": did you mean "if all them are nil"?
BTW, there will be always at least the first page link (even if it is empty), so that should not happen... however I will try to avoid asumptions. Thanks

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if no nil is removed the result will be nil

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't get your comment at all :/ (maybe my morning-dumbness... or just dumbness)
How [].compact! relates to the code there? Is that a suggestion or a warning?

@@ -10,10 +10,23 @@ class Pagy
# see https://ddnexus.github.io/pagy/api/frontend#i18n
I18n = eval(Pagy.root.join('locales', 'utils', 'i18n.rb').read) #rubocop:disable Security/Eval

module Helpers
# This works with all Rack-based frameworks (Sinatra, Padrino, Rails, ...)
def pagy_url_for(page, pagy, path_or_url=:path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def pagy_url_for(page, pagy, path_or_url=:path)
def pagy_url_for(page, pagy, path_or_url: :path)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nicer, but Pagy is ruby 1.9+ compatible since version 2 (I needed to use it in legacy apps with legacy envs)

module Helpers
# This works with all Rack-based frameworks (Sinatra, Padrino, Rails, ...)
def pagy_url_for(page, pagy, path_or_url=:path)
p_vars = pagy.vars; params = request.GET.merge(p_vars[:page_param].to_s => page).merge!(p_vars[:params])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can avoid a merge:

Suggested change
p_vars = pagy.vars; params = request.GET.merge(p_vars[:page_param].to_s => page).merge!(p_vars[:params])
p_vars = pagy.vars; params = request.GET.merge(p_vars[:params])
params[p_vars[:page_param].to_s] = page

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we didn't notice that for so long! Thank

end



Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra newlines ?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ooops

Copy link
Contributor

@grosser grosser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks pretty useable and nice that it's opt-in

@ddnexus ddnexus merged commit 8bd19d6 into dev Mar 12, 2019
@ddnexus ddnexus deleted the feature/headers branch March 12, 2019 04:36
@ddnexus ddnexus added the merged label Mar 12, 2019
@espen
Copy link
Contributor

espen commented Jan 22, 2021

Didn't see this until now. Works great. Thanks 👏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants