Closed heartsentwined closed 11 years ago
This seems good, would also be useful for the various scaffolding/grid frameworks, such as this bootstrap looking thing:
.container: .row: p Here is some text
I would just want to make sure it'd fit in nicely with work being done here https://github.com/machty/emblem.js/issues/39 , which, unlike slim, allows for a single line chain of nested HTML elements and block mustache helpers. Take a look at that and lemme know if you think this might play nicely with what's being done in that issue.
Not comprehensive perhaps, but a couple of tests and observations:
ul %li
doesn't work
Taking these two examples:
ul: li
ul = each items %li
:
and %
serve slightly different grammatical purposes.
The first case, the :
serves to mark the end of the first tag-chain, so it can appear in these statements:
ul#id: li
ul attr="foo": li
The second case, the %
serves to mark a particular literal as a tag, as opposed to being a block argument of the handlebars block. It also, coincidentally, marks the end of the handlebars block. It would disambiguate:
= linkTo navs.show nav
- nav
being a nav instance, for some reason= linkTo navs.index %nav
- nav
is a tagEither could be replaced by another, i.e.
ul %li
= each items: li
But I would argue that the :
should be adopted, following the convention from slim.
However, I would think that the example in #39
ul = each items %li = linkTo "item" this = item.text
while being possible, is drifting towards Perl-golf-ish. It took me a few full seconds to read and parse the line.
The original intention of the :
is, I think, to be reserved for the class of multiple-semantics content I cited, i.e. at most two or three tags used together in a meaningful chain. A quick scan can still make out this compact syntax, especially in common idioms that one is familiar with, such as the example you have given:
.container: .row: p Here is some text
This example also shows an advantage of using the :
- the ability to continue using the implicit div
. A %
line would have to be written thus:
.container %div.row %p Here is some text
Well, not really actually, you could still allow
.container %.row %p Here is some text
But the human brain processes letters and symbols as two different groups. A couple of symbols jammed together tend to get read (at first sight) as one group, requiring additional mental effort to separate them. Thus
%.class
%#id
%#id.class
Would be hard to process, at least as compared to the :
syntax. The %#
in particular, resembles "consonant cluster" in natural languages, using a rather bad metaphor. Or put it simply it looks like a jumble of trash to me.
Continuing on the issue of human readibility, if one wants to compress block helper contents into one line, as in the above example, then neither of these would be sufficient:
ul = each items %li = linkTo "item" this = item.text
ul = each items: li = linkTo "item" this = item.text
Neither line is easily readible (as least as compared with the rest of the slim/emblem syntax). Readibility comments are, of course, subjective, but I'd say that this points out either of the following:
ul = each items { li = linkTo "item" this { = item.text } }
- not ideal, but I'd say more readible than the above case.Wow, awesome response. Busy at the moment but I'll be parsing this shortly. Thank you for the writeup!
Any update?
So, I think I understand, but lemme just rephrase it a bit.
In Emblem, at the beginning of a non-empty, non-comment line, the rules are:
|
, it's a line/block of text=
, it's a mustache invocation%
, it's an HTML elementIt sounds like what you're saying as that when a :
is encountered, we should start over with this logic, and subject whatever follows the :
to the same rules. Some examples:
ul: li: a href="http://www.google.com" class=something: span Hello!
<ul><li><a href="http://www.google.com" {{bindAttr class="something"}}><span>Hello!</span></a></li></ul>
These were proposed in https://github.com/machty/emblem.js/issues/39, here's how they've be revised, presented in order of 1) original suggested syntax, 2) what it compiled to, and 3) how it'd look using a :
approach. Afterwards I'll revise/simplify the syntax.
bullshit = bullshit = bullshit | bullshit
{{#bullshit}}{{#bullshit}}{{#bullshit}}bullshit{{/bullshit}}{{/bullshit}}{{/bullshit}}
bullshit: bullshit: bullshit: | bullshit
ul = each items = linkTo destination = text
<ul>{{#each items}}{{#linkTo destination}}{{text}}{{/each}}</ul>
ul: each items: linkTo destination: text
ul = each items %li = name
<ul>{{#each items}}<li>{{name}}</li>{{/each}}</ul>
ul: each items: li: name
ul = each items %li = linkTo "item" this = item.text
<ul>{{#each items}}<li>{{#linkTo "item" this}}{{item.text}}{{/linkTo}}</li>{{/each}}</ul>
ul: each items: li: linkTo "item" this: item.text
This looks great to me. I prefer how this looks to the already-established syntax of
p = foo
<p>{{foo}}</p>
I'd keep this syntax around, but also support all of these colon-based ones.
p: foo
<p>{{foo}}</p>
%p: foo
<p>{{foo}}</p>
%each: =span
<each>{{span}}</p>
=span: %each Hello
{{#span}}<each>Hello</each>{{/span}}
These last two bring up an important case; when you want to override the default convention to use a known HTML5 tag name as a mustache invocation, you have to use =
. Same if you want to render an HTML element with non-HTML5 tag by using the %
prefix. Without a further syntax adjustment, you'd have to weirdly clutter :
together with %
or =
. These corner cases are important, but there's also the case of text nodes. The following is already valid Emblem syntax:
linkTo foo | Hello
{{#linkTo foo}}Hello{{/linkTo}}
This would also technically be valid with the new colon syntax:
linkTo foo: | Hello
But that clearly sucks compared to what already works. But I think I have the solution.
In a chain of elements/helpers/text, the following will be nesting delimiters:
:
nest the following content according to the line-starting conventions described above%
nest the following content, forcing it to be interpreted as HTML=
nest the following content, forcing it to be interpreted as a mustache invocation|
nest the following text content. This one's unique because it must terminate a chain; everything that follows will be considered text until a line break.So now here are a few examples of how to generate handlebars HTML:
{{#each controller}}<div class="fun" data-foo="5"><p>Hello</p></div>{{/each}}
each controller: .fun data-foo="5": p Hello
/ less ideal (IMO), but working alternatives
= each controller: .fun data-foo="5" %p Hello
= each controller: div.fun data-foo="5": p Hello
Anyway, there's a million permutations to this that I don't need to bother with, but I think you get the point.
I'm really happy with this approach; seems clean and sexy.
There's some existing sugar for using %
in a mustache invocation that sets the tagName
hash value that I need to kill of to make this work. Good riddance.
Wow, excellent "summary"! You sure think of it thoroughly, with the bunch of edge cases that you have given. I have not used neither PEG nor JISON/BISON before, so you're the expert here in language grammar. Just one observation:
This sentence
when a
:
is encountered, we should start over with this logic, and subject whatever follows the:
to the same rules.
if taken literally, would break existing syntax, e.g. when used in conjunction with ember's alternate true/false class bindings div class={isActive::hidden :content}
(example from issue 40 ). (Need exceptions for :
s used inside expressions.)
I'm 99% sure you have thought of that and the sentence is just an quick summary in natural language, but well, no harm pointing out I guess.
Yes, I'd definitely handle that case. It'll work the way you expect.
Thanks in advance @machty!
P.S. updated previous comment, wrong copy-and-paste issue link :-)
Closed by https://github.com/machty/emblem.js/commit/b5e44284c3e9ae030e8e36558d05e039af315cc0
Pretty happy about this one. The final syntax ended up having some slight differences from my long ass earlier post, but it should all seem pretty intuitive. Gonna spend some time shifting the docs to this style, but in the mean time, you can get an idea of the syntax by looking at https://github.com/machty/emblem.js/blob/master/spec/qunit_spec.coffee#L1553
Thank you so much for suggesting this and helping to work through it all!
The test suite use cases look impressive indeed. Great work @machty! I'm going to pull the latest version and test it on my emblem templates.
Once again many thanks for this (rather big) enhancement!
Hi @machty.
The new colon syntax is mostly working well, but I have found an edge case:
li data-foo='bar': a
(bar
is a literal) compilesli data-foo=bar: a
nor li data-foo=bar %a
(bar
is an ember binding) compile<li data-bind-attr-1="1">a</li>
(the binding is there, but the declared data-foo
attribute is missing)I'll continue applying the new syntax to my emblem templates, and I'll let you know if I find any more edge case.
Looking into it.
Note that li data-foo=bar %a
is not valid syntax. You need to have the colon. The only time you don't need the colon is if you're initiating a text block within a block mustache, such as
helperName | Text nested inside block helper.
Also, I don't know what you're saying here: first you say li data-foo=bar %a
doesn't compile, but then you say it compiles but the data-foo
is missing... which one is it? Also, if Ember/Handlebars can't find the non-falsy value for bar
, it may very well not render data-foo
attribute even if it's properly bound, so it might not be an emblem thing.
This all seems to be compiling correctly. Aside from the one with invalid syntax, I think you might just be running into a case where Handlebars isn't finding the property with the bar
path.
Sorry, I phrased poorly. Here is a proper one:
With bar
= somevalue
set (somewhere in model / controller / etc):
li data-foo=bar
a
baz
compiles (via handlebars) to
<li data-bind-attr-1="1" data-foo="somevalue"><a>baz</a></li>
From what I understand, the data-bind-attr-1="1"
is there for ember's own use; it is a hook so that ember knows where to update bar
. The data-foo="somewhere"
is the actual attribute that userland code wants to be there.
Using the colon syntax:
li data-foo=bar: a
baz
(I did not compact to a: baz
to isolate the problem more properly.)
This compiles (via handlebars) to
<li data-bind-attr-1="1">a baz</a>
Two issues:
a
is now treated as a literaldata-foo="somevalue"
is now missing. data-bind-attr-1="1"
is there, but ember will have nowhere to bind to.One potential issue:
a
literal and the nested literal baz
. I don't know if this is the intended behavior, or if this is a side-effect of the above bug.I can help with debugging. I understand that there are two steps here that can go wrong (emblem -> handlebars or handlebars -> html). This can be a handlebars fault, but I'm not sure which part went wrong, because the handlebars template is already in an evaluated form when I see it in the console. Perhaps you can tell me how to obtain the handlebars template output from emblem, and I'll post the handlebars templates?
The reference "working" case:
Both these emblem templates:
li data-foo='bar'
a
baz
li data-foo='bar': a
baz
compiles (via handlebars) to
<li data-foo="bar"><a>baz</a></li>
But here, bar
is a literal, not a binding reference.
Could you clone the repo and add a breaking test case to spec/qunit_spec.coffee
? You can build emblem and run the tests just by doing rake
or bundle exec rake
. I've tried copying your test cases myself but they all still seem to pass, and this would be a more direct way of demonstrating something that's broken for you. Once you have something that's breaking, I'll accept a pull request and then fix whatever doesn't seem to be working.
Thanks, I'll do that
Note that the Emblem tests presently just test vanilla Handlebars compilation (and stub out Ember helpers when testing Ember functionality), so there's not going to be any concept of bindAttr
in your output if you just do something like
a data-foo=bar Hello
This would just set data-foo="value_of_bar"
and not installing bindAttr
s, but that's fine, since the problems you're describing seem like syntactical issues that would affect both vanilla and Ember Handlebars.
Thanks for taking the time to do this!
Allow me some time, I'm digesting your qunit test suite and your fixtures.
I'm confused, what exactly is the relationship between emblem
, handlebars
, and html
? I originally thought emblem
would compile to handlebars
, which would then compile to html
. But looking at your test suite, it seems that emblem
compiles directly to html
, or that it is a part of handlebars
, or the other way round, etc.
In short, what should I expect from emblem
template tests? handlebars
? Or html
?
Well, you don't actually need to know the answer to those questions to copy the pattern of the test cases set up for the colon syntax that I linked to before, but all that Emblem does is compile to a Handlebars AST that Handlebars uses to compile the rest of the way, but for testing purposes, we need to check the final output to HTML, so we render the template to HTML because it's the easiest way to confirm that it does what we think it does.
But just copy one of the colon syntax cases; they're all in the format of 1) Here is Emblem code, 2) Here is the object passed to the template when it's rendering, 3) Here is the output HTML that I expect.
Should be a dependency problem. My guess is that the tests run on npm
modules, but emblem-rails
depends on gem
packages, and that some differences somewhere among these sets of modules caused the observed behavior. Here is what I did:
ember-rails
and emblem-rails
to it, and made a minimal "test case" template. :banghead: Now it fails as I described. The project is at heartsentwined/emblem-test, heroku demo here, key code and output extracted in this gist.li data-foo=bar: a quux
, the failing caseli data-foo='bar': a quux
, the control caseli data-foo=bar href='#': a quux
, curiously, I have found a workaround: by ensuring that the line-section terminates in a literal attribute (terminating in a quotation mark), like a href
(albeit invalid in a li
), everything works fine. Suggestive to a ":
without an immediately preceding ["']
" bug?emblem-rails
uses gem packages.handlebars
. npm's handlebars
is at 1.0.10
, but the corresponding gem handlebars-source
is at 1.0.9
or 1.0.0.rc{1,2,3}
. emblem uses the 1.0.10
handlebars module from npm, but there is no 1.0.10
gem.#= require handlebars
sprockets line. I traced it, and I think it should come from (handlebars-source)/dist/handlebars.js
. I tried removing handlebars.js
from the gem, and symlinking the one from emblem's (under node_module
). The error is still there. (I'm not confident on this step though, I might have done something wrong here.)ember-rails
, handlebars-source
, emblem-rails
, and coffee-script-source
, as far as dependencies allow. This is not fruitful though, because the latest emblem-rails
already restricts ember-rails
to the 0.11.*
branch, which in turn restricts handlebars-source
to 1.0.0.rc3
. I tried several combinations of older versions, and they, expectedly, fail.THANK YOU for going this in-depth, I've finally found a way to reproduce this error. It doesn't really have anything to do with dependency hell (but yes, stuff is really complicated, and handlebars
versioning really went off the deep end in that their package versions don't match the versions listed in the code). I should have it fixed shortly.
I have no idea how you got it, but good luck with the (hopefully) solution!
Try now, emblem-source
0.2.1.
The problem stems from testing only in a raw Handlebars context. There's syntax patterns that only get detected when Ember is enabled, and I thought they were similar enough that I could get away with not strictly testing it. Silly me. I really need to find a way to properly test Emberized-Emblem, but it's a really tricky thing to set up, since Emberized handlebars template expect a lot more information provided to them in order to render, stuff which might depend on there being a DOM, which kinda sucks. But anyway, I found your exact problem, and that's certainly totally fixed at this point. Lemme know if you find anything else, and thanks so much
Testing now. I think you mean emblem-source 0.2.1
. :-)
Working perfectly :+1: Brilliant!
wooo!
Will there be support for a compact inline tag shortcut denoted by the colon (
:
)?In slim,
can be written more succintly as
A lot of common patterns fit into this syntax:
li: a
,nav: ul
,a: img
, etc. They all belong to the family of items having multiple semantics: a link that is a list item, a list that is a navigation, an image that is a link, etc.