feat(view): add Prism, ViewHelpers and PaginationHelpers

This commit is contained in:
dualfroz committed 2026-09-09 10:59:51 +02:00
1 parent 17c4602543
commit d673a50ed4
4 files changed
+128

No files matched your search

+26
View File
@@ -0,0 +1,26 @@
require_relative 'tag_builder'
require_relative 'form_builder'
require_relative 'form_helpers'
require_relative 'url_helpers'
require_relative 'content_for_helpers'
require_relative 'cache_helpers'
require_relative 'pagination_helpers'
require_relative 'asset_helpers'
module Prism
module ViewHelpers
include TagBuilder
include FormHelpers
include UrlHelpers
include ContentForHelpers
include CacheHelpers
include PaginationHelpers
include AssetHelpers
end
module Template
class Context
include ViewHelpers
end
end
end
+47
View File
@@ -0,0 +1,47 @@
module Prism
# Renders page links for an `Orm::Relation` that has been through
# `.page(n)`. Deliberately decoupled from the router/request: the caller
# supplies `path` (a Proc mapping a page number to a URL string), so the
# helper works for `/posts?page=n`, `/blog/:id/comments?page=n`, or
# anything else without knowing about routes itself.
module PaginationHelpers
def paginate(relation, path:, window: 2)
return '' if relation.total_pages <= 1
links = +''
links << pagination_nav_link('Previous', relation.prev_page, path)
pagination_page_numbers(relation, window).each do |page|
links << if page == :gap
content_tag(:span, '...', class: 'page-gap')
else
pagination_page_link(page, relation.current_page, path)
end
end
links << pagination_nav_link('Next', relation.next_page, path)
"#{tag(:nav, class: 'pagination')}#{links}</nav>"
end
private
def pagination_page_numbers(relation, window)
total = relation.total_pages
current = relation.current_page
visible = (1..total).select { |page| page == 1 || page == total || (page - current).abs <= window }
visible.each_with_object([]) do |page, result|
result << :gap if result.any? && result.last != :gap && page - result.last != 1
result << page
end
end
def pagination_page_link(page, current_page, path)
classes = page == current_page ? 'page-link current' : 'page-link'
content_tag(:a, page.to_s, href: path.call(page), class: classes)
end
def pagination_nav_link(text, target_page, path)
return content_tag(:span, text, class: 'page-link disabled') unless target_page
content_tag(:a, text, href: path.call(target_page), class: 'page-link')
end
end
end
+38
View File
@@ -0,0 +1,38 @@
module Prism
# HTML tag construction shared by every other view helper. `tag` and the
# content-returning form of `content_tag` are meant to be used from a
# `<%== %>` template tag (they hand back an already-escaped string);
# the block form of `content_tag` is meant to be used from a plain
# `<% %>` tag and appends itself to the template's output buffer, same
# as `form_for` (see Prism::Template::Context#capture).
module TagBuilder
def tag(name, attributes = {})
"<#{name}#{html_attributes(attributes)}>"
end
def content_tag(name, content = nil, attributes = {})
if block_given?
attributes = content if content.is_a?(Hash)
inner = capture { yield }
@__out << "<#{name}#{html_attributes(attributes)}>#{inner}</#{name}>"
nil
else
"<#{name}#{html_attributes(attributes)}>#{escape_html(content.to_s)}</#{name}>"
end
end
def html_attributes(attributes)
return '' if attributes.nil? || attributes.empty?
attributes.filter_map { |key, value| html_attribute(key, value) }.join
end
private
def html_attribute(key, value)
return nil if value.nil? || value == false
name = key.to_s.tr('_', '-')
return %( #{name}="#{name}") if value == true
%( #{name}="#{escape_html(value.to_s)}")
end
end
end
+17
View File
@@ -0,0 +1,17 @@
module Prism
module UrlHelpers
def link_to(text, url, attributes = {})
content_tag(:a, text, attributes.merge(href: url))
end
def button_to(text, url, method: :post, form: {}, **button_attrs)
verb = method.to_s.upcase
markup = +"<form action=\"#{escape_html(url)}\" method=\"post\"#{html_attributes(form.merge(class: [form[:class], 'button-to'].compact.join(' ')))}>"
markup << hidden_method_field(verb) unless verb == 'POST'
markup << csrf_hidden_field
markup << content_tag(:button, text, { type: 'submit' }.merge(button_attrs))
markup << '</form>'
markup
end
end
end