How can I access ruby instance variable from within coffeescript template?
In sinatra documentation it's said that templates are evaluated within same scope as rout that invoke that template.
So, I have following sinatra app:
server.rb:
require "sinatra"
require "coffee-script"
get '/app.js' do
#str = "Hello"
coffee :app
end
and in views/app.coffe file I would like to use #strvariable. Is it possible? If so, how can I access #str variable?
It could be possible only if you'll process coffee source file with something like erb. So if you'd use rails assets pipeline you can just append .erb to file extension and the file will be processed with erb before sending it to coffee I think in sinatra you'll have to wrap up something similar yourself.
The idea will be close to this one - http://www.sinatrarb.com/intro#Textile%20Templates
P.S: accessing variables from different layers of application is bad idea anyway.
EDIT
You have amultistage template compilation process in RAILS driven by a gem called sprockets. You start with a file for example called /app/views/foo/show.js.coffee.erb
class <%= #magic %>
doSomthing: ->
console.log "hello"
In your controller you add the instance variable
#magic = "Crazy"
Rails first processes the erb file and generates
class Crazy
doSomething: ->
console.log "hello"
Secondly it processes the coffeescript file to generate
var Crazy;
Crazy = (function() {
function Crazy() {}
Crazy.prototype.doSomething = function() {
return console.log("hello");
};
return Crazy;
})();
That is why it is called the asset pipeline. More conventionally you could call it
a compilation pipeline. If you know what you are doing you might be able to get sprockets running with Sinatra. However your life would be easier if you just used Rails 3.1 from
the start.
I wrote this for Rails: https://github.com/ludicast/ice
but it can be easily adapted for Sinatra.
It lets you use Eco and CoffeeKup templates inside a Rails application, with the ruby models exposed to Coffeescript.
Nate
Related
Let's say I have a Ruby files structure like this:
app.rb
/views/index.erb
/views/layout.erb
app.rb is the main app file that contains the routes. Let's say I have one route as this:
get "/" do
erb :index
end
In layout.erb I have my html head and closing </body> and </html> and etc. It is also where I have <head>..</head> and where I can put that scope for some JavaScript methods.
Let's say now that I have my Ruby logic in index.erb. I get a value somehow from my logic (index.erb) and I want to pass that value to my layout.erb. Is it possible? If so, how we do that?
I have checked the #variable_name to pass it as argument from the route definition:
get "/" do
#variable_name = ??? -> this do not works since I did not get the logic yet from index.erb, no?
erb :index
end
I also have tried to use simply <%= variable_name %> or #{variable_name} directly in the layout.erb but without any success.
I actually found out that I was able to transport my "code" to view the widgets I wanted to populated in the index.erb so by that I resolve my problem of accessing the value that was define from a variable declared in index.erb.
Problem solved and a better way to work with Ruby as this issue makes me read more about how to structure myself (Thx to Dave).
I have a sinatra app that is mostly using erb for templates, but I am adding some mustache partials for blocks of html that need to be rendered on both the server and client side. I have a "views" directory where I'm keeping all my templates, including the mustache templates I'm adding. For example, the structure looks something like this:
views/
index.html.erb
_wingding.html.erb
_widget.html.mustache
Let's say I'm rendering index.html.erb using this endpoint:
get '/' do
erb :index
end
And inside that template, I want to render both of the above partials [UPDATE: it turns out this partial method is not built into sinatra, but is included via a gem (see my answer for details), but it still relies on the main sinatra rendering component, so the problem here still stands.]. So index.html.erb contains:
<%= partial :wingding %>
<%= partial :widget %>
The erb partial (wingding) renders fine, but the mustache partial (widget) does not. It causes the application to throw an exception:
Template engine not found: mustache
I have included the 'mustache' gem in the project. I tried 'require mustache/sinatra' and register Mustache::Sinatra. Both of these statements worked, but didn't solve the problem above. Any idea how to tell sinatra about mustache so that it can render mustache partials?
I'm setting my views directory by including this configuration:
configure do
set :views, File.join(File.dirname(__FILE__), 'views')
end
Version numbers:
sinatra 1.3.1
mustache 0.99.4
ruby 1.9.3
So it turns out the gem listed in the solution in my other answer caches output, meaning you can't use it to include the same partial with different locals in the same request, which makes it worthless for my purposes. So here's another (admittedly hackish) solution that I've settled on. I created a helper that will just deliver the contents of a given view:
helpers do
def template_contents(path)
File.open("#{settings.views}/#{path}") { |f| f.read }
end
end
Then, I just do a regular Mustache#render:
<%= Mustache.render(template_contents('_widget.html.mustache'), { ... }) %>
This works great for my use case.
I figured this out. First, I must note something I didn't realize when I posted the question. partial is not actually built into sinatra. It is being included in my app as part of the sinatra-more gem (discontinued, most of it's functionality is now in a project called padrino, but still using sinatra-more in this app). The partial method is included like this:
require 'sinatra_more/render_plugin'
module Sinatra
register SinatraMore::RenderPlugin
end
Anyway, that just adds the partial method (along with some other rendering helpers) but this method falls back on the built-in sinatra rendering code, so this fact is independent of the actual issue at hand in this question -- that sinatra does not by default recognize the mustache template engine. Turns out someone created a super simple gem to make it work, sinatra-mustache. You simply include the gem and require the library:
require 'sinatra/mustache'
That's it -- it just works! No additional configuration necessary. If you're curious how it works, the source code is pretty simple. It's by no means a one-liner to register a new template engine with sinatra (even saying "register" is a misnomer -- you basically have to implement it), but it's still fairly simple.
UPDATE: This gem caches the output for a given request/template, so you can't use it a second time with different locals. I could see cases where that's okay, but that makes it worthless for my use case.
I've just started writing a reasonably straightforward site using sinatra. My problem is that I wanted to refactor the main app.rb file but am getting errors trying to access the url params.
In my get '/' action, Sinatra's looking at which params are set and then needs to do a few different things depending on what's in the url. Something like this.
class App < Sinatra::Application
...
get '/' do
if params['code1']
#network = 'code1'
mode code here
elsif params['called'] && params['mac']
#network = 'code2'
mode code here
elsif params['code3']
#network = 'code3'
mode code here
end
end
The problem is that I need to require a file that also uses the params.
I've put the following in the above code:
require File.dirname(__FILE__) + '/lib/networks/code1.rb'
Where code1.rb includes:
class App < Sinatra::Application
if params['login'] # == 'login'
pass = 'uampass'
elsif
...
But that gives me the following error:
undefined local variable or method `params' for main:Object
How can I refactor this without causing an error
As far as i know you can't use two (or more) Sinatra applications in, well one application. Since both files define a Sinatra::Application descendant this isn't possible.
Also if you want to use values from the params-hash you should define helper methods Helper Documentation, which you call when processing the route, or you just create Class which has class or instance methods which take params-values as parameters. Actually calling params from another file/class doesn't seem like good practice.
To put this in context: Sinatra applications are organised as handlers. The Sinatra::Application descendant is something like the main handler which uses support methods(helpers and instance methods of the Sinatra::Application descendant) or support Classes, which are usually defined in other files, but do not descend from Sinatra::Application.
To make this a little bit more clearly:
Your main Sinatra file:
require_relative 'another_file.rb'
class App < Sinatra::Application
# ...
#a_handler = MyHandler.new
get '/' do
if params['something'] == 'wanted_value'
#a_handler.handle_it(params)
end
end
Another file ('another_file.rb'):
class MyHandler
def initialize
#an_instance_variable = 'foobar'
end
def handle_it(params_hash)
if params_hash['login'] # == 'login'
pass = 'uampass'
elsif
# ...
end
# ...
# do some stuff
# ....
return pass
end
end
Actual code would of course depend on the real problem you're trying to solve, so if you would elaborate i could be more precise...
The error message contains everything you need to know, and it's nothing to do with Sinatra.
You are requiring code1.rb, which contains this (slightly edited so it will run):
require 'sinatra'
class App < Sinatra::Application
if params['login'] # == 'login'
pass = 'uampass'
end
end
Ruby evaluates code as it encounters it. In this case, you've required 'code1.rb', so it evaluates the code in that file. It encounters 'params' and asks "is there a local variable or method with that name?". There isn't, so it fails as you've seen. Open an irb session and check it out.
Class definitions in ruby are just an expression with a scope.
In relation to Sinatra: params is available in the block that a route declaration takes.
I'd recommend reading Sinatra: Up and Running, which explains some of the 'magic' that is going on (a good companion to the Sinatra Book).
I am writing a helper and I need to get a rendered file as String.
I see that the method that I need exists in the middleman's library: http://rubydoc.info/github/middleman/middleman/Middleman/CoreExtensions/Rendering/InstanceMethods#render_individual_file-instance_method
How do I call this function from my helper class?
I tried:
require "middleman-core/lib/middleman-core/core_extensions/rendering.rb"
...
puts Middleman::CoreExtensions::Rendering::InstanceMethods.render_individual_file(filepath)
But it does not seem to find the file, any idea?
I'm not sure 3.0 beta is quite ready for primetime.
That said, it does sound like the partial method is what you're looking for.
Unless I'm missing something, the Middleman method seems like an overly-complex solution. For one of my sites I wanted to load entire text files into my templates, so I wrote this helper:
# Shortcut for loading raw text files. Obviously assumes that given file is in a valid format.
# #return [String] File contents
def load_textfile(filename)
File.read filename.to_s
end
Also, you should clarify if you are intending to use this within a template, or within Ruby code. It's not clear to me based on your question.
Here is an example of how one would use above helper:
Currently of note, Middleman is in the process of transitioning to version 4, and the conventions for loading helpers will change. The most straightforward way to define a helper is within a helper block in your config.rb file, as follows:
helpers do
# Define helper functions here to make them available in templates
end
I use Slim for templating. It really is the best. In slim you would appply helper as thus:
= load_textfile 'path'
p You can embed helper output in your page with interpolation, too: #{load_textfile 'path'}
I really don’t need the overhead of Rails for my very small project, so I’m trying to achieve this just using just plain Ruby and HAML.
I want to include another HAML file inside my HAML template. But I haven’t found a good—or really usable—way of doing this.
For example, I have these two HAML files:
documents.haml
%html
%body
= include(menu.haml) body
%article …
menu.haml
%ul
%li
%a whatever …
Include is obviously not the way to go here. But it does a nice job describing what I’m trying to achieve in this example.
I totally recommend the Tilt gem for these things. It provides a standard interface for rendering many different template langages with the same API, lets you set custom scope and locals, lets you use yield, and is robust and fast. Sinatra is using it for templates.
Example:
require 'haml'
require 'tilt'
template = Tilt.new('path/to/file.haml')
# => #<Tilt::HAMLTemplate #file="path/to/file.haml" ...>
layout = Tilt.new('path/to/layout.haml')
output = layout.render { template.render }
This lets you yield inside the layout to get the rendered template, just like Rails. As for partials, David already described a simple and nice way to go.
But actually, if what you're writing is going to be served over HTTP, i suggest you take a look at Sinatra, which already provides templating, and has the simplest request routing you could imagine.
I've done this before, just for a quick-and-dirty template producer. The easiest way is to just render the HAML inside the parent object:
%p some haml that's interesting
= Haml::Engine.new('%p this is a test').render
%p some more template
You'll more than likely want to build some methods to make this easier--a couple of helper methods. Maybe you write one called render_file that takes a filename as an argument. That method might look something like:
def render_file(filename)
contents = File.read(filename)
Haml::Engine.new(contents).render
end
Then, your template would look more like:
%p some template
= render_file('some_filename.haml')
%p more template
Note, you will probably need to pass self to the original Haml::Engine render so that it knows how to find your render_file method.
I've had great success just using the instructions posted by David Richards in a concatenated way, without variables, like this:
= Haml::Engine.new(File.read('/Volumes/Project/file_to_include.haml')).render
There's obviously a more elegant way. But for someone who just wants to include one or two files, this should work nicely. It's a drawback that all base files using these includes have to be recompiled after some changes to the latter. It might be worthwile to just use php include if the environment allows that.
def render_file(filename)
contents = File.read('views/'+filename)
Haml::Engine.new(contents).render
end
(Adding this semi-redundant answer to show how one might incorporate the techniques from other answers.)
Include something like this in your setup code to monkey-patch the Haml library.
module Haml::Helpers
def render_haml(filename)
contents = File.read(Rails.root.join("app", "assets", "templates", filename))
Haml::Engine.new(contents).render
end
end
Then in your Haml file
.foo
= render_haml('partial.haml')
.bar
Obviously this is a Rails-ish example (because I wanted to use HAML in my asset pipeline instead of as views)... You will want to replace Rails.root.join(...) with a way to find filename in your project's templates or partials directory.