Output slim to variable - ruby-on-rails

I'm using Base64.encode64(val) to convert html to base64.
Example:
- val = link_to 'Link', link_path
= Base64.encode64(val)
But how can I get slim markup to variable? Like so:
.class = link_to 'Link', link_path # <- this output with slim div
Or even multiple lines
div
span
.another_div
There is a way by putting slim code into partial and do this:
- var = render 'partial'
= Base64.encode64(var) # Convert into base64
How to do this without partial?

Slim exposes its templating through the Tilt interface, like so:
# Render a template file:
Slim::Template.new("template.slim", options).render(scope)
# Render a string:
Slim::Template.new(options) { "b slim markup" }.render(scope)
Where options is an optional hash of options for slim and scope is the object in which the template code is executed.
So the following:
slim_markup = <<-SLIM
div
span
.another_div
SLIM
# The options hash and scope have been omitted for the sake of simplicity
html_output = Slim::Template.new { slim_markup }.render
Sets the value of html_output to:
<div></div>
<span></span>
<div class="another_div"></div>
But for your example with the url helper link_path, you must provide slim a scope in which the url helpers are available e.g. a controller.

This is an old question, but I have wondered about this many times, and I always spend a lot of time researching it.
Using Slim 4 you can use capture directly:
- val = capture
div
span
.another_div
This will put the rendered slim into your variable.

Another way from the box using capture method. From the docs:
Using the Binding you can capture to local variables as follows:
module Helpers
def capture_to_local(var, &block)
set_var = block.binding.eval("lambda {|x| #{var} = x }")
# In Rails we have to use capture!
# If we are using Slim without a framework (Plain Tilt),
# you can just yield to get the captured block.
set_var.call(defined?(::Rails) ? capture(&block) : yield)
end
end
The helper can then be used in the Slim template as follows
/ The captured_content variable must be known by the Binding beforehand.
= capture_to_local captured_content=:captured_content
p This will be captured in the variable captured_content
= captured_content
Read more https://github.com/slim-template/slim#capturing-to-local-variables

Related

HAML filters in a helper

Helper functions can receive a block which they yield to render the block. Sometimes I'd want that block to be spec'd with a filter. So for example:
= doc_page title: 'FAQ' do
:markdown
# Welcome to the *FAQ*
This is not so DRY as we are always writing doc_page and markdown together. Can I make the helper method accept a block and pass it through a HAML filter. Something like:
= doc_page title: 'FAQ' do
# Welcome to the *FAQ*
In this fantasy, doc_page is a helper method that does some setup stuff and then passes the content through markdown, saving us the need to declare :markdown everywhere and making the world a DRYer place.
Currently it is not possible to use filters in helpers. An alternative approach would be to use redcarpet to parse the markdown and then pass the output to a helper.
an example would be:
= doc_page title: 'FAQ', :markdown do
### my markdown
= doc_page title: 'FAQ' do
normal html
The implementation of the doc_page would be something like this:
def doc_page(title, markup=:html)
content = yield
if markup == :markdown
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
content = markdown.render(content)
end
content
end
This would solve your problem, as you define your markdown filter in the helper. And you don't need an extra indentation level in your haml.
You could use tilt (the api haml uses to render markdown) directly instead of through haml. Probably something like this (not tested).
markdown_template = Tilt['md'].new { "# this is markdown code" }
markdown_template.render
You can find a similar example in the Tilt docs.
But this is more of an idea than a complete implementation.
I'm afraid, but it's not possible, because haml is a preprocessor, basicly this piece of code:
= doc_page title: 'FAQ' do
# Welcome to the *FAQ*
%a href="/" link
Will be converted in runtime ruby code like:
concat(doc_page title: 'FAQ' do
# Welcome to the *FAQ*
concat('link')
end)

Rails - print a "recursive" html variable in Slim

I would like to render in my view a "html"-content variable, let me explain:
Somewhere in a helper there is a pseudocode like this
# view helper for an ERB view
def render_something_recursively(i = 1)
html = "<li>"
html << "hi number #{i}"
if i < 1000
html << render_something_recursively(i++)
end
html << "</li>"
end
Sorry for the bad example but I hope that it give you an idea, in facts I would like to iterate on a hierarchy structure (a tree) with flexible depth. For this reason I need a recursive method and I would like to keep it out from the view.
My question is: how can I accomplish to the same result but in Slim (or eventually in HAML)? How can I give the information of "indentation" at the htmlvariable?
Is it possible or I must use an ERB view?
My final goal should be "easily" something like that:
# recursive_list.html.slim
ul class="a_recursive_list"
=render_something_recursively
Use content_tag and it will render whatever templating engine you are using:
def render_something_recursively(i = 1)
content_tag(:li, "hi number #{i}") do
if i < 1000
render_something_recursively(i++)
end
end
end
Additionally, I don't think you should nest <li> elements directly inside each other, you should either render text or a <ul> element nested inside the <li>.

What is the syntax for named yield with both names and parameters in ruby/rails?

One could use yield with a :name in views in rails:
= yield :some_place
so then using then using content_for :some_place do ... to insert a code block only in there where yield :some_place is placed (http://guides.rubyonrails.org/layouts_and_rendering.html#using-the-content-for-method).
Also ruby allows passing parameters in the yiled (http://www.tutorialspoint.com/ruby/ruby_blocks.htm):
def test
yield 5
puts "You are in the method test"
yield 100
end
test {|i| puts "You are in the block #{i}"}
But I didn't find anything about using yield/content_for both with names and parameters in rails views:
= yield :some_place, 5, 6
...
= content_for :some_place do |a,b|
h3 = "Yield provided parameters: #{a} and #{b}"
Is it possible? Where is the official rails or ruby syntax for yield statements and passing blocks?
I heard something about the Proc.new() that could be somehow related to the problem.
content_for(:name) evaluates first, and stores a snip of HTML for later use. yield(:name) only fetches this content. Hence, you can't pass arguments into a method that was already called, and won't be called again.
You probably merely need to cut a partial HTML.erb file, and render it from your target location. Render takes named parameters as a hash.

Backbone.js with Eco Templates: How to include template within a template?

Is it possible to include a template within a template? Maybe something similar to the way ERB handles partials?
Rather than attempting to render nested models in a fashion like ERB, it's better to let Backbone.js take care of this.
Note, I am using coffeescript syntax:
Projects.IndexView
template: JST["backbone/templates/projects/index"]
addAll: () ->
#options.projects.each(#addOne)
addOne: (project) ->
view = new Worktimer.Views.Projects.ProjectView({model : project})
#$("#projects-table").append(view.render().el)
render: ->
$(#el).html(#template(projects: #options.projects.toJSON() ))
#addAll()
the model Project has a nested collection called sessions:
Projects.ProjectView
template: JST["backbone/templates/projects/project"]
$(#el).html(#template(#model.toJSON() ))
for s in #model.sessions.models
v = new Worktimer.Views.ProjectSessions.ShowView(model: s)
$(#el).find('.sessions').append(v.render().el)
ProjectSessions.ShowView
template: JST["backbone/templates/project_sessions/show"]
render: ->
$(this.el).html(#template(#model.toJSON() ))
so, in the end we have nested templates like this:
Projects Index
Project
Session
Session
Session
Session
Project
Session
Project
Session
Session
here a little helper I use for spine:
# Render Partials in ECO-Templates like in Rails-ERB
#
# usefull to clean up structure in spine.js and other js-mvc´s like backbone
#
# usage:
# <%- render_partial 'path/to/partial' %> .. will render ../spine-app/views/path/to/_partial.jst.eco
# <%- render_partial 'path/to/partial', foo: 'bar' %> .. will render ../spine-app/views/path/to/_partial.jst.eco .. locals = #foo
#
window.render_partial = ( path, options = {} ) ->
# add the leading underscore (like rails-partials)
path = path.split('/')
path[ path.length - 1 ] = '_' + path[ path.length - 1 ]
path = path.join('/')
# render and return the partial if existing
try
JST["app/views/#{ path }"]( options )
catch error
# if App.Environment != 'production' then "<p class='error'>Sorry, there is no partial named '#{ path }'.</p>" else ''
"<p class='error'>Sorry, there is no partial named '#{ path }'.</p>"
I don't think Eco supports this. It's intended more as a simple templating system like Mustache than as a full-fledged ERB replacement. On Rails, you'd probably render an Eco template and inject the output into an ERB or Haml template.
For Node.js development, you might want to take a look at CoffeeKup, which lets you do your templating in CoffeeScript and supports partials.
If you prefix the template with .erb, you can use the ERB processor.
Change yourfile.js.coffee to yourfile.js.coffee.erb, then you'll be able to add <%= %> tags to your CoffeeScript template.

Evaluating string templates

I have a string template as shown below
template = '<p class="foo">#{content}</p>'
I want to evaluate the template based on current value of the variable called content.
html = my_eval(template, "Hello World")
This is my current approach for this problem:
def my_eval template, content
"\"#{template.gsub('"', '\"')}\"" # gsub to escape the quotes
end
Is there a better approach to solving this problem?
EDIT
I used HTML fragment in the sample code above to demonstrate my scenario. My real scenario has set of XPATH templates in a configuration file. The bind variables in the template are substituted to get a valid XPATH string.
I have thought about using ERB, but decided against as it might be a overkill.
You can do what you want with String's native method '%':
> template = "<p class='foo'>%s</p>"
> content = 'value of content'
> output = template % content
> puts output
=> "<p class='foo'>value of content</p>"
See http://ruby-doc.org/core/classes/String.html#M000770
You can render a string as if it were an erb template. Seeing that you're using this in a rake task you're better off using Erb.new.
template = '<p class="foo"><%=content%></p>'
html = Erb.new(template).result(binding)
Using the ActionController methods originally suggested, involves instantiating an ActionController::Base object and sending render or render_to_string.
I can't say I really recommend either of these approaches. This is what libraries like erb are for, and they've been throughly tested for all the edge cases you haven't thought of yet. And everyone else who has to touch your code will thank you. However, if you really don't want to use an external library, I've included some recommendations.
The my_eval method you included didn't work for me. Try something like this instead:
template = '<p class="foo">#{content}</p>'
def my_eval( template, content )
eval %Q{"#{template.gsub(/"/, '\"')}"}
end
If you want to generalize this this so you can use templates that have variables other than content, you could expand it to something like this:
def my_eval( template, locals )
locals.each_pair{ |var, value| eval "#{var} = #{value.inspect}" }
eval %Q{"#{template.gsub(/"/, '\"')}"}
end
That method would be called like this
my_eval( '<p class="foo">#{content}</p>', :content => 'value of content' )
But again, I'd advise against rolling your own in this instance.
This is also a nice one:
template = "Price of the %s is Rs. %f."
# %s - string, %f - float and %d - integer
p template % ["apple", 70.00]
# prints Price of the apple is Rs. 70.000000.
more here
To late but I think a better way is like ruby-style-guide:
template = '<p class="foo">%<content>s</p>'
content_text = 'Text inside p'
output = format( template , content: content_text )

Resources