Documentation is lacking for will_paginate custom renderers:
There is no documentation how to write your own link renderer, but the source code is pretty self-explanatory. Dive into it, and selectively override methods of the LinkRenderer to adjust them to your needs.
Is there any unofficial documentation?
Found a decent blog post about custom will_paginate renderer
module ApplicationHelper
# change the default link renderer for will_paginate
def will_paginate(collection_or_options = nil, options = {})
if collection_or_options.is_a? Hash
options, collection_or_options = collection_or_options, nil
end
unless options[:renderer]
options = options.merge :renderer => MyCustomLinkRenderer
end
super *[collection_or_options, options].compact
end
end
and then in an initializer
class MyCustomLinkRenderer < WillPaginate::ActionView::LinkRenderer do
def container_attributes
{class: "tc cf mv2"}
end
def page_number(page)
if page == current_page
tag(:span, page, class: 'b bg-dark-blue near-white ba b--near-black pa2')
else
link(page, page, class: 'link ba b--near-black near-black pa2', rel: rel_value(page))
end
end
def gap
text = #template.will_paginate_translate(:page_gap) { '…' }
%(<span class="mr2">#{text}</span>)
end
def previous_page
num = #collection.current_page > 1 && #collection.current_page - 1
previous_or_next_page(num, #options[:previous_label], 'link ba near-black b--near-black pa2')
end
def next_page
num = #collection.current_page < total_pages && #collection.current_page + 1
previous_or_next_page(num, #options[:next_label], 'link ba near-black b--near-black pa2')
end
def previous_or_next_page(page, text, classname)
if page
link(text, page, :class => classname)
else
tag(:span, text, :class => classname + ' bg-dark-blue near-white')
end
end
end
Thanks to previous answer, i wrote this code to use will_paginate with materialize
application_controller.rb
def custom_paginate_renderer
# Return nice pagination for materialize
Class.new(WillPaginate::ActionView::LinkRenderer) do
def container_attributes
{class: "pagination"}
end
def page_number(page)
if page == current_page
"<li class=\"cyan active\">"+link(page, page, rel: rel_value(page))+"</li>"
else
"<li class=\"waves-effect\">"+link(page, page, rel: rel_value(page))+"</li>"
end
end
def previous_page
num = #collection.current_page > 1 && #collection.current_page - 1
previous_or_next_page(num, "<i class=\"material-icons\">chevron_left</i>")
end
def next_page
num = #collection.current_page < total_pages && #collection.current_page + 1
previous_or_next_page(num, "<i class=\"material-icons\">chevron_right</i>")
end
def previous_or_next_page(page, text)
if page
"<li class=\"waves-effect\">"+link(text, page)+"</li>"
else
"<li class=\"waves-effect\">"+text+"</li>"
end
end
end
end
your_controller.rb
# GET /articles/1
def articles
#articles = #articles.paginate(:page => params[:page], :per_page => 20).order(id: :desc)
#custom_paginate_renderer = custom_paginate_renderer
end
your_view.html.erb
<%= will_paginate #articles, renderer: #custom_paginate_renderer %>
Not the most beautiful rails code, but it works
Thanks to the answers which have guided me to write this renderer for Bootstrap 5.
//config/initializers/bootstrap_paginate_renderer.rb
class BootstrapPaginateRenderer < WillPaginate::ActionView::LinkRenderer
def container_attributes
{ class: 'pagination' }
end
def html_container(html)
child = tag(:ul, html, container_attributes)
tag(:nav, child)
end
def page_number(page)
if page == current_page
'<li class="page-item active">' + link(page, page, rel: rel_value(page),class: 'page-link') + '</li>'
else
'<li class="page-item">' + link(page, page, rel: rel_value(page),class: 'page-link') + '</li>'
end
end
def previous_page
num = #collection.current_page > 1 && #collection.current_page - 1
previous_or_next_page(num, '<span aria-hidden="true">«</span>')
end
def next_page
num = #collection.current_page < total_pages && #collection.current_page + 1
previous_or_next_page(num, '<span aria-hidden="true">»</span>')
end
def previous_or_next_page(page, text)
if page
'<li class="page-item">' + link(text, page, class: 'page-link') + '</li>'
else
'<li class="page-item disabled">' + link(text, page, class: 'page-link') + '</li>'
end
end
end
//app/helpers/application_helper.rb
def will_paginate(coll_or_options = nil, options = {})
if coll_or_options.is_a? Hash
options = coll_or_options
coll_or_options = nil
end
unless options[:renderer]
options = options.merge renderer: BootstrapPaginateRenderer
end
super *[coll_or_options, options].compact
end
For anyone looking for a TailwindUI solution, I'm working on one right now using templates as the renderer.
Gist is here.
Related
Trying to use f.input(:activated_at, as: :date_flag) to render as a checkbox, which sends down Time.now for true and nil for false.
https://github.com/postageapp/date_flag/blob/master/lib/date_flag.rb
https://raw.githubusercontent.com/heartcombo/simple_form/main/lib/simple_form/inputs/boolean_input.rb
# frozen_string_literal: true
class DateFlagInput < SimpleForm::Inputs::Base
def input(wrapper_options = {})
template.content_tag(:div, class: "form-check form-switch") do
template.concat(#builder.check_box(attribute_name, {class: 'form-check-input'}, Time.now, nil))
template.concat(template.label(attribute_name, label_text, {class: "form-check-label"}))
end
end
end
This code above gets me a bootstrap5 checkbox, which sends down the time, but cannot render a "checked".
I figured this out!
# frozen_string_literal: true
class DateFlagInput < SimpleForm::Inputs::BooleanInput
def input(wrapper_options = {})
flag_value = object.send(attribute_name)
# date_flag magic
input_html_options[:checked] = flag_value ? (flag_value <= Time.now) : false
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
merged_input_options[:class] = [input_html_classes]
template.content_tag(:div, class: container_class_name) do
build_hidden_field_for_checkbox +
build_check_box_without_hidden_field(merged_input_options) +
#builder.label(label_target, label_text, {class: boolean_label_class})
end
end
def label_translation
if SimpleForm.translate_labels && (translated_label = translate_from_namespace(:labels))
translated_label
elsif object.class.respond_to?(:human_attribute_name)
object.class.human_attribute_name(reflection_or_attribute_name.to_s.gsub(/_at$/, ''))
else
attribute_name.to_s.gsub(/_at$/, '').humanize.titleize
end
end
def container_class_name
"form-check"
end
def label(wrapper_options)
template.label_tag(nil, ' '.html_safe)
end
def input_html_classes
'form-check-input'
end
def boolean_label_class
"form-check-label"
end
def checked_value
Time.now
end
def unchecked_value
0
end
end
adding the checked: option to show how the checkbox should render.
the unchecked_value cannot be nil because nil does gets excluded from the params. using nil or 0 both worked for the unchecked_value!
I created this class for assembling the text based on the length of the product attributes for sharing on twitter.
My questions:
Is this the good approach to tackle the problem? If not this then what? (where should I put the class and the methods, how to invoke it, etc.)
If this is the good approach then what should be changed? For instance I feel the def twitter_share_text shouldn't be in the product.rb.
show.html.erb
<a class="twitter-share" data-behavior="twitter-share"
data-twittertext="<%= #product.twitter_share_text %>"
data-twitterurl="<%= product_url(#product) %>"
data-twitteranchor>
<i class="fa fa-lg fa-twitter"></i>
</a>
product.rb
def twitter_share_text
TwitterProductShare.new(self).return_text
end
app/services/twitter_product_share.rb
class TwitterProductShare
URL_LENGTH = 23 #defined by twitter API
SPACE_LENGTH = 1
TWITTER_MAX = 140
attr_reader :name, :oneliner
def initialize(product)
#name = product.name
#oneliner = product.oneliner
end
def return_text
if full_length <= TWITTER_MAX
return basic_text
else
return basic_text[0...-(difference + text_end.length)] + text_end
end
end
private
def basic_text
"#{name}: #{oneliner}"
end
def difference
full_length - TWITTER_MAX
end
def full_length
basic_text.length + SPACE_LENGTH + URL_LENGTH
end
def text_end
"..."
end
end
I think code like that belongs into a view helper:
# in app/helpers/product_helper.rb
def twitter_share_link(product)
data = {
behavior: 'twitter-share',
twittertext: TwitterProductShare.new(product).return_text,
twitterurl: product_url(product),
twitteranchor: 'twitteranchor'
}
link_to(class: 'twitter-share', data: data) do
tag(:i, class: 'fa fa-lg fa-twitter')
end
end
In your view use this helper like this:
<%= twitter_share_link(#product) %>
Or you could even return the whole data hash from the TwitterProductShare.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 8 years ago.
Improve this question
In three different layouts I have to display social links utilizing three different configurations. I wanted to setup a few methods to handle this, but I
feel like this code could be optimized further. The section attached can be called by social_links(["show_div","show_email"]) for example which will
trigger various configurations from the methods. Is this overkill? The other thought I had was to break these down even further and only include the
link instead of the html code.
Tips to refactor? Optimize?
In my application_helper.rb file:
# shared social links displayed in multiple layouts
def social_links(view_options = nil)
html=%(<ul class="social-links">)
html+=%(<li>) + blog_link(view_options) + %(</li>)
html+=%(<li>) + podcast_link(view_options) + %(</li>)
html+=%(<li>) + twitter_link(view_options) + %(</li>)
html+=%(<li>) + instagram_link(view_options) + %(</li>)
html+=%(<li>) + facebook_link(view_options) + %(</li>)
html+=%(<li>) + pinterest_link(view_options) + %(</li>)
if view_options && view_options.include?("show_email")
html+=%(<li>) + contact_us_link(view_options) + %(</li>)
end
html+=%(</ul>)
raw(html.strip)
end
def twitter_link(view_options = nil)
html=%(<a href="#{ENV['SOCIAL_TWITTER']}" target="_blank">)
if view_options && view_options.include?("show_div")
html+=%(<div>)
end
html+=%(<i class="fa fa-twitter-square"></i>)
if view_options && view_options.include?("show_desc")
html+=%(Twitter)
end
if view_options && view_options.include?("show_div")
html+=%(</div>)
end
html+=%(</a>)
raw(html.strip)
end
def instagram_link(view_options = nil)
html=%(
<a href="#{ENV['SOCIAL_INSTAGRAM']}" target="_blank">)
if view_options && view_options.include?("show_div")
html+=%(<div>)
end
html+=%(<i class="fa fa-instagram"></i>)
if view_options && view_options.include?("show_desc")
html+=%(Instagram)
end
if view_options && view_options.include?("show_div")
html+=%(</div>)
end
html+=%(</a>)
raw(html.strip)
end
def facebook_link(view_options = nil)
html=%(
<a href="#{ENV['SOCIAL_FACEBOOK']}" target="_blank">)
if view_options && view_options.include?("show_div")
html+=%(<div>)
end
html+=%(<i class="fa fa-facebook-square"></i>)
if view_options && view_options.include?("show_desc")
html+=%(Facebook)
end
if view_options && view_options.include?("show_div")
html+=%(</div>)
end
html+=%(</a>)
raw(html.strip)
end
def pinterest_link(view_options = nil)
html=%(
<a href="#{ENV['SOCIAL_PINTEREST']}" target="_blank">)
if view_options && view_options.include?("show_div")
html+=%(<div>)
end
html+=%(<i class="fa fa-pinterest-square"></i>)
if view_options && view_options.include?("show_desc")
html+=%(Pinterest)
end
if view_options && view_options.include?("show_div")
html+=%(</div>)
end
html+=%(</a>)
raw(html.strip)
end
def blog_link(view_options = nil)
html=%(
<a href="#{ENV['SOCIAL_BLOG']}" target="_blank">)
if view_options && view_options.include?("show_div")
html+=%(<div>)
end
html+=%(<i class="fa fa-rss-square"></i>)
if view_options && view_options.include?("show_desc")
html+=%(Blog)
end
if view_options && view_options.include?("show_div")
html+=%(</div>)
end
html+=%(</a>)
raw(html.strip)
end
def podcast_link(view_options = nil)
html=%(
<a href="#{ENV['SOCIAL_PODCAST']}" target="_blank">)
if view_options && view_options.include?("show_div")
html+=%(<div>)
end
html+=%(<i class="fa fa-caret-square-o-right"></i>)
if view_options && view_options.include?("show_desc")
html+=%(Podcast)
end
if view_options && view_options.include?("show_div")
html+=%(</div>)
end
html+=%(</a>)
raw(html.strip)
end
def contact_us_link(view_options = nil)
html=%(
<a href="/contact_us" target="_blank">)
if view_options && view_options.include?("show_div")
html+=%(<div>)
end
html+=%(<i class="fa fa-envelope"></i>)
if view_options && view_options.include?("show_desc")
html+=%(Contact)
end
if view_options && view_options.include?("show_div")
html+=%(</div>)
end
html+=%(</a>)
raw(html.strip)
end
UPDATE ONE: Partial Method
After further conversation and thought, I changed the helpers to a partial _social_links.html.slim
- social_twitter = "<i class='fa fa-twitter-square'></i>".html_safe
- social_instagram = "<i class='fa fa-instagram'></i>".html_safe
- social_facebook = "<i class='fa fa-facebook-square'></i>".html_safe
- social_pinterest = "<i class='fa fa-pinterest-square'></i>".html_safe
- social_rss = "<i class='fa fa-rss-square'></i>".html_safe
- social_podcast = "<i class='fa fa-caret-square-o-right'></i>".html_safe
- social_email = "<i class='fa fa-envelope'></i>".html_safe
ul[class="social-links"]
li
a[href="#{ENV['SOCIAL_TWITTER']}" target="_blank"]
- if collection && collection.include?("show_div")
div
= social_twitter
- else
= social_twitter
- if collection && collection.include?("show_desc")
| Twitter
li
a[href="#{ENV['SOCIAL_INSTAGRAM']}" target="_blank"]
- if collection && collection.include?("show_div")
div
= social_instagram
- else
= social_instagram
- if collection && collection.include?("show_desc")
| Instagram
li
a[href="#{ENV['SOCIAL_FACEBOOK']}" target="_blank"]
- if collection && collection.include?("show_div")
div
= social_facebook
- else
= social_facebook
- if collection && collection.include?("show_desc")
| Facebook
li
a[href="#{ENV['SOCIAL_PINTEREST']}" target="_blank"]
- if collection && collection.include?("show_div")
div
= social_pinterest
- else
= social_pinterest
- if collection && collection.include?("show_desc")
| Pinterest
li
a[href="#{ENV['SOCIAL_BLOG']}" target="_blank"]
- if collection && collection.include?("show_div")
div
= social_rss
- else
= social_rss
- if collection && collection.include?("show_desc")
| Blog
li
a[href="#{ENV['SOCIAL_PODCAST']}" target="_blank"]
- if collection && collection.include?("show_div")
div
= social_podcast
- else
= social_podcast
- if collection && collection.include?("show_desc")
| Podcast
- if collection && collection.include?("show_email")
li
a[href="/contact_us" target="_blank"]
- if collection && collection.include?("show_div")
div
= social_email
- else
= social_email
- if collection && collection.include?("show_desc")
| Contact
The partial can be invoked by:
render partial: "layouts/social_links", locals: { collection: ["show_div","show_email"]} or
render partial: "layouts/social_links", locals: { collection: nil}
UPDATE TWO: OO Method suggest below
This can be called like:
social_links()
social_links(show_desc: true, show_list: %w{contact})
social_links(show_div: true, show_list: %w{contact})
In the folder /helpers/social_link I have my general helper:
module SocialLink
module SocialLinkHelper
# ****************************************************************************************************
# ****************************************************************************************************
DEFAULT_SOCIAL_PROVIDERS = %w{twitter facebook instagram pinterest blog podcast}
def social_links(options={})
content_tag :ul, class: "social-links" do
provider_link(options)
end
end
def provider_link(options={})
social_providers = DEFAULT_SOCIAL_PROVIDERS
social_providers = social_providers + options[:show_list] if options[:show_list]
social_providers.each do |provider|
klass = "SocialLink::SocialLinkClass::#{provider.titleize}Link".constantize
link = klass.new(options)
concat link.generate
end
end
# ****************************************************************************************************
# ****************************************************************************************************
end
end
In the folder /helpers/social_link I have my control class:
module SocialLink
module SocialLinkClass
class SocialBase < ActionView::Base
include ActionView::Helpers::TagHelper
def initialize(options)
#show_div = options[:show_div] || nil
#show_desc = options[:show_desc] || nil
end
def generate
content_tag :li do
content = content_tag(:i, nil, class: self.class.icon)
content = content + self.class.description.titleize if #show_desc
content = "<div>#{content}</div>" if #show_div
link_to(content.html_safe, self.class.link)
end
end
end
# ****************************************************************************************************
class ContactLink < SocialBase
def self.link
"/contact_us"
end
def self.description
"contact"
end
def self.icon
"fa fa-envelope"
end
end
# ****************************************************************************************************
class BlogLink < SocialBase
def self.link
ENV['SOCIAL_BLOG']
end
def self.description
"blog"
end
def self.icon
"fa fa-rss-square"
end
end
# ****************************************************************************************************
class PodcastLink < SocialBase
def self.link
ENV['SOCIAL_PODCAST']
end
def self.description
"podcast"
end
def self.icon
"fa fa-caret-square-o-right"
end
end
# ****************************************************************************************************
class PinterestLink < SocialBase
def self.link
ENV['SOCIAL_PINTEREST']
end
def self.description
"pinterest"
end
def self.icon
"fa fa-pinterest-square"
end
end
# ****************************************************************************************************
class InstagramLink < SocialBase
def self.link
ENV['SOCIAL_INSTAGRAM']
end
def self.description
"instagram"
end
def self.icon
"fa fa-instagram"
end
end
# ****************************************************************************************************
class TwitterLink < SocialBase
def self.link
ENV['SOCIAL_TWITTER']
end
def self.description
"twitter"
end
def self.icon
"fa fa-twitter-square"
end
end
# ****************************************************************************************************
class FacebookLink < SocialBase
def self.link
ENV['SOCIAL_FACEBOOK']
end
def self.description
"facebook"
end
def self.icon
"fa fa-facebook-square"
end
end
# ****************************************************************************************************
end
end
UPDATE THREE: My buddy #drewtempelmeyer approach
File: _social_link.html.erb
<%= social_icon service, collection %>
<% if show_desc?(collection) %>
| <%= service.titleize %>
<% end %>
File: profile.html.erb
<%= render_social_link 'facebook', %w(show_div show_desc) %>
File: social_helper.rb
module SocialHelper
def render_social_link(service = 'facebook', collection = nil)
render partial: 'shared/social_link', locals: {
service: service, collection: collection
}
end
def show_div?(collection)
collection && collection.include?('show_div')
end
def show_desc?(collection)
collection && collection.include?('show_desc')
end
def social_icon(service, collection)
content = content_tag(:li, nil, class: "fa fa-#{service.downcase}")
content = "<div>#{content}</div>" if show_div?(collection)
content
end
end
This would be a good use case of applying OOP on view logic. Partials can also do the job but with too much options it will looks messy.
Starting with a general helper social_links is nothing wrong. Helper is just to help the view.
But later, you need classes to do the detailed job.
Your view which calls the helper
<%= social_links show_div: 'foo' %>
The general helper
SOCIAL_PROVIDERS = ['Twitter', 'Facebook', 'GooglePlus']
def social_links(options)
content_tag :ul do
provider_link(options)
end
end
def provider_links(options)
SOCIAL_PROVIDERS.each do |provider|
klass = "#{provider}Link".constantize
link = klass.new(options)
concat link.generate
end
end
Then the classes
class SocialLink
def self.link
"#"
end
def initialize(options)
#show_div = options.try(:show_div)
#show_desc = options.try(:show_div)
end
def generate
content_tag :li do
concat main_link
concat show_div_link if #show_div
concat show_desc_link if #show_desc
end
end
private
def main_link
# Your html
# call self.class.link here
end
def show_div_link
# You html
end
def show_desc_link
# You html
end
end
class TwitterLink < SocialLink
def self.link
ENV['SOCIAL_TWITTER']
end
end
These are demo code but you got the idea
I'm trying to have an table in text mail, so I write some helpers:
module MailerHelper
def field_width(text, width)
' ' * (width - text.length) + text
end
def cell(text, width)
output = '| ' + field_width(text, width-2) + " |\n"
output << '+-' + '-'*(width-2) + '-+'
end
end
Then in view I write it like this:
<%= cell 'Test', 10 %>
But that what I get (according to letter_opener) is:
| Test |
+----------+
As can you see, the spaces that are repeating before Test. My question is how to prevent ActionMailer (or anything else what is destroying my beautiful table) from doing that.
Mailer code:
def remind(client, invoices)
#client = client
#company = #client.company
#invoices = invoices.to_a
days_left = #invoices.first.pay_date - Date.today
message = #client.group.messages.find_by_period days_left.to_i
raise 'No messages for this invoices.' if message.nil?
#template = message.template || if days_left < 0
t 'message.before'
elsif days_left > 0
t 'message.after'
else
t 'message.today'
end
#text = liquid_parse #template
#html = markdown_parse #text
mail(:to => #client.email, :subject => t('message.title'))
end
private
def markdown_parse(text)
markdown = Redcarpet::Markdown.new Redcarpet::Render::HTML,
:autolink => true, :space_after_headers => true
markdown.render text
end
def liquid_parse(text)
renderer = Liquid::Template.parse text
renderer.render 'company' => #company, 'invoice' => #invoice, 'client' => #client
end
I've found bug. It was caused by Premailer what I use to inline CSS in HTML part.
class InlineCSSInterceptor
def self.delivering_email(message)
#message.text_part.body = Premailer.new(message.text_part.body.to_s, with_html_string: true).to_plain_text # this is line causing the problem.
message.html_part.body = Premailer.new(message.html_part.body.to_s, with_html_string: true).to_inline_css
end
end
Mailer.register_interceptor InlineCSSInterceptor
How do I wrap if-else statement in a content_tag?
def entry_template
content_tag(:div, :class => "item_title") do
"<h3>
if x == 'garage'
#{entry.garage.name}
else
'Not garage'
end
</h3>"
end
end
Thanks!
def entry_template
content_tag(:div, :class => "item_title") do
"<h3>" +
if x == 'garage'
entry.garage.name
else
'Not garage'
end +
"</h3>"
end
end
or, in my eyes somewhat nicer, since it doesn't switch styles all the time,
def entry_template
content_tag(:div, :class => "item_title") do
content_tag(:h3) do
if x == 'garage'
entry.garage.name
else
'Not garage'
end
end
end
end