sstephenson / eco Goto Github PK
View Code? Open in Web Editor NEWEmbedded CoffeeScript templates
License: MIT License
Embedded CoffeeScript templates
License: MIT License
I'm trying use eco for client-side templating. I have multiple .eco files I'd like to combine into one js file - I know I can just do that on js files after they are generated, but is there a better way to do this?
Document the command-line compiler, eco(...)
, eco.compile(...)
, templates as Node modules, rendering templates in the browser, and the in-browser compiler.
A nice addition would be helper functions that aren't tied to the model. One example would be a function to localize time (see below). If I've understood things correctly that would now require the localizedTime function on each model. If view/template helpers could be passed as as third argument that would be neat.
Or perhaps this is already possible, and I've just missed it.
var localizedTime = function(utcTime) {
var offset = (new Date).getTimezoneOffset() / -60;
// return a localized time (utcTime + offset)
}
I'd like to augment Eco, so that when a function object is given to output like so
<%- -> div class: 'big' %>
or even nicer
<%-> div class: 'big' %>
This function to be passed to the coffeekup compiler, if present.
This way, coffeekup becomes a nice DSL for view helpers.
Maybe have some API to plug different resolvers to different tags <%= <%- <%-> <%=> <%+> etc. ?
At the moment I know I can do
ck = coffeekup.compile
...
<%-ck -> div class: 'big' %>
But I'm mostly thinking at the zen of it.
PS.
Is Eco stream-able ? meaning does it "send" the resolved chunks of the template before the rest of the template is processed (like the recent Rails streaming additions) ?
Finding data associated with elements is a rather common use case. For example, let's say an element was clicked on, I'd love to be able to retrieve the data the element is associated with in the event handler. This is possible in jQuery.tmpl using $.fn.tmplItem(). Does eco have support, or plans for such a feature?
version 1.1.0-rc-3
eco -p spling.eco gives this:
(function() {
this.ecoTemplates || (this.ecoTemplates = {});
this.ecoTemplates["spling"] = function(__obj) {
if (!__obj) __obj = {};
var __out = [], __capture = function(callback) {
var out = __out, result;
__out = [];
callback.call(this);
result = __out.join('');
__out = out;
return __safe(result);
}, __sanitize = function(value) {
if (value && value.ecoSafe) {
return value;
} else if (typeof value !== 'undefined' && value != null) {
return __escape(value);
} else {
return '';
}
}, __safe, __objSafe = __obj.safe, __escape = __obj.escape;
__safe = __obj.safe = function(value) {
if (value && value.ecoSafe) {
return value;
} else {
if (!(typeof value !== 'undefined' && value != null)) value = '';
var result = new String(value);
result.ecoSafe = true;
return result;
}
};
if (!__escape) {
__escape = __obj.escape = function(value) {
return ('' + value)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
};
}
(function() {
(function() {
if (model.get("resizeMode")) {
__out.push('\n<div class="resize"><div class="tl"></div><div class="tr"></div><div class="bl"></div><div class="br"></div></div>\n');
}
__out.push('\n<div class="overlay">\n</div>\n<a href="#"><span>');
__out.push(__sanitize(model.get("Title")));
__out.push('</span></a>\n<img src="');
__out.push(__sanitize(model.get('Image')));
__out.push('" alt="" />');
}).call(this);
}).call(__obj);
__obj.safe = __objSafe, __obj.escape = __escape;
return __out.join('');
};
}).call(this);
however:
eco -s, and then giving the file contents with the stdin gives this:
function(__obj) {
if (!__obj) __obj = {};
var __out = [], __capture = function(callback) {
var out = __out, result;
__out = [];
callback.call(this);
result = __out.join('');
__out = out;
return __safe(result);
}, __sanitize = function(value) {
if (value && value.ecoSafe) {
return value;
} else if (typeof value !== 'undefined' && value != null) {
return __escape(value);
} else {
return '';
}
}, __safe, __objSafe = __obj.safe, __escape = __obj.escape;
__safe = __obj.safe = function(value) {
if (value && value.ecoSafe) {
return value;
} else {
if (!(typeof value !== 'undefined' && value != null)) value = '';
var result = new String(value);
result.ecoSafe = true;
return result;
}
};
if (!__escape) {
__escape = __obj.escape = function(value) {
return ('' + value)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
};
}
(function() {
(function() {
if (model.get("resizeMode")) {
__out.push('\n<div class="resize"><div class="tl"></div><div class="tr"></div><div class="bl"></div><div class="br"></div></div>\n');
}
__out.push('\n<div class="overlay">\n</div>\n<a href="#"><span>');
__out.push(__sanitize(model.get("Title")));
__out.push('</span></a>\n<img src="');
__out.push(__sanitize(model.get('Image')));
__out.push('" alt="" />\n');
}).call(this);
}).call(__obj);
__obj.safe = __objSafe, __obj.escape = __escape;
return __out.join('');
}
(the overall call() is missing)
Not being a node person I have no idea what's causing this. But it stops the latter working in the browser.
Thanks!
It's pretty painful not being able to use newlines inside CoffeeScript code blocks!
Here's a simple example test page showing the variety of use cases I miss from other templating engines (like EJS):
https://gist.github.com/966202
Thanks for your consideration. =)
Is it possible to embedded eco template inside coffeescript file?
For example, using it with backbone view
class Foo extends Backbone.View
template: ''' <Eco template as a string> '''
render: ->
# Compile eco template contained in @template and append it to $(el).html
this
How to load css and js files with eco.render?
It seems like the code in lib is designed to run within node.js on the server side.
In the documentation it says that <%= will escape the string, while <%- won't escape it. However after trying it and looking at this:
https://github.com/sstephenson/eco/blob/master/src/scanner.coffee#L73
It looks like it's actually the other way around. Bug? Outdated documentation?
Thanks!
https://gist.github.com/966242
Trailing colons in comments should be ignored. =)
I have done quite a basic template, with a simple loop.
I don't know if this is a bug or a misinterpretation of the examples.
<ul>
<% for meaning in @meanings: %>
<li>
<strong><%= meaning.description %></strong>
</li>
<% end %>
</ul>
Actually, the rendering is correct, but there is an error in the console:
_ref is undefined
_ref = this.meanings;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
A typical data could be this one:
{"word": "submitter", "language": "en", "nature": "noun", "gender": "", "usage": "Plural submitters", "meanings": [{"description": "A person who submits (e.g. a paper for publication)", "examples": []}]}
I have a pretty simple Backbone View that I'd like to have render some blocks of HTML conditionally. I'm seeing a behavior where following the conditionally rendered HTML there is a integer rendered as well. From what I can tell, it seems to happen when I use the end
statement to terminate a conditional block.
Here's some code that is demonstrating the error for me:
<% if true: %>Hello World!<% end %>
I would expect this to renderHello World!
into the containing element. However, it's actually rendering Hello World!2
.
If I add several of blocks in the same template:
<% if true: %>Foo, <% end %>
<% if true: %>Bar, <% end %>
<% if true: %>Baz<% end %>
I would expect this to render Foo, Bar, Baz
into the containing element. However, it's actually rendering Foo, 2 Bar, 5 Baz8
. After running a somewhat larger set of them, it seems the integer being printed goes up by 3 every time. From reading over the gem's README I can't see anything I'm doing wrong.
Any help would be much appreciated!
Hi,
I was looking through the readme, and I'm seeing that linkTo doesn't have to be @linkto. I am hoping there is a way to do this (to easily differentiate between functions and variables), but it seems like all helper functions have to be prefaced with "@".
Does the readme have a mistake? or is there a way to do this?
Asynchronous blocks would be converted into a function that would be invoked as a callback. I've created a Gist that shows an example that is common to my MySQL development. Before I can invoke a template, I must perform all of the SQL queries to create a JSON model of the document data. Instead, it would be nice to perform those callbacks from within the template.
is there a way to change the default eco <% %> tag, so I can use erb inside my eco templates, for example to generate image tags using the rails asset pipeline?
EDIT: oops, realized this isin't eco territory at all. Still, if I get anywhere itnteresting, I'll post updates.
Hi
Loving using eco for the latest project!
Was wondering if there are any common solutions for post processing templates. Many plugins are designed to progressively enhance server-generated markup, rather than doing so inline. Such enhancements include jquery linkify, bootstrap plugins, and many more.
Currently the only way to tie-in such post-processing functionality is by have a method called after every template render. This gets cumbersome quickly.
What if there were a third method in addition to escape() and safe(): after(). It could either be passed methods to add to a callback chain which would be called after the template has been rendered, or it could be expected to be redefined in-line and so called directly.
Thoughts?
What is the best way to get syntax highlighting for eco in my text editor? I use textmate
Hey there,
Great work on this stuff! Just one request: is it possible to use this as a template engine in Express?
I'm not sure exactly what that entails, but I don't think it's much; I'm pretty sure it's just a matter of exposing the expected APIs. I can ask the Express author(s) for details on this if you would like.
Here's the sample app I made to test this: https://gist.github.com/927144 (Check out the comments to see the error message.)
Thanks in advance!
Cheers,
Aseem
It seems that render takes the full template string as argument and so it will be evaluated several times instead of prerender a function that will be called. I am not quite sure, but this is not very fast.
Something seems to be wrong with Cakefile.
That's what I get:
rainman@grand-piano ~/opensource/eco $ ~/bin/cake dist
fs:153
return binding.open(path, stringToFlags(flags), mode);
^
Error: ENOENT, No such file or directory '/home/rainman/opensource/eco//home/rainman/opensource/eco/package.json'
at Object.openSync (fs:153:18)
at Object.readFileSync (fs:94:15)
at /home/rainman/opensource/eco/Cakefile:44:17
at Object.action (/home/rainman/opensource/eco/Cakefile:49:26)
at /home/rainman/.node_libraries/.npm/coffee-script/0.9.5/package/lib/cake.js:31:26
at /home/rainman/.node_libraries/.npm/coffee-script/0.9.5/package/lib/cake.js:53:23
at path:81:19
at node.js:773:9
I've got a couple of cases where I have a template that I reuse within other templates and at the moment its a copy-paste job. Am I missing something?
Is this a reasonable feature request?
Something like this
File: content.eco
@layout = "layout.eco"
body =
... html for the content page
If not, how to avoid duplicating common sections (e.g. header, footer etc.) in a site?
Due to some restrictions on my current project I am forced to develop in a windows environment, this being said I have come across a windows specific parsing issue. When I run Eco.compile(File.read ("ANY FILE WITH A CONTROL STRUCTURE") ) I get an unexpected indent error, when I run this same command on my mac it compiles properly. The only control structure that doesn't error out is an if statement without an else statement. I am guessing that this is an issue with the windows line breaks causing the parser to improperly indent, but this is only a guess.
gem install eco
ERROR: While executing gem ... (Gem::DependencyError)
Unable to resolve dependencies: eco requires eco-source (>= 0)
gem install eco-source
ERROR: Could not find a valid gem 'eco-source' (>= 0) in any repository
If sprockets is dependent on eco then is it deployed to rubygems.org?
In an view.eco, I have the following snippet that I want to pass to my layout.eco.
<% @meta = { "title" : "Learning Resources", "key": "learn", "css": [ "/css/learn.css", "/css/dialog.css" ] } %>
I can't for the life of me figure out how to line break it so the parser won't barf. Desired output would be...
<% @meta =
title : "Learning Resources"
key: "learn"
css: [ /css/learn.css, /css/dialog.css ]
%>
The documentation suggests that I can add a colon (:) to escape a line break but I am either doing it wrong or it doesn't work.
Thoughts?
It seems that you cannot have a Coffeescript switch statement in an eco template:
http://stackoverflow.com/questions/8920981/can-i-use-a-coffeescript-switch-block-in-eco
Or if you can, it is not obvious how.
will this ever be possible:
name="fred"
eco.render "<%= name %>"
currently I have a lot of:
@name = name
for the sole purpose of getting them to render in the template.
I would expect this to work:
https://gist.github.com/966214#file_foreach.html
But it doesn't (no names are printed). The generated intermediate CoffeeScript is this:
https://gist.github.com/966214#file_generated.coffee
I'm not sure what the _capture
function does exactly -- I'm guessing it's related to Eco's block support -- but it seems to be setting the HTML content aside instead of printing it right away, which is my intent with the forEach iteration.
In other words, if I'm defining a function, yeah it's meant to be captured, but if I'm executing (invoking) a function, shouldn't my HTML be used right away?
I want to be able to change delimiters - for example to a shorter version like <= >, <- > or mustaches - like in underscore.js: http://documentcloud.github.com/underscore/#template
Is there an official way to do this?
E.g. if I write <<%= @email %>>
then nothing is printed.
This does work < <%= @email %> >
but isn't what I want.
I'm assuming this is a bug? Is there a way to escape "special" characters?
Hi,
I'm a eco newbie and I'm trying to integrate it with Backbone. I checked out Ryan Bates' RailsCast example. However, I'm having trouble accessing a simple get.
I can do this: <%= @stream.get('stream_type') %> which returns "project"
But how do I get user.name? I tried <%= @stream.user.get('name') %> but I got error: Uncaught TypeError: Cannot call method 'get' of undefined
[
: {
: : "id":"5004095283de4ca9ff000005",
: : "stream_type":"project",
: : "user":
: : {
: : : "id":"5002f30560de7d0ffb000003",
: : : "name":"Regular User2"
: : },
The sys
module is now called util
. Thanks
being able to have multiple line coffeescript execute within <% %> would be awesome.
in erb and ejs, you can say
<%
def something(arg)
...
end
%>
or
<%
function(){
...
}
%>
is there a workaround / somewhere i can hack this in ?
<%- @views.html({
body: body()
title: 'xyz'
css: [ '/static/css/global.css', '/static/css/index.css' ]
}) %>
Result:
Parse error on line 34: unexpected newline in code block
I would like to be able to have subdirectories in my template directory based on the resources in my app. Within each resource directory there would be eco templates such as index.html.eco, show.html.eco, etc. I made a script that can find all the eco files recursively in the template directory however anything with the same file name gets overwritten in the ecoTemplate object. Is their a way to change the generation of the key for the object to include some name spacing base on the directory the template is in?
Templates -->
User ->
index.html.eco
Post ->
index.html.eco
#! /bin/bash
find src/templates -type f -name *.eco -exec eco -o www/js/eco {} \;
cat www/js/eco/* > vendors/templates.js
This will cause templates.js to only one ecoTemplates["index.html"] since the index.eco.js is overwritten in www/js/eco.
Just an idea / feature request:
In general, you want client-side JavaScript in a separate, external file -- good for cacheability, separation of concerns, etc.
There are occasions though where you want a snippet of inline JavaScript in a <script>
block. Maybe it's something page-specific, or something dynamically generated, or maybe this is just a quick prototype.
It would be super awesome and convenient if I could write that code in CoffeeScript, e.g. via <script type="text/coffeescript">
, and have Eco automatically compile that to JavaScript. <3
I've been trying out using stitch.js and eco with backbone.js but I'm having
some troubles.
I'm sending a backbone model to a Eco template with:
$(this.el).html(template(this.model));
Then in my template, all I'm trying is this:
<%= @get('text') %>
Now what I've been able to figure out is that eco first checks if
there's already an escape function on the object in question and since
it's a backbone model there is a method called escape so eco calls
that method instead of its own. The backbone escape method looks likes
this:
escape : function(attr) {
var html;
if (html = this._escapedAttributes[attr]) return html;
var val = this.attributes[attr];
return this._escapedAttributes[attr] = escapeHTML(val == null ? '' : '' + val);
},
Now since when this is called this is DOMWindow the
this._escapedAttributes[attr]
part fails with an error:
Uncaught TypeError: Cannot read property 'Finish some great tasks' of undefined
_.extend.escape
__sanitize
__obj.safe
__obj.safe
module.exports
module.exports.Backbone.View.extend.render
module.exports.Backbone.View.extend.add
_.each._.forEach
_.each.Backbone.Collection.(anonymous function)
module.exports.Backbone.View.extend.addAll
Backbone.Events.trigger
_.extend.reset
_.extend.fetch.options.success
jQuery.Callbacks.fire
jQuery.Callbacks.self.fireWith
done
jQuery.ajaxTransport.send.callback
'Finish some great tasks' is what's in my models 'text' attribute.
I tought maybe I could change __esacpe(value)
in your code to
__escape.call(__obj, value)
but I havn't got it working yet. Any
ideas?
Also I can change the template to <%- @escape('text') %>
and it works
but I'm not sure what I think is best and I thought I would tell you
about this problem.
I have a situation for a web app I'm building where it'd be really convenient to be able to create pre and post functions for some of the templates I've created. One example of what I'd like to do is to check rendered HTML for overly long links that need shortened. Right now I'm putting this sort of code after each place in the app the template is called but it'd be much more DRY to have pre/post functions.
Is there a way already of doing this that I haven't thought of? Or if not, if someone could give me a few pointers, I'd be happy to write a patch to add this functionality.
I know there's a lot of change going on at the moment (npm 0.2.18, node 0.4.0), but there seems to be an installation or path problem when installing eco with npm 0.2.18 (using node 0.4.0). While it worked fine with npm 0.2.16 it gives the following error when requiring after installed with the current npm:
Error: Cannot find module 'eco/compiler'
at Function._resolveFilename (module.js:289:11)
at Function._load (module.js:241:25)
at require (module.js:317:19)
at Object. (/Users/sschuster/.node_libraries/.npm/eco/1.0.1/package/lib/eco.js:2:18)
at Module._compile (module.js:373:26)
at Object..js (module.js:379:10)
at Module.load (module.js:305:31)
at Function._load (module.js:271:10)
at require (module.js:317:19)
at Object. (/Users/sschuster/.node_libraries/[email protected]/index.js:11:18)
Also the contents of .node-modules/eco is different (eco.js, index.js, package.json.js and an eco folder with *.coffee.js files), while with the old npm it's just a bunch of *.js files.
I'm pretty new to the whole node/npm thing and don't fully understand the path/require things, but as I understood with node >= 0.3 coffee files can be included directly so maybe it's some npm change in this regard. Maybe you can have a look at this.
So I wanted to use eco client-side and I noticed that eco pre-compiles all the helper functions in every single one of the compiled templates.
This could be solved by maybe allowing the compiler to use a global eco object which would then contain the helpers.
This was also mentioned in #48.
The following command fails:
node ..\..\..\node_modules\eco\bin\eco ^
-i views ^
-o ..\build\views ^
..\views\listing.eco
with following error
C:\[...]\node_modules\eco\lib\command.js:215
if (err) throw err;
^
Error: Command failed: A subdirectory or file -p already exists.
Error occurred while processing: -p.
A subdirectory or file ..\\build\\views already exists.
Error occurred while processing: ..\\build\\views.
at ChildProcess.exithandler (child_process.js:536:15)
at ChildProcess.EventEmitter.emit (events.js:91:17)
at maybeClose (child_process.js:634:16)
at Process._handle.onexit (child_process.js:676:5)
When using -p it outputs JS code correctly. Windows XP.
Here's a simple example of an input string that fails:
https://gist.github.com/966185#file_error.html
But the error I get from it is this:
Error: Parse error on line 10: Unexpected 'IDENTIFIER'
at Object.parseError (/usr/local/lib/node/.npm/coffee-script/1.0.1/package/lib/parser.js:470:11)
at Object.parse (/usr/local/lib/node/.npm/coffee-script/1.0.1/package/lib/parser.js:540:22)
at Object.compile (/usr/local/lib/node/.npm/coffee-script/1.0.1/package/lib/coffee-script.js:26:22)
at /usr/local/lib/node/.npm/eco/1.0.3/package/lib/eco/compiler.js:11:27
at Object.render (/usr/local/lib/node/.npm/eco/1.0.3/package/lib/eco/compiler.js:19:39)
at Object.<anonymous> (/Users/aseemk/Projects/Node/misc/app.coffee:10:33)
at ServerResponse._render (/usr/local/lib/node/.npm/express/2.2.2/package/lib/view.js:377:21)
at ServerResponse.render (/usr/local/lib/node/.npm/express/2.2.2/package/lib/view.js:242:17)
at Object.<anonymous> (/Users/aseemk/Projects/Node/misc/app.coffee:19:18)
at param (/usr/local/lib/node/.npm/connect/1.3.0/package/lib/middleware/router.js:148:21)
But line 10 in the input is an empty line! After some investigating, line 10 is referring to the intermediate CoffeeScript that Eco generates:
https://gist.github.com/966185#file_generated.coffee
The problem is, that generated CoffeeScript isn't saved to a file or anything -- I had to manually add some console.logs() to Eco's compiler.js
to see it.
The end result is that the line numbers reported in these error messages are pretty meaningless when you're looking at the source. It would be nice if they mapped to the source Eco template instead.
And for inspiration, the real goal to strive towards is the awesome reporting that Stylus has achieved:
http://tjholowaychuk.com/post/5002088731/stylus-vs-sass-vs-less-error-reporting
=)
Can't run tests because "package" is now a reserved word in node.js. So it must be changed.
In the Cakefile, the dist
task refers to files that look like they moved.
lib/eco.js
doesn't exist, and
lib/eco/compiler.coffee
, et. al should probably be src/eco/compiler.coffee
In order to support the @safe
and @escape
helpers, eco adds these to the context object, which can make for confusing bugs.
Here's a very simplified version of where I ran into the problem:
sales = ({price: i, shipping:i, tax:i} for i in [0..10])
tpl = eco.compile "Price: <%= @price %>; Shipping: <%= @shipping %>; Tax: <%= @tax %><br>"
totals =
price: 0
shipping: 0
tax: 0
for s in sales
console.log tpl s
totals[k] += v for k,v of s
console.log tpl totals
The problem is calling tpl
pollutes s
with the escape
property, which then gets concatenated repeatedly onto totals.escape. As a result, we end up with a garbled function string that begins "undefinedfunction (value)..."
. Then when we call tpl totals
, this eco tries to call that garbled string as a function, and we get an error which was rather confusing to pin down.
For now, I'm working around this by shallow copying my inputs. But this is really not how a template library should be treating its inputs. Any changes made on the context object should be reversed before returning.
Hello, it seems like <%=
syntax doesn't work well with Backbone models, when a model is used as template context. For example:
<%= @get("foo") %>
is compiled to:
this.escape(this.get("foo"))
What's wrong with this? A model has an escape()
method, which takes attribute name as a single argument, and returns its escaped value. This is obviously not what eco
is expecting.
Maybe there's a way to disable this implicit escape()
lookup?
I think I've run into an issue with Eco and the new HTML formaction
attribute when applied to button
elements. This attribute overrides the closest parent form's own action
whenever specified in supported browsers. But if my button
happens to contain another element (<strong>
in my case), the attribute no longer has an effect in Chrome 14 or Safari 5.1 when rendered from an Eco template. If I remove the <strong>
it works. Firefox 6 works either way.
<strong>
is supported "phrasing content" and the button works properly outside of the Eco template. So maybe it's something to do with the WebKit DOM implementation?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.