RDiscount :generate_toc with Sinatra - ruby

So I have sinatra setup with Rdiscount to render a markdown file with a HAML layout. This all works but I want RDiscount to generate a table of contents based on the headers in my haml file. I've tried setting it in the sinatra configuration.
set :markdown, :generate_toc => true
but that doesn't seem to work.
I've also tried doing it when I render the markdown like so:
markdown :PAGENAMEHERE, :layout => :'layouts/PAGENAMEHERE', :generate_toc => true
which also doesn't work.
Is this even possible? If it is, what am I doing wrong?

While #three's answer helped me a lot, I would like to show a perhaps more general solution:
class MDWithTOC < ::Tilt::RDiscountTemplate
def flags
[:generate_toc]
end
end
Tilt.register MDWithTOC, 'md'
Here we override the flags method of the RDiscount Tilt template handler and regiter it as a handler for the md format.
Now you can use the md helper as always and it will generate the TOC.

This should work:
get '/' do
text = File.read('README.md')
markdown = RDiscount.new(text, :generate_toc)
body = markdown.to_html
haml :home, :layout => true, :locals => {:body => body}
end
You create the body directly via RDiscount and include it as plain HTML in your HAML layout. Whatever prevented toc creation should work this way natively. I found the solution via https://github.com/mjijackson/markdown/blob/master/app.rb

Related

How to output data from Yaml variables (written in markdown) into an HTML Haml file?

In a Yaml data file aaa.yml I have:
yyy: |
[foo](http://example.com) bar.
I want to pull this data from a Haml file (zzz.html.haml).
1. If I do zzz.html.haml:
%p
= data.aaa.yyy
Middleman renders zzz.html:
<p> [foo](http://example.com) bar</p>
2. If I do:
%p
:markdown
= data.aaa.yyy
Middleman renders:
<p>= data.aaa.yyy</p>
How can I make it render:
<p>foo bar</p>
?
You have a string that contains markdown, and you want to render that markdown and include the result in your output.
One way would be to use the Haml :markdown filter. You can’t use normal Haml syntax inside a filter (like you’ve tried), but you can use interpolation inside #{...}, so you could do
:markdown
#{data.aaa.yyy}
A better way, if the string is a standalone chunk, might be to create a helper method that renders markdown, and call that.
Wherever you have your helpers add something like:
def markdown s
# I'm using kramdown here, but you can use whatever processor you prefer.
Kramdown::Document.new(s).to_html
end
Then in your Haml:
= markdown(data.aaa.yyy)
Yay! Turns out there's this helper, which was already in my config.rb but didn't work by itself:
def markdown(text)
Tilt['markdown'].new { text }.render
end
When I installed this helper (from #matt's answer) with the first one:
def markdown s
Kramdown::Document.new(s).to_html
end
Then = markdown(foo.bar) worked, which made my day!! :D

How do you create pretty json in CHEF (ruby)

How would you make an erb template that has human readable json?
The following code works, but it makes a flat json file
default.rb
default['foo']['bar'] = { :herp => 'true', :derp => 42 }
recipe.rb
template "foo.json" do
source 'foo.json.erb'
variables :settings => node['foo']['bar'].to_json
action :create
end
foo.json.erb
<%= #settings %>
Similar SO questions
Chef and ruby templates - how to loop though key value pairs?
How can I "pretty" format my JSON output in Ruby on Rails?
As pointed out by this SO Answer .erb templates are great for HTML, and XML, but is not good for json.
Luckily, CHEF uses its own json library which has support for this using .to_json_pretty
#coderanger in IRC, pointed out that you can use this library right inside the recipe. This article shows more extensively how to use chef helpers in recipes.
default.rb
# if ['foo']['bar'] is null, to_json_pretty() will error
default['foo']['bar'] = {}
recipe/foo.rb
pretty_settings = Chef::JSONCompat.to_json_pretty(node['foo']['bar'])
template "foo.json" do
source 'foo.json.erb'
variables :settings => pretty_settings
action :create
end
Or more concise as pointed out by YMMV
default.rb
# if ['foo']['bar'] is null, to_json_pretty() will error
default['foo']['bar'] = {}
recipe/foo.rb
template "foo.json" do
source 'foo.json.erb'
variables :settings => node['foo']['bar']
action :create
end
templates/foo.json.erb
<%= Chef::JSONCompat.to_json_pretty(#settings) %>
Something like this would also work:
file "/var/my-file.json" do
content Chef::JSONCompat.to_json_pretty(node['foo']['bar'].to_hash)
end
<%= Chef::JSONCompat.to_json_pretty(#settings) %> Works like Charm !!

Sinatra's way to render multiple .erb files

I have a web project that will render an HTML file, that is based on various .erb files.
I'm not sure about the best way, to do this, since each .erb file need to get specific information, such as cookie content.
Currently I have used this concept:
I have a directory with all of my .erb files, which get rendered using:
ERB.new(template).result
the rendered HTML will get returned to the main .erb template, which will get again rendered by sinatra, using:
erb :main
the result is pretty good, but i don't have the chance to include content from session based cookies, since .erb can not access them
I am pretty sure, the sinatra framework provides a better way to do this. A good way would be...
require 'sinatra'
enable :sessions
get "/" do
content1 = erb :template1, :locals => { :cookie => session[:cookie] }
content2 = erb :template2, :locals => { :cookie => session[:cookie] }
erb :mainTemplate, :locals => { :content => [content1, content2] }
end
... but, unfortunately it doesn't work that easy :(
Does anybody has a better idea?
Here is what I did for mine:
get '/Login' do verbose = params['verbose']
cTest = MyTests.new("envo")
result = cTest.login()
if verbose == 'true'
erb :newResultPage, :locals => {:result => result}
elsif verbose == 'false'
erb :resultPage, :locals => {:result => result}
end
end
Basically, I use a conditional statement to determine which page to use. This one is based on parameters. You could also use return results, or what ever other conditions you like.

Make ERB.run quiet

I'm using ERB inside a Rails app to render an ERB template, something like...
T = ERB.new(...)
result = T.run(....)
However I noticed in my terminal that ERB is dumping the transformation (from erb template to filled-in text) to the terminal. How can I make ERB be quiet?
I was able to do it with this:
T = ERB.new(...)
file = open('some-new-file.html','w')
file.write T.result(binding) # where binding is the thing that has the data

How to structure a layout template in Haml

I have a web page that uses Haml for layouts. "layout.haml" is a separate layout file which is used when rendering any actual Haml page.
layout.haml looks something like:
-# layout.haml
!!! XML
!!!
%html
%head
...
%body
...
#content= yield
This is of course already in the document's <body> so manipulating things in the header is not directly possible. For instance <title> is changed via #title. A bigger problem is the fact that every page-specific JavaScript needs to be loaded in the body. Moreover, layout.haml already contains JavaScript, so jQuery is usually instantiated multiple times.
Are there any suggestions for a better template structure?
This solution is for Ruby on Rails only:
You can use yield(:location) and the content_for(:location) methods. "Using the content_for Method" has more information.
layout.haml:
!!!
%html
%head
%title= yield(:title)
= yield(:head)
%body
= yield
view.haml:
- content_for(:title, 'My title')
- content_for(:head) do
= javascript_include_tag :foo
%h1 My view!
I use partials:
!!!
%html
= partial('trst_sys/shared/html-head')
%body{:id => "srv",:'data-lang' => current_lang}
#main.wrap
%header#header
= partial('trst_sys/shared/header')
%nav#menu
= partial('trst_sys/shared/menu')
%section#content
%article#xhr_content
= yield
%article#xhr_msg.hidden
%section#sidebar
= partial('trst_sys/shared/sidebar')
%section#main_footer.wrap
%footer#footer.wrap
= partial('trst_sys/shared/footer')

Resources