Slim Line breaks and formatting - ruby-on-rails

I am using slim for templating and ruby on rails(just started using them). The only problem I am facing is: there is no formatting for the html rendered. i.e no line breaks, no indentation.
I can understand it can be a little tricky for slim to render formatting intrinsically.
Is there anyway to render properly formatted html?

From the docs:
Slim::Engine.set_default_options pretty: true
or directly
Slim::Engine.default_options[:pretty] = true
To expand a bit, as #rubiii mentioned in the comments this is a feature of Slim. For the same reasons it's good practice to minify and compress Javascript and CSS in production Slim strips unecessary whitespace from the HTML it produces without this :pretty option set to true.
If you have some config/initializers/slim.rb file you can configure the :pretty option dynamically by checking the environment.
Slim::Engine.set_default_options pretty: Rails.env.development?
Otherwise you should set this option to true only in config/environments/development.rb, leaving it false in production.

Just add data-force-encoding="✓" attribute to the body tag. That will make Rails to send email as quoted printable (trick is to use UTF8 char in fact). See: https://github.com/slim-template/slim/issues/123

Is there anyway to render properly formatted html?
HTML can be 'properly formatted' technically speaking, without line breaks. Look into 'HTML validation' if you are curious.
As for Slim, line breaks are one of the few points you have to do some extra work. Where many people use the HTML <br> tag, I prefer using $/ which is the ruby variable corresponding to the local line seperator. I prefer this becasue the <br> element is outmoded and though slim doesn't produce any output rendering it, it's bad practice to use the actual tag. The line ending variable is also more famliar and reliable for me personally :)
Actually another great reason to use $/ is that it seamlessly fits into other ruby coding patterns and is more agnostic overall. Check out this template as an example.

Related

rails 5.x: add nofollow to all links in 'sanitize'

I am working on a Rails application whose HAML templates frequently make use of a routine called sanitize. I have deduced from context that this routine sanitizes user-controlled HTML. Example:
# views/feed_items/_about.html.haml
%h3 Summary:
.description
= sanitize #feed_item.description
I want to make this routine add 'rel=nofollow' to all outbound links, in addition to what it's already doing. What is the most straightforward way to do that?
N.B. I am not having any luck finding the definition of this method, or the official configuration knobs for it. The vendor directory has two different HTML sanitizer gems in it and I can't even figure out which one is being used. This is a large, complicated web application that I did not write, and I barely understand Ruby, let alone all of Rails' extensions to it. Please assume I do not know any of the things that you think are obvious.
The sanitizer will strip out the rel tags if they exist.
I ran into a similar issue and added an additional helper method - clean_links to the ApplicationHelper module, and called it after sanitizing the content.
# application_helper.rb
def clean_links html
html.gsub!(/\\2')
html.html_safe
end
This method looks for all <a> tags, and adds rel="nofollow". The html_safe method is necessary or else the HTML will be displayed as a string (it's already been sanitized).
This solution treats all links equally, so if you only want this for links pointing outside the domain, you'll have to update the REGEX accordingly.
In your view: <%= clean_links sanitize(#something) %>
So, first the content is sanitized, then you add the rel="nofollow" tag before displaying the link.
Actually there's a built-in way:
sanitize "your input", scrubber: Loofah::Scrubbers::NoFollow.new

Rendering markdown with Slim/Rails from an instance var

I'm having trouble getting Slim to render my markdown:
div.container
div.row
div.col-md-8.job_description
markdown:
= #listing.job_description
That just renders the string
This is an h1 ## h2 ### h3 > this is a quote * hello * goodbye foo
No line breaks or anything (which are contained in the actual string)
How do I get this to render properly? Thanks!
I gave up on using markdown: in slim, had tried everything.
I ended up creating this helper, place it in just any file in app/helpers
def markdown(content)
return '' if content.blank?
markdown = Redcarpet::Markdown.new(Redcarpet::Render::XHTML, autolink: true, space_after_headers: true)
sanitize(markdown.render(content)).html_safe
end
And then in a view
= markdown #listing.job_description
You will of course have to install the redcarpet gem.
gem 'redcarpet'
Rendering Markdown out of a variable in this way isn't possible, as Slim first renders the Markdown (or any other embedded engine language) and only after that interpolates the variable contents.
It makes sense that it would work this way as Slim's embedded engine tags are intended as a way of including different languages in-line so you can read them there. If the markdown isn't included in literal form in the template, there's no advantage to using the embedded engine over simply rendering the Markdown before you pass it to Slim (as HTML, and using '==' to prevent further processing by Slim).
I think, also, that it's set up like this because it's intended to provide and easy way for interpolating text into Markdown - which doesn't itself have a means of interpolation - within the same template as everything else.
The helper Iceman suggests is a nice way to do it. I'd probably call it outside of the template, but that's just because it's my personal preference to do as little as possible inside the template.
Looks like your markdown content is not indented under markdown:, so it won't be processed by markdown.
It should look more like this:
div.col-md-8.job_description
markdown:
= #listing.job_description
See this section of the docs for more information.

How to use slim for mailer text template

I am using slim as the template engine for my rails app and would like to use slim for mailer templates as well.
There is no problem with html mailer templates (views/mailer/default_email.en.html.slim) but, I am not sure how to make the text templates work.
I have placed a text template in views/mailer/default_email.en.text.slim with this content:
Hello,
Your video is ready.
= #url
Thank you,
The A-Team
But the result is parsed as slim HTML, and looks like this:
<Hello>,</Hello><Your>video is ready.</Your><Click>the link below to watch it:</Click>http://watch.thy/video<Thank>you,</Thank><The>A-Team</The>
Other than prefixing every line with a pipe, isnt there a more natural way?
I even looked for an embedded plugin (like the markdown one) to say "plain text" but there is none.
Thanks in advance.
Slim was designed to generate HTML, not plain text, so you'll have to either use the pipe prefix for each line or go with .text.erb templates. I'd use the ERB templates, especially if you don't have a lot of interpolation going on.
For what it's worth, you can actually go the Slim route without having to prefix each line with a pipe, like so:
|
Hello,
<br><br>
Your video is ready.<br>
#{#url}
<br><br>
Thank you,<br>
The A-Team
I would definitely agree with eugen though, that the text.erb route is the best fit. Just providing another solution, in case somebody absolutely insisted on doing this in Slim. :-)

How do you write multiline if/else statements with inline javascript HAML?

I'm basically trying to produce jQuery effects based on the data I am coming in with from the server. I've tried multiple methods but its not coming out correctly
:javascript
"#{if #user.nil?}"
$('#test-container').show();
"#{end}"
The contents of #{...} need to be a single expression, and is allowed to go over multiple lines. Inside #{...} you can use literal strings simply by quoting them, and you don’t need quotes around the whole thing.
:javascript
#{if #user.nil?
"$('#test-container').show();"
end}
In this case you can do it as a single line:
:javascript
#{"$('#test-container').show();" if #user.nil?}
In general you want to avoid complex multiline interpolated blocks like in the first example. If necessary you should look at creating helpers to keep your views simple and understandable.
Does this work?
- unless #user
:javascript
$('#test-container').show();

Markdown to text/plain and text/html for multipart email

I’m looking for a solution to send DRY multipart emails in Rails. With DRY I mean that the content for the mail is only defined once.
I’ve thought about some possible solutions but haven’t found any existing implementations.
The solutions I’ve thought about are:
load the text from I18n and apply Markdown for the html mail and apply Markdown with a special output type for the text mail where
links are put in parenthesis after the link text
bold, italic and other formatting that doesn't make sense are removed
ordered and unordered lists are maintained
generate only the html mail and convert that to text according to the above conditions
Is there any available solution out there? Which one is probably the better way to do it?
In Chapter 4 of Crafting Rails Applications, Jóse Valim walks you through how to make a "merb" handler that uses markdown with interspersed erb and can compile to text and html. Then you make a mailer generator that generates a single merb template for each of your mail actions.
You can read an excerpt from that chapter on the page I linked you to. I highly recommend buying the book.
If you're interested in using my sorry version of what he describes in that book, you can slap this in your Gemfile:
gem 'handlers', :git => "git://github.com/chadoh/handlers.git"
Be warned that I barely know what I'm doing, that I'm not versioning that gem, and that I probably won't really even maintain it. Frankly, I wish I could find someone else who was doing a better job, but I've been unsuccessful in doing so. If you want to fork my project and be the person doing that better job, go for it!
This is a PITA, but is the only way to DRY mail such that you can support both HTML (multipart) & plaintext:
Put the html email copy in a partial file in your ActionMailer view directory with the following extension: _action.html.erb
Replace "action" with whatever action name you are using.
Then create 2 more files in the same directory:
action.text.html.erb and
action.text.plain.erb
In the text.html partial:
<%= render "action.html", :locals => {:html => true} %>
In the text.plain partial:
<% content = render "action.html", :locals => {:html => false} %>
<%= strip_tags(content) %>
That works for me, though it certainly makes me want to pay the monthly service for madmimi
Use the maildown gem.
This gems does the heavy lifting of allowing you to use email.md.erb instead of email.html.erb and email.text.erb. Write it once in a sane format and have it automatically display in HTML and in Plain Text. Win.
There are some intricacies here that you'll want to look at based on your use-case, but here's some of what we did to get it working well:
Create a maildown.rb initializer to setup some sane defaults:
Maildown.allow_indentation = true # Prevents code blocks from forming when using indentiation in markdown emails.
Maildown::MarkdownEngine.set_text do |text|
text.gsub( /{:.*}\n?/, "" ) # Removes Kramdown annotations that apply classes, etc. with `{: .class }`.
This allows you to use indents in your blocks, etc. But also precludes the ability to add indents in your Plain Text. It also removes Kramdown-specific annotation from Plain Text.
Then just replace your HTML and Plain Text files with a single .md.erb file and test it out to make sure it looks good in both versions.
Note, until you remove the .html.erb and .text.erb files, it will show those first before looking for a .md.erb file. This may actually be a nice feature if you ever needed to write separate formats for a specific email (maybe a marketing one that requires more complex formatting than Markdown can provide) without having to specify anything anywhere.
Works a treat.

Resources