Skip to content

Commit

Permalink
Default Frozen Strings for Ruby Files (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
berniechiu authored and ddnexus committed Jun 19, 2018
1 parent 981ca8b commit 9afa9d9
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 48 deletions.
5 changes: 3 additions & 2 deletions lib/pagy.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# See Pagy API documentation: https://ddnexus.github.io/pagy/api/pagy
# frozen_string_literal: true

require 'pathname'

Expand All @@ -10,13 +11,13 @@ class OutOfRangeError < StandardError; attr_reader :pagy; def initialize(pagy) @
def self.root; Pathname.new(__FILE__).dirname end

# default vars
VARS = { page:1, items:20, outset:0, size:[1,4,4,1], page_param: :page, params:{}, anchor:''.freeze, link_extra:''.freeze, item_path:'pagy.info.item_name'.freeze }
VARS = { page:1, items:20, outset:0, size:[1,4,4,1], page_param: :page, params:{}, anchor:'', link_extra:'', item_path:'pagy.info.item_name' }

attr_reader :count, :page, :items, :vars, :pages, :last, :offset, :from, :to, :prev, :next

# Merge and validate the options, do some simple aritmetic and set the instance variables
def initialize(vars)
@vars = VARS.merge(vars.delete_if{|_,v| v.nil? || v == ''.freeze }) # default vars + cleaned vars
@vars = VARS.merge(vars.delete_if{|_,v| v.nil? || v == '' }) # default vars + cleaned vars
{ count:0, items:1, outset:0, page:1 }.each do |k,min| # validate instance variables
(@vars[k] && instance_variable_set(:"@#{k}", @vars[k].to_i) >= min) \
or raise(ArgumentError, "expected :#{k} >= #{min}; got #{instance_variable_get(:"@#{k}").inspect}")
Expand Down
13 changes: 7 additions & 6 deletions lib/pagy/extras/bootstrap.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/bootstrap
# frozen_string_literal: true

class Pagy
# Add nav helper for bootstrap pagination
module Frontend

# Pagination for bootstrap: it returns the html with the series of links to the pages
def pagy_nav_bootstrap(pagy)
tags, link, p_prev, p_next = '', pagy_link_proc(pagy, 'class="page-link"'.freeze), pagy.prev, pagy.next
tags, link, p_prev, p_next = +'', pagy_link_proc(pagy, 'class="page-link"'), pagy.prev, pagy.next

tags << (p_prev ? %(<li class="page-item prev">#{link.call p_prev, pagy_t('pagy.nav.prev'.freeze), 'aria-label="previous"'.freeze}</li>)
: %(<li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.prev'.freeze)}</a></li>))
tags << (p_prev ? %(<li class="page-item prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
: %(<li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.prev')}</a></li>))
pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
tags << if item.is_a?(Integer); %(<li class="page-item">#{link.call item}</li>) # page link
elsif item.is_a?(String) ; %(<li class="page-item active">#{link.call item}</li>) # active page
elsif item == :gap ; %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.gap'.freeze)}</a></li>) # page gap
elsif item == :gap ; %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.gap')}</a></li>) # page gap
end
end
tags << (p_next ? %(<li class="page-item next">#{link.call p_next, pagy_t('pagy.nav.next'.freeze), 'aria-label="next"'.freeze}</li>)
: %(<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.next'.freeze)}</a></li>))
tags << (p_next ? %(<li class="page-item next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
: %(<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.next')}</a></li>))
%(<nav class="pagy-nav-boostrap pagination" role="navigation" aria-label="pager"><ul class="pagination">#{tags}</ul></nav>)
end

Expand Down
25 changes: 13 additions & 12 deletions lib/pagy/extras/compact.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/compact
# frozen_string_literal: true

class Pagy
# Add nav helpers for compact pagination
Expand All @@ -7,41 +8,41 @@ module Frontend
# Generic compact pagination: it returns the html with the series of links to the pages
# we use a numeric input tag to set the page and the PagyCompact javascript to navigate
def pagy_nav_compact(pagy, id=caller(1,1)[0].hash)
tags, link, p_prev, p_next, p_page, p_pages = '', pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
tags, link, p_prev, p_next, p_page, p_pages = +'', pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages

tags << %(<nav id="pagy-nav-#{id}" class="pagy-nav-compact pagination" role="navigation" aria-label="pager">)

tags << link.call(MARKER, '', %(style="display: none;" ))
tags << (p_prev ? %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'.freeze), 'aria-label="previous"'.freeze}</span> )
: %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev'.freeze)}</span> ))
tags << (p_prev ? %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> )
: %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev')}</span> ))

input = %(<input type="number" min="1" max="#{p_pages}" value="#{p_page}" style="padding: 0; text-align: center; width: #{p_pages.to_s.length+1}rem;">)
tags << %(<span class="pagy-compact-input" style="margin: 0 0.6rem;">#{pagy_t('pagy.compact.page'.freeze)} #{input} #{pagy_t('pagy.compact.of'.freeze)} #{p_pages}</span> )
tags << %(<span class="pagy-compact-input" style="margin: 0 0.6rem;">#{pagy_t('pagy.compact.page')} #{input} #{pagy_t('pagy.compact.of')} #{p_pages}</span> )

tags << (p_next ? %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'.freeze), 'aria-label="next"'.freeze}</span>)
: %(<span class="page next disabled">#{pagy_t('pagy.nav.next'.freeze)}</span>))
tags << (p_next ? %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
: %(<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>))

tags << %(</nav><script>PagyCompact('#{id}', '#{MARKER}', '#{p_page}');</script>)
end

# Compact pagination for bootstrap: it returns the html with the series of links to the pages
# we use a numeric input tag to set the page and the PagyCompact javascript to navigate
def pagy_nav_bootstrap_compact(pagy, id=caller(1,1)[0].hash)
tags, link, p_prev, p_next, p_page, p_pages = '', pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
tags, link, p_prev, p_next, p_page, p_pages = +'', pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages

tags << %(<nav id="pagy-nav-#{id}" class="pagy-nav-bootstrap-compact pagination" role="navigation" aria-label="pager">)

tags << link.call(MARKER, '', %(style="display: none;" ))

tags << %(<div class="btn-group" role="group">)
tags << (p_prev ? link.call(p_prev, pagy_t('pagy.nav.prev'.freeze), 'aria-label="previous" class="prev btn btn-primary"'.freeze)
: %(<a class="prev btn btn-primary disabled" href="#">#{pagy_t('pagy.nav.prev'.freeze)}</a>))
tags << (p_prev ? link.call(p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous" class="prev btn btn-primary"')
: %(<a class="prev btn btn-primary disabled" href="#">#{pagy_t('pagy.nav.prev')}</a>))

input = %(<input type="number" min="1" max="#{p_pages}" value="#{p_page}" style="padding: 0; border: none; text-align: center; width: #{p_pages.to_s.length+1}rem;">)
tags << %(<div class="pagy-compact-input btn btn-primary disabled">#{pagy_t('pagy.compact.page'.freeze)} #{input} #{pagy_t('pagy.compact.of'.freeze)} #{p_pages}</div>)
tags << %(<div class="pagy-compact-input btn btn-primary disabled">#{pagy_t('pagy.compact.page')} #{input} #{pagy_t('pagy.compact.of')} #{p_pages}</div>)

tags << (p_next ? link.call(p_next, pagy_t('pagy.nav.next'.freeze), 'aria-label="next" class="next btn btn-primary"'.freeze)
: %(<a class="next btn btn-primary disabled" href="#">#{pagy_t('pagy.nav.next'.freeze)}</a>))
tags << (p_next ? link.call(p_next, pagy_t('pagy.nav.next'), 'aria-label="next" class="next btn btn-primary"')
: %(<a class="next btn btn-primary disabled" href="#">#{pagy_t('pagy.nav.next')}</a>))

tags << %(</div></nav><script>PagyCompact('#{id}', '#{MARKER}', '#{p_page}');</script>)
end
Expand Down
5 changes: 3 additions & 2 deletions lib/pagy/extras/items.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/items
# frozen_string_literal: true

class Pagy

Expand Down Expand Up @@ -31,11 +32,11 @@ def pagy_url_for(page, pagy)
def pagy_items_selector(pagy, id=caller(1,1)[0].hash)
pagy = pagy.clone; p_vars = pagy.vars; p_items = p_vars[:items]; p_vars[:items] = "#{MARKER}-items-"

tags = %(<span id="pagy-items-#{id}">)
tags = +%(<span id="pagy-items-#{id}">)

tags << %(<a href="#{pagy_url_for("#{MARKER}-page-", pagy)}"></a>)
input = %(<input type="number" min="1" max="#{p_vars[:max_items]}" value="#{p_items}" style="padding: 0; text-align: center; width: #{p_items.to_s.length+1}rem;">)
tags << %(#{pagy_t('pagy.items.show'.freeze)} #{input} #{pagy_t('pagy.items.items'.freeze)})
tags << %(#{pagy_t('pagy.items.show')} #{input} #{pagy_t('pagy.items.items')})

tags << %(</span><script>PagyItems('#{id}', '#{MARKER}', #{pagy.from});</script>)
end
Expand Down
25 changes: 13 additions & 12 deletions lib/pagy/extras/responsive.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/responsive
# frozen_string_literal: true

require 'json'

Expand Down Expand Up @@ -33,18 +34,18 @@ module Frontend
# Generic responsive pagination: it returns the html with the series of links to the pages
# we build the tags as a json object string and render them with the PagyResponsive javascript
def pagy_nav_responsive(pagy, id=caller(1,1)[0].hash)
tags, link, p_prev, p_next, responsive = '{', pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.responsive
tags, link, p_prev, p_next, responsive = +'{', pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.responsive

tags << (p_prev ? %('prev':'<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'.freeze), 'aria-label="previous"'.freeze}</span> ',)
: %('prev':'<span class="page prev disabled">#{pagy_t('pagy.nav.prev'.freeze)}</span> ',))
tags << (p_prev ? %('prev':'<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> ',)
: %('prev':'<span class="page prev disabled">#{pagy_t('pagy.nav.prev')}</span> ',))
responsive[:items].each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
tags << if item.is_a?(Integer); %('#{item}':'<span class="page">#{link.call item}</span> ',) # page link
elsif item.is_a?(String) ; %('#{item}':'<span class="page active">#{item}</span> ',) # current page
elsif item == :gap ; %('#{item}':'<span class="page gap">#{pagy_t('pagy.nav.gap'.freeze)}</span> ',) # page gap
elsif item == :gap ; %('#{item}':'<span class="page gap">#{pagy_t('pagy.nav.gap')}</span> ',) # page gap
end
end
tags << (p_next ? %('next':'<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'.freeze), 'aria-label="next"'.freeze}</span>'})
: %('next':'<span class="page next disabled">#{pagy_t('pagy.nav.next'.freeze)}</span>'}))
tags << (p_next ? %('next':'<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>'})
: %('next':'<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>'}))
script = %(<script>PagyResponsive('#{id}', #{tags}, #{responsive[:widths].to_json}, #{responsive[:series].to_json});</script>)
%(<nav id="pagy-nav-#{id}" class="pagy-nav-responsive pagination" role="navigation" aria-label="pager"></nav>#{script})
end
Expand All @@ -53,18 +54,18 @@ def pagy_nav_responsive(pagy, id=caller(1,1)[0].hash)
# Responsive pagination for bootstrap: it returns the html with the series of links to the pages
# we build the tags as a json object string and render them with the PagyResponsive javascript
def pagy_nav_bootstrap_responsive(pagy, id=caller(1,1)[0].hash)
tags, link, p_prev, p_next, responsive = '{', pagy_link_proc(pagy, 'class="page-link"'.freeze), pagy.prev, pagy.next, pagy.responsive
tags, link, p_prev, p_next, responsive = +'{', pagy_link_proc(pagy, 'class="page-link"'), pagy.prev, pagy.next, pagy.responsive

tags << (p_prev ? %('prev':'<li class="page-item prev">#{link.call p_prev, pagy_t('pagy.nav.prev'.freeze), 'aria-label="previous"'.freeze}</li>',)
: %('prev':'<li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.prev'.freeze)}</a></li>',))
tags << (p_prev ? %('prev':'<li class="page-item prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>',)
: %('prev':'<li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.prev')}</a></li>',))
responsive[:items].each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
tags << if item.is_a?(Integer); %('#{item}':'<li class="page-item">#{link.call item}</li>',) # page link
elsif item.is_a?(String) ; %('#{item}':'<li class="page-item active">#{link.call item}</li>',) # active page
elsif item == :gap ; %('#{item}':'<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.gap'.freeze)}</a></li>',) # page gap
elsif item == :gap ; %('#{item}':'<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.gap')}</a></li>',) # page gap
end
end
tags << (p_next ? %('next':'<li class="page-item next">#{link.call p_next, pagy_t('pagy.nav.next'.freeze), 'aria-label="next"'.freeze}</li>'})
: %('next':'<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.next'.freeze)}</a></li>'}))
tags << (p_next ? %('next':'<li class="page-item next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>'})
: %('next':'<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.next')}</a></li>'}))
script = %(<script>PagyResponsive('#{id}', #{tags}, #{responsive[:widths].to_json}, #{responsive[:series].to_json});</script>)
%(<nav id="pagy-nav-#{id}" class="pagy-nav-bootstrap-responsive pagination" role="navigation" aria-label="pager"><ul class="pagination"></ul></nav>#{script})
end
Expand Down
29 changes: 15 additions & 14 deletions lib/pagy/frontend.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# See Pagy::Frontend API documentation: https://ddnexus.github.io/pagy/api/frontend
# frozen_string_literal: true

require 'yaml'

Expand All @@ -10,26 +11,26 @@ module Frontend

# Generic pagination: it returns the html with the series of links to the pages
def pagy_nav(pagy)
tags, link, p_prev, p_next = '', pagy_link_proc(pagy), pagy.prev, pagy.next
tags, link, p_prev, p_next = +'', pagy_link_proc(pagy), pagy.prev, pagy.next

tags << (p_prev ? %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'.freeze), 'aria-label="previous"'.freeze}</span> )
: %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev'.freeze)}</span> ))
tags << (p_prev ? %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> )
: %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev')}</span> ))
pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
tags << if item.is_a?(Integer); %(<span class="page">#{link.call item}</span> ) # page link
elsif item.is_a?(String) ; %(<span class="page active">#{item}</span> ) # current page
elsif item == :gap ; %(<span class="page gap">#{pagy_t('pagy.nav.gap'.freeze)}</span> ) # page gap
elsif item == :gap ; %(<span class="page gap">#{pagy_t('pagy.nav.gap')}</span> ) # page gap
end
end
tags << (p_next ? %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'.freeze), 'aria-label="next"'.freeze}</span>)
: %(<span class="page next disabled">#{pagy_t('pagy.nav.next'.freeze)}</span>))
tags << (p_next ? %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
: %(<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>))
%(<nav class="pagy-nav pagination" role="navigation" aria-label="pager">#{tags}</nav>)
end


# Return examples: "Displaying items 41-60 of 324 in total" or "Displaying Products 41-60 of 324 in total"
def pagy_info(pagy)
name = pagy_t(pagy.vars[:item_path], count: pagy.count)
path = pagy.pages == 1 ? 'pagy.info.single_page'.freeze : 'pagy.info.multiple_pages'.freeze
path = pagy.pages == 1 ? 'pagy.info.single_page' : 'pagy.info.multiple_pages'
pagy_t(path, item_name: name, count: pagy.count, from: pagy.from, to: pagy.to)
end

Expand All @@ -45,28 +46,28 @@ def pagy_url_for(page, pagy)
def pagy_get_params(params) params end


MARKER = "-pagy-#{'pagy'.hash}-".freeze
MARKER = "-pagy-#{'pagy'.hash}-"

# Returns a performance optimized proc to generate the HTML links
# Benchmarked on a 20 link nav: it is ~27x faster and uses ~13x less memory than rails' link_to
def pagy_link_proc(pagy, link_extra=''.freeze)
def pagy_link_proc(pagy, link_extra='')
p_prev, p_next = pagy.prev, pagy.next
a, b = %(<a href="#{pagy_url_for(MARKER, pagy)}" #{pagy.vars[:link_extra]} #{link_extra}).split(MARKER, 2)
-> (n, text=n, extra=''.freeze) { "#{a}#{n}#{b}#{ if n == p_prev ; ' rel="prev"'.freeze
elsif n == p_next ; ' rel="next"'.freeze
else ''.freeze end } #{extra}>#{text}</a>" }
-> (n, text=n, extra='') { "#{a}#{n}#{b}#{ if n == p_prev ; ' rel="prev"'
elsif n == p_next ; ' rel="next"'
else '' end } #{extra}>#{text}</a>" }
end

# Pagy::Frontend::I18N constant
I18N_DATA = YAML.load_file(Pagy.root.join('locales', 'pagy.yml')).first[1]
zero_one = ['zero'.freeze, 'one'.freeze]; I18N = { plurals: -> (c) {zero_one[c] || 'other'.freeze}}
zero_one = ['zero', 'one']; I18N = { plurals: -> (c) {zero_one[c] || 'other'}}
def I18N.load_file(file) I18N_DATA.replace(YAML.load_file(file).first[1]) end

# Similar to I18n.t for interpolation and pluralization but without translation
# Use only for single-language apps: it is specialized for Pagy and 5x faster than I18n.t
# See also https://ddnexus.github.io/pagy/extras/i18n to use the standard I18n gem instead
def pagy_t(path, vars={})
value = I18N_DATA.dig(*path.to_s.split('.'.freeze)) or return %(translation missing: "#{path}")
value = I18N_DATA.dig(*path.to_s.split('.')) or return %(translation missing: "#{path}")
if value.is_a?(Hash)
vars.key?(:count) or return value
plural = I18N[:plurals].call(vars[:count])
Expand Down

0 comments on commit 9afa9d9

Please sign in to comment.