cream-and-sugar
Version:
A deliciously functional syntax for JavaScript with native support for JSX
152 lines (139 loc) • 7.8 kB
HTML
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Cream & Sugar: Method Chaining</title>
<link rel="icon" type="image/x-icon" href="https://jgnewman.github.io/cream-and-sugar/assets/images/favicon.ico" />
<link href="https://fonts.googleapis.com/css?family=Bitter:400,700|Open+Sans:300,400,800" rel="stylesheet">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css" />
<link rel="stylesheet" href="../../assets/css/app.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/styles/default.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/highlight.min.js"></script>
</head>
<body class="docs">
<div class="branding"></div>
<div class="content">
<div class="left-border">
<h1>Method Chaining</h1>
<h2 class="subhead">Clutter Reduction For the Win!</h2>
</div>
<p>Before diving into Cream & Sugar's shiny new chaining techniques, we ought to mention that we stole something really useful from CoffeeScript.</p>
<pre><code>foo?.bar?.baz
</code></pre><p>In JavaScript you sometimes have to work with multi-level objects where certain nodes in the tree may or may not exist. In order to avoid having to write things like <code>if (foo && foo.bar) { ... }</code>, C&S takes a tip from CoffeeScript and allows you to use the <code>?.</code> form to avoid errors.</p>
<pre><code>foo = {}
foo? #=> true
</code></pre><p>If a value statement ends with a <code>?</code> symbol, C&S will return a boolean telling you whether that value "exists" (in other words, that it is not null or undefined).</p>
<pre><code>foo = { bar: { baz: 'quux' } }
foo?.bar?.baz #=> 'quux'
foo?.schmoo?.baz #=> undefined
</code></pre><p>If the <code>?</code> symbol is used somewhere in the middle of a dot chain, the whole chain statement will terminate at the first undefined value without throwing an error.</p>
<p><br/></p>
<pre><code>((foo _)?.bar _)?.baz
</code></pre><p>This syntax even works with ugly, chained function calls, which we'll be talking about next.</p>
<pre><code># JavaScript chaining
foo(a, b).bar(c, d).baz(e, f)
# Cream & Sugar's gross equivalent
(((foo a b).bar c d).baz e f)
</code></pre><p>In Cream & Sugar, function calls do not take parentheses. This departure from JavaScript syntax has pros and cons. One thing you may notice rather quickly is that method chaining is not quite so easy. While it can be accomplished using standard call syntax, it is much nicer to use C&S' chain syntax.</p>
<p><br/></p>
<pre><code>chain
foo a, b
bar c, d
baz e, f
</code></pre><p>So instead of using all of those hard-to-parse parentheses, we can use the keyword <code>chain</code> followed by an indented block where each line constitutes a method chained off of the method before it.</p>
<p><br/></p>
<pre><code>chain
$ '#some-dom-node'
addClass 'foo'
insertBefore '#some-other-node'
chain
asyncCall _
then fn x => resolver x
then fn y => resolver y
catch fn z => end z
</code></pre><p>This is especially nice for things like jQuery or <code>promise</code> syntax.</p>
<p><br/>
<br/>
<br/>
<br/>
<br/></p>
<p>Because there are no dots in this syntax, there may be times when built-in function names or reserved words need to be used because they are method names on the object returned from the previous item in the chain. In cases like this, you might normally expect to get a compile or, even worse, a runtime error because the compiler translated out your method name for a bif by accident.</p>
<pre><code>chain
getObject _
~default 'some argument'
# Compiled output
getObject().default('some argument')
</code></pre><p>In all cases in Cream & Sugar you have the option of protecting a word from compiler translation by prefixing it with the <code>~</code> symbol. The provided example uses <code>~default</code> in order to prevent the compiler from tripping up on the fact that <code>default</code> is normally a reserved word.</p>
</div>
<section id="docs-app"></section>
<script>
// Section for random JavaScript hacks just to get this out quickly.
window.addEventListener('load', function () {
// Fix indentation in code blocks the best we can since panini's
// markdown compiler injects whitespace into pre tags.
Array.prototype.slice.call(document.querySelectorAll('pre code')).forEach(function (code) {
var lines = code.innerHTML.trim().split('\n');
var out = [lines[0]];
var sliceAmount;
lines.slice(1).forEach(function (line, index) {
if (index === 0) {
var whitespace = line.match(/^\s+/);
whitespace = whitespace ? whitespace[0] : '';
if (/([=-](\>)?|\{|match|\>|div|try|chain|default\s+[^\n])$/.test(lines[0])) {
whitespace = whitespace.slice(2);
}
sliceAmount = whitespace;
}
out.push(line.replace(sliceAmount, ''));
});
code.innerHTML = out.join('\n');
code.className = 'visible lang-coffeescript';
hljs.highlightBlock(code);
});
// Attach id's to h4 tags so that we can link to specific bifs.
Array.prototype.slice.call(document.querySelectorAll('h4')).forEach(function (h4) {
var html = h4.innerHTML.split(/\s+/g);
html[0] = '<span class="bif" id="' + html[0] + '">' + html[0] + '</span>';
h4.innerHTML = html.join(' ');
h4.className = 'visible';
});
// Fix relative link issues with the github site living in a subdirectory
Array.prototype.slice.call(document.querySelectorAll('.content a')).forEach(function (a) {
if (/^\/cream-and-sugar\//.test(location.pathname)) {
a.setAttribute('href', '/cream-and-sugar' + a.getAttribute('href'))
}
});
// Some script conflict is preventing us from linking to a certain
// spot on the page so here we just wait until the next run loop
// and if we have a hash in the location object, scroll to it.
(function () {
var hash = location.hash;
if (hash) {
var item = document.querySelector(hash);
setTimeout(function () {
item && window.scroll(0, item.offsetParent.offsetTop);
},0);
}
}());
// Fixes a responsiveness issue where on large screens sometimes
// the offgray sidebar isn't tall enough. Make sure its always
// tall enough whenever the screen resizes.
(function () {
var branding = document.querySelector('.branding');
var content = document.querySelector('.content');
function resizerizer() {
var brandingHeight = branding.offsetHeight;
content.style.height = '';
if (content.offsetHeight < brandingHeight) {
content.style.height = brandingHeight + 'px';
}
}
window.addEventListener('resize', resizerizer);
resizerizer();
}());
});
</script>
<script src="../../assets/js/app.js"></script>
</body>
</html>