How to use Slim directly in ruby - ruby

I would like to create a basic ruby script that renders Slim templates into html (This would eventually be part of a larger project). Ideally I would like to use the HTML produced within the script.
I understand this is possible using TILT (as shown in the SLIM README) where it says the following:
Slim uses Tilt to compile the generated code. If you want to use the Slim template directly, you can use the Tilt interface.
Tilt.new['template.slim'].render(scope)
Slim::Template.new('template.slim', optional_option_hash).render(scope)
Slim::Template.new(optional_option_hash) { source }.render(scope)
The optional option hash can have to options which were documented in the section above. The scope is the object in which the template code is executed.
However, I'm still unable to successfully run this. Therefore, I was wondering if someone could help me by producing a working example.
EDIT (this has recently been edited further ):
I have played about with the code quite a bit but I keep on getting the following error:
undefined local variable or method `source' for main:Object (NameError)
This is what i'm running:
require 'slim'
# I'm not sure about the next two lines...
optional_option_hash = {}
scope = Object.new
Tilt.new('template.slim').render(scope)
Slim::Template.new('template.slim', optional_option_hash).render(scope)
Slim::Template.new(optional_option_hash) { source }.render(scope)
Many Thanks for all your help.

See Specifying a layout and a template in a standalone (not rails) ruby app, using slim or haml
This is what I ended up using:
require 'slim'
# Simple class to represent an environment
class Env
attr_accessor :name
end
scope = Env.new
scope.name = "test this layout"
layout =<<EOS
h1 Hello
.content
= yield
EOS
contents =<<EOS
= name
EOS
layout = Slim::Template.new { layout }
content = Slim::Template.new { contents }.render(scope)
puts layout.render{ content }
For the scope, you can put in modules/classes or even self.

Quick essentials of
module SlimRender
def slim(template, variables = {})
template = template.to_s
template += '.slim' unless template.end_with? '.slim'
template = File.read("#{ROOT}/app/views/#{template}", encoding: 'UTF-8')
Slim::Template.new { template }.render OpenStruct.new(variables)
end
end
Include SlimRender to your class and:
def render_something
slim 'streams/scoreboard', scores: '1-2'
end

Related

Ruby ERB - Create a content_for method

I'm currently working on an ERB View class for a gem. With this class I would like to have some helper methods for ERB templates.
It's okay about basic helpers like h(string). I found erbh gem who help me to understand more how context works.
But now I'm trying to create a content_for method like there is in Rails or Sinatra.
On first time I was using simple Proc to capture the view block and then just calling call method to print it. It was working enough at the beginning.
But after having completed views I saw wired thinks, some content are printed multiple times.
So I take a look on the Sinatra ContentFor helper to understand how they did it and I copy some methods of this helper. I have no errors, but the block return are always empty... and I don't really know why.
My knowledge about ERB are not good enough to know how ERB buffering works.
Code
Here a gist who explain the status of my code. I extracted the code from my library and simplified it a bit.
https://gist.github.com/nicolas-brousse/ac7f5454a1a45bae30c52dae826d587f/66cf76c97c35a02fc6bf4a3bc13d8d3b587356de
What I would like?
I just would like to have content_for methods works like they do with Rails and Sinatra.
Thanks!
After reading this blog article I finally found why it wasn't working. I don't know if I did it in the best way and cleaner way but it works.
So the bug was mainly from the ERB initilization. By using a property instead a local variable as eoutvar it now works.
erb = ERB.new(str, nil, "<>", "#_erbout")
I also change a bit the capture method who is used by content_for helper.
It looks like this now (gist)
def content_for(key, content = nil, &block)
block ||= proc { |*| content }
content_blocks[key.to_sym] << capture_later(&block)
end
def content_for?(key)
content_blocks[key.to_sym].any?
end
def yield_content(key, default = nil)
return default if content_blocks[key.to_sym].empty?
content_blocks[key.to_sym].map { |b| capture(&b) }.join
end
def capture(&block)
#capture = nil
#_erbout, _buf_was = '', #_erbout
result = yield
#_erbout = _buf_was
result.strip.empty? && #capture ? #capture : result
end
def capture_later(&block)
proc { |*| #capture = capture(&block) }
end

return json from ruby using rack

i'm still fairly new to server side scripts and try myself a little bit on ruby to write me little helpers and to learn some new things.
I currently try to write a small ruby app which sends a json file of all images within a specific folder to my page where i can use those to handle them further in js.
I read quite a few introductions to ruby and rails and got a recommendation to look into rack as a lightweight communicator between server and app.
While the ruby part works fine, i have difficulties to understand how to send out the generated JSON as a reaction to a future ajax call (e.g.). Hope someone can give me a few hints or sources to look into for further understanding. Thanks!
require 'json'
class listImages
def call(env)
imageDir = Dir.chdir("./img");
files = Dir.glob("img*")
n = 0
tempHash = {}
files.each do |i|
tempHash["img#{n}"] = i
n += 1
end
File.open("temp.json","w") do |f|
f.write(tempHash.to_json)
end
[200,{"Content-Type" => "application/javascript"}, ["temp.json"]]
end
puts "All done!"
end
run listImages.new
if $0 == __FILE__
require 'rack'
Rack::Handler::WEBrick.run MyApp.new
end
You don't have to save the JSON to a file before you can send it. Just send it directly:
[200, {"Content-Type" => "application/json"}, [tempHash.to_json]]
With your current code, you are only sending the String "temp.json".
That said, the rest of your code looks a little bit messy/not conform Ruby coding standards:
Start your classnames with an uppercase: class ListImages, not class listImages.
Use underscores, not camelcase for variable names: image_dir, not imageDir.
The puts "All done!" statement is outside the method definition and will be called early, when the class is loaded.
You define a class ListImages but in the last line of your code you refer to MyApp.

Preprocessing markup files in jekyll

I would like to write a preprocessor that operates on a range of markup languages before they're processed into HTML by Jekyll. Ideally the user would simply create a file called _posts/xxyyzz.md.wmd, and Jekyll would preprocess it into xxyyzz.md using a plugin I provide, and then process that into HTML in the usual way.
It looks like Jekyll's Converter framework doesn't allow that, because the output_ext function is only given the final extension "wmd", preventing it from returning ".md" for ".md.wmd", ".textile" for ".textile.wmd", etc.
Is there a way to implement a chain of processing steps like this?
EDIT: grammar
Maybe you can try to use a Generator plugin that uses your wmd converter:
require "yourWmdConverter"
module Jekyll
class ConvertWmd < Jekyll::Generator
def initialize(config)
config['convert_wmd'] ||= true
end
def generate(site)
#site = site
site.posts.docs.each { |post| convertWmd post }
end
private
def convertWmd(post)
post.content = yourWmdConverter post.content
end
end
end

How can I use views and layouts with Ruby and ERB (not Rails)?

How can I use views and layouts with Ruby and ERB (not Rails)?
Today i'm using this code to render my view:
def render(template_path, context = self)
template = File.read(template_path)
ERB.new(template).result(context.get_binding)
end
This works very well, but how can I implement the same function, but to render the template inside a layout? I want to call render_with_layout(template_path, context = self), and so that it will have a default layout.
Since you tagged it with Sinatra I assume that you us Sinatra.
By default you view is rendered in your default layout called layout.erb
get "/" do
erb :index
end
This renders your view index with the default layout.
If you need multiple layouts you can specify them.
get "/foo" do
erb :index, :layout => :nameofyourlayoutfile
end
* If you don't use Sinatra you may want to borrow the code from there.
If you use the Tilt gem (which I think is what Sinatra uses) you could do something like
template_layout = Tilt::ERBTemplate.new(layout)
template_layout.render {
Tilt::ERBTemplate.new(template).render(context.get_binding)
}
If you are using Sinatra so it has a good docimentation and one of the topics it's nested layouts (see Sinatra README)
Also good idea to use special default layout file (layout.haml or layout.erb in your view directory) This file will be always use to render others. This is example for layout.haml:
!!!5
%html
%head
##<LOADING CSS AND JS, TILE, DESC., KEYWORDS>
%body
=yield ## THE OTHER LAYOUTS WILL BE DISPALYED HERE
%footer
# FOOTER CONTENT
Thanks for all the answers!
I solved it finally by doing this, I hope someone else also can find this code useful:
def render_with_layout(template_path, context = self)
template = File.read(template_path)
render_layout do
ERB.new(template).result(context.get_binding)
end
end
def render_layout
layout = File.read('views/layouts/app.html.erb')
ERB.new(layout).result(binding)
end
And I call it like this:
def index
#books = Book.all
body = render_with_layout('views/books/index.html.erb')
[200, {}, [body]]
end
Then it will render my view, with the hardcoded (so far) layout..

Extending Middleman to handle a new filetype (.JADE)

I'd like to add .jade support to Middleman. I don't need to use any of jade's dynamic features, but I'd like to compile my app in middleman rather than with my own messy compile script.
What is the simplest way to add a new file type to Middleman?
Middleman's templating is built on Tilt, so using the tilt-jade gem it should be pretty straightforward.
Here's some code for adding Mustache templates to Middleman:
require 'tilt-mustache'
# Mustache Renderer
module Middleman::Renderers::Mustache
class << self
def registered(app)
# Mustache is not included in the default gems,
# but we'll support it if available.
begin
# Require Gem
require "mustache"
# After config, setup mustache partial paths
app.after_configuration do
Mustache.template_path = source_dir
# Convert data object into a hash for mustache
provides_metadata %r{\.mustache$} do |path|
{ :locals => { :data => data.to_h } }
end
end
rescue LoadError
end
end
alias :included :registered
end
end
Middleman::Base.register Middleman::Renderers::Mustache
that should be quite easy to adapt to work with Jade.

Resources