How to Add Disqus to a Rails Application
Copy the “universal code” of your Disqus account from your account’s admin page,
https://<your-account>.disqus.com/admin/install/platforms/universalcode/
.
The universal code is like below.
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://<your-account>.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
Paste it into where the Disqus comments block should display.
Set this.page.url
and this.page.identifier
and uncomment these lines to “avoid split threads and missing comments”.
For example, for this site they are set like below.
// tested in Rails 5
this.page.url = '<%= url_for host: "https://onefeed.xyz" %>';
this.page.identifier = '<%= @post.slug %>';
Update
If the Rails application is deployed behind a Nginx, where SSL/Https is enabled, and listening on an internal port like 3000.
The <%= url_for host: "https://onefeed.xyz" %>
above will generate values like http://onefeed.xyz:3000/posts/how-to-add-disqus-to-a-rails-application
.
To correct the port and the schema, use the code below.
this.page.url = '<%= url_for host: "onefeed.xyz", port: nil, protocol: "https" %>';
It generates URLs like https://onefeed.xyz/posts/how-to-add-disqus-to-a-rails-application
.
Check more in url_for API.
Deploy a Rails App with Docker
This site is deployed using Docker containers on a small DigitalOcean Droplet.
Since I never have a private Docker registry, the original deploy flow was,
- push the source code to the remote Git repository
- on the Droplet, fetch the source code from the Git remote
- build an Docker image from the source code
- start the Docker container
Since some time ago, the docker build
has kept failing on the bundle install
instruction on the Droplet.
During bundle install
, the Bundler fails to build the sassc
gem.
The Bundler complains that,
virtual memory exhausted: Cannot allocate memory
, while building the sassc
gem.
A workaround I tried was,
- build the Docker image on a more powerful machine, like the local dev machine
- save the image into a tar archive (
docker save img-foo | bzip2 > foo.img.bz
) - copy the tar to the Droplet
- load the image from the tar (
bunzip2 foo.img.bz && docker load -i foo.img
) - start the Docker container
One problem of the workaround was the volume to transfer is a bit large (the image is around 1G before compression, and 370M after compression). It was time consuming over a slow network.
Current Deploy Workflow
Actually, Bundler supports fetching gems from local cache, not only from rubygems.org
.
Using bundle package --all
can,
Copy all of the .gem files needed to run the application into the vendor/cache directory.
In the future, when running bundle install, use the gems in the cache in preference to the ones on rubygems.org.
The sassc
gem is built on the local machine while bundle package
, and put into vendor/cache
directory.
Now, the deploy flow is like,
- run
bundle package --all
if the application’s dependencies change - check in
vendor/cache
folder and push it to the Git remote - on the Droplet, fetch the source code from the Git remote
- build an Docker image from the source code
- start the Docker container
The Dockerfile
is like,
USER app
RUN mkdir -p /home/app/source-code
WORKDIR /home/app/source-code
COPY vendor/cache ./vendor/cache/
COPY Gemfile* ./
# use RVM, so "bash -lc ..."
RUN bash -lc 'bundle install --deployment --without development test'
# copy the app source code
COPY . ./
Other Notes
If a gem is located at a particular git repository using the :git
parameter, like
gem "rails", "2.3.8", :git => "https://github.com/rails/rails.git"
Then the --all
option should be used in bundle package
.
Some gems are built using CMake.
When run bundle package --all
, CMakeCache.txt
files are created and stored in vendor/cache
.
The problem with CMakeCache.txt
files is that they use hardcoded paths of the machine runs bundle package --all
.
When build the Docker image in the remote server, the hardcoded paths fail the bundle install
.
Therefore, in the .dockerignore
file, add the following line.
vendor/cache/*/CMakeCache.txt
Fix an Escaped Hyperlink Bug
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
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上的一个多租户实现
Scale Out Multi-Tenant Apps based on Ruby on Rails
一些ORM框架,比如EclipseLink,自带了多租户的实现。 这篇文章介绍了一个在Rails ActiveRecord之上的多租户实现。
Ruby on Rails框架的历史
Rails从0.9.3版本开始到当前版本的所有Changelogs。 自“底”向“上”过一遍这些Changelogs,可以了解一个web框架(该有)的功能和演化。
Changelog(总)是了解一个软件(新)功能的最佳文档。