Fix an Escaped Hyperlink Bug

2019 Mar 26

There was a bug in timeline page. Hyperlinks (<a>) were unwanted escaped, showed in the page like below.

if a feedback is submitted in <a href="/about">About</a> page, ...

The fix is rather simple as below.

-    <li><%= p %></li>
+    <li><%= p.html_safe %></li>

Basically, html_safe tells rails not to HTML escape a string by claiming the string is “safe” (doing nothing on the string but setting a flag). html_safe should not be called on any user input strings, otherwise you’ll be under risk of XSS attacks. For this site, the fix is safe since all the content in the timeline page is solely input by myself, not by other malicious users.

More about html_safe

html_safe is a method of SafeBuffer, which is a String wrapper designed to prevent XSS attack. SafeBuffer is almost the same as String, beside having a difference behavior on concatenation. When a “safe” SafeBuffer A is appended by an “unsafe” SafeBuffer or String B, B will be HTML escaped before concatenation. By default a SafeBuffer/String is not marked safe.

Rails uses SafeBuffer to prevent XSS attack. For the below erb,

  <li><%= p %></li>

Rails translates it into something like,

  '<li>'.html_safe + p + '<li>'.html_safe

Therefore, p, which may be from user input, is HTML escaped when concatenated, and rendered safely in the result page.

More than One Way in Ruby

Using raw has the same effect, but show the intention in a much clearer way.

  <li><%= raw p %></li>

And one more, <%== is equivalent to raw, in case you really want to save some keystrokes.

will_paginate with Bootstrap

2019 Jan 29

will_paginate doesn’t come with Bootstrap style pagination by default. However, as its doc says, it does support customization by providing your own LinkRenderer.

to customize HTML output of will_paginate, you’ll need to subclass WillPaginate::ActionView::LinkRenderer

Below is a simple implementation to render pagination links with Bootstrap(4) css components.

# app/helpers/application_helper.rb

module ApplicationHelper
  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 => BootstrapRenderer
    end
    super *[collection_or_options, options].compact
  end

  class BootstrapRenderer < WillPaginate::ActionView::LinkRenderer
    protected
    def html_container(html)
      tag :nav, tag(:ul, html, class: "pagination pagination-sm"), container_attributes
    end

    def page_number(page)
      tag :li, link(page, page, rel: rel_value(page), class: 'page-link'),
        class: (page == current_page ? 'page-item active': 'page-item')
    end

    def previous_or_next_page(page, text, classname)
      tag :li, link(text, page || '#', class: 'page-link'),
        class: ['page-item', classname, ('disabled' unless page)].join(' ')
    end
  end
end
# app/views/posts/index.html.erb

# use 'text-center' to center inline-blocks (<ul>) within <nav>
<%= will_paginate @posts, params: @will_paginate_params, class: 'text-center' %>

Ruby on Rails上的一个多租户实现

2017 Jan 6

一些ORM框架,比如EclipseLink,自带了多租户的实现。 这篇文章介绍了一个在Rails ActiveRecord之上的多租户实现。

Ruby on Rails框架的历史

2016 Nov 15

Rails从0.9.3版本开始到当前版本的所有Changelogs。 自“底”向“上”过一遍这些Changelogs,可以了解一个web框架(该有)的功能和演化。

Changelog(总)是了解一个软件(新)功能的最佳文档。