tag:blogger.com,1999:blog-55388360608235179692024-03-19T00:22:17.015-07:00Jamund's Coding BlogJamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.comBlogger128125tag:blogger.com,1999:blog-5538836060823517969.post-82463194108903189742015-06-27T12:04:00.002-07:002015-06-27T15:28:13.360-07:00From Require.js to Webpack - Part 1 (the reasons)<span style="font-family: Times, Times New Roman, serif;">Wherein I layout all of the reasons why we decided to move off of <a href="http://requirejs.org/">require.js</a> and on to <a href="http://webpack.github.io/">webpack</a>.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">About 2 months ago we completed a migration from require.js to webpack. Require.js had been the primary UI module system at PayPal and was baked into the default template kraken server we use to build our applications. Everyone was familiar with it and it worked pretty well. Despite our success with it, I identified a few features that were provided by webpack that ultimately led my team to migrate off of require.js. This is an overview of the reasons why we chose webpack.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">I hope this proves helpful to you as you seek to improve your own module/build systems!</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<br />
<h3>
<span style="font-family: Arial, Helvetica, sans-serif; font-size: large;">
CommonJS By Default</span></h3>
<div>
<b><span style="font-family: Times, Times New Roman, serif;"><br /></span></b></div>
<span style="font-family: Times, Times New Roman, serif;">Working in node all of the time, we sure like our common js modules. Despite a handful of CJS-like structures supported by require.js (including the "lite" CJS wrapper and partial support for the syntax within r.js), it really does not work as expected. There are a number of key differences.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">1. Confusion about returning vs. module.exporting</span><br />
<span style="font-family: Times, Times New Roman, serif;">2. Module resolution path (it will default to ".", which is basically like adding <b>path.resolve() </b>to everything.)</span><br />
<span style="font-family: Times, Times New Roman, serif;">3. NPM support (including the package.json "main" field, etc.)</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<br />
<h4>
<b><span style="font-family: Arial, Helvetica, sans-serif;">Client-side Unit Testing</span></b></h4>
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">One of the immediate benefits we experienced was being able to rip out this excessively annoying code we had to put in place to support client-side unit testing. There was a great deal of things we had to do to get mocha to support the files in the require.js. All of this code we were literally able to rip out and throw away once a file was moved to CJS, because node could just read the module and as long as it didn't rely on the DOM it could be tested like any other node module. Up until this point our client-side coverage had been terrible. With this change it was much easier to ratchet up the testing much further.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<br />
<h4>
</h4>
<h4>
<span style="font-family: Arial, Helvetica, sans-serif;">
Re-use and modularity</span></h4>
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">Moving to CJS also opened up a flood of module reusability. This reusability was always available with AMD modules. Why the change? The reasons were 2 fold:</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">1. Syntax parity with our node.js code meant we started applying the same practices we were already applying to our node code to our client-side code. It turns out that we associated certain patterns with AMD code (monolothic) and certain types of patterns with our CJS code (modular). This meant that even though the shift was primarily mental, we quickly adopted a more modular approach to our UI code-bases when took that mental shift.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">2. Actual compatibility with our server-side code meant that a number of important modules (date/time formatting, various utilities, etc.) could be directly shared and reused immediately.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">Re-sharpening of our focus on modularity and sharing was one of the main benefits of the move to webpack.</span><br />
<h4>
<b><span style="font-family: Times, Times New Roman, serif;"><br /></span></b></h4>
<h4>
<b><span style="font-family: Arial, Helvetica, sans-serif; font-size: large;">NPM Support</span></b></h4>
<div>
<b><span style="font-family: Times, Times New Roman, serif;"><br /></span></b></div>
<span style="font-family: Times, Times New Roman, serif;">While the CJS syntax itself is quite a bit nicer than the AMD syntax and provides a bunch of benefits as listed above. The most important reason for switching to webpack is full NPM support. Meaning I can install any module with NPM and without any extra work <b>require()</b> that into my library. Technically, this was possible with require.js, but it was really difficult and limited in scope:</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<b><span style="font-family: inherit;">NPM with require.js:</span></b><br />
<span style="font-family: Times, Times New Roman, serif;"><b><br /></b>1. <b>Expose you</b>r<b> node_modules </b>folder as a publicly accessible route in your app.</span><br />
<span style="font-family: Times, Times New Roman, serif;">2. <b>Add an alias</b> from your module to the convoluted new module path in your config.js.</span><br />
<span style="font-family: Times, Times New Roman, serif;">3. Make sure the module you want has 0 dependencies (<i>because they won't work at all</i>.)</span><br />
<span style="font-family: Times, Times New Roman, serif;">3. <b>define('your-module', function(yourModule) { });</b></span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<b><span style="font-family: Times, Times New Roman, serif;">NPM with webpack:</span></b><br />
<span style="font-family: Times, Times New Roman, serif;"><b><br /></b>1. <b>require()</b> the module.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<br />
<h3>
<b><span style="font-family: Arial, Helvetica, sans-serif; font-size: large;">Mature ES6/Plugin Support</span></b></h3>
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">Webpack Loaders are awesome. Require.js plugins on the other hand are not well supported and syntax can be confusing </span><span style="background-color: #f7f7f7; color: #333333; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13.6000003814697px; line-height: 1.45;">define([</span><span class="pl-s" style="box-sizing: border-box; color: #183691; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13.6000003814697px; line-height: 1.45;"><span class="pl-pds" style="box-sizing: border-box;">'</span>css!styles/main<span class="pl-pds" style="box-sizing: border-box;">'</span></span><span style="background-color: #f7f7f7; color: #333333; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13.6000003814697px; line-height: 1.45;">], </span><span class="pl-k" style="box-sizing: border-box; color: #a71d5d; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13.6000003814697px; line-height: 1.45;">function</span><span style="background-color: #f7f7f7; color: #333333; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13.6000003814697px; line-height: 1.45;">() { /* .. */})</span><span style="font-family: Times, 'Times New Roman', serif;">. The loader config in webpack is a breeze and the plugin ecosystem is much stronger.</span><br />
<span style="font-family: Times, 'Times New Roman', serif;"><br /></span>
<span style="font-family: Times, 'Times New Roman', serif;">With requirejs I tried to add ES6 support using the legacy <a href="https://github.com/guybedford/es6">es6 require.js plugin</a> which is based on <a href="https://github.com/google/traceur-compiler">Traceur</a>. I just couldn't get it to work, so we moved to 6to5 (</span><a href="https://babeljs.io/" style="font-family: Times, 'Times New Roman', serif;">babel</a><span style="font-family: Times, 'Times New Roman', serif;">) </span><span style="font-family: Times, 'Times New Roman', serif;">using a two-step process. First, </span><span style="font-family: Times, 'Times New Roman', serif;">we'd run <a href="https://babeljs.io/">babel</a> to convert our files to JS and then we'd suck them up with </span><a href="http://requirejs.org/" style="font-family: Times, 'Times New Roman', serif;">require.js</a><span style="font-family: Times, 'Times New Roman', serif;">. Later we'd need to run some kind of "clean" command to remove the extra files it left hanging around our file system. </span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">Using <a href="https://github.com/babel/babel-loader">babel-loader</a> had proved to be a lot simpler and nicer and has cut both one step and additional time out of our build process. Source maps of course work out of the box as well.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">With all of the great plugins including built-in support for uglify and other thing we barely need <b>grunt</b> at all for building our app. Almost all of it is done by webpack directly.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<b><span style="font-family: Arial, Helvetica, sans-serif; font-size: large;">Concerns</span></b><br />
<div>
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">When I showed the team the changes and talked with various people about them I'm frequently bombarded with the same two questions. People are generally under the assumption that it's no longer possible to do an asynchronous <b>require() </b>and others wonder why we didn't just go with <a href="http://browserify.org/">browserify</a>. There are good stories for both, so hear me out.</span></div>
<h4>
<b><span style="font-family: Times, Times New Roman, serif;"><br /></span></b></h4>
<h4>
<b><span style="font-family: Times, Times New Roman, serif;">What about asynchronous/dynamic requires?</span></b></h4>
<div>
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">Asynchronous require's are often helpful in large projects. In our case we used the AMD <b>require([]) </b>at the router level to do something like this:</span></div>
<div>
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div>
<b><span style="font-family: Courier New, Courier, monospace;">routeChange(path) {</span></b></div>
<div>
<b><span style="font-family: Courier New, Courier, monospace;"> require(['/views/' + path], function(PageView) {</span></b></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><b> app.setView(</b><b>new PageView()</b><b>);</b></span></div>
<div>
<b><span style="font-family: Courier New, Courier, monospace;"> }</span></b></div>
<div>
<b><span style="font-family: Courier New, Courier, monospace;">}</span></b></div>
<div>
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div>
<span style="font-family: Times, Times New Roman, serif;">This allowed us to avoid defer loading hundreds of kb of code until it was absolutely necessary and avoid loading code for different parts of the app that weren't being accessed at this time. It's a pretty neat trick and one we didn't really want to give up.</span></div>
<div>
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div>
<span style="font-family: Times, Times New Roman, serif;">Turns out, we didn't have to give it up. <u>Webpack supports this with only minor changes to the code above</u>. In the end our various bundles were the same size or smaller than the ones created with <b>r.js</b> (not to mention the build time was cut down from 50s to around 10s).</span></div>
<div>
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div>
<span style="font-family: Times, Times New Roman, serif;">I'll go more into the exact code we used to get this to work in the follow up, but if you're interested you can check out Pete Hunt's popular <a href="https://github.com/petehunt/webpack-howto">Webpack How-To</a> for those getting started which has a nice section on <a href="https://github.com/petehunt/webpack-howto#9-async-loading">async loading</a>.</span></div>
<div>
<b><span style="font-family: Times, Times New Roman, serif;"><br /></span></b></div>
<div>
<b><span style="font-family: Arial, Helvetica, sans-serif;">Why not browserify?</span></b></div>
<div>
<b><span style="font-family: Times, Times New Roman, serif;"><br /></span></b></div>
<span style="font-family: Times, Times New Roman, serif;">Browserify is pretty legit and indeed my first 2 attempts to migrate to away from require.js were both to browserify. Browserify has been innovating in this space for ages and was the obvious choice when I started this project. Where did it fall down?</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">1. Spotty support for AMD modules (even with the <a href="https://github.com/jaredhanson/deamdify">deAMDify plugin</a>)</span><br />
<span style="font-family: Times, Times New Roman, serif;">2. No built-in way to asynchronously require().</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<b><span style="color: #666666; font-family: Arial, Helvetica, sans-serif;"><i>AMD Support</i></span></b><br />
<div>
<b><span style="font-family: Times, Times New Roman, serif;"><br /></span></b></div>
<span style="font-family: Times, Times New Roman, serif;">Because migrating all of our UI code from CommonJS to AMD meant changing nearly 1000 files, clearly we wanted to automate that in some way. A number of tools support this in one capacity or another, but none of them worked very well. As with most folks migrating away from AMD land, the easiest way with browserify was to use the <a href="https://github.com/jaredhanson/deamdify">deAMDify transform</a>. This almost worked! Despite it being sold as the solution to our problems it fell down pretty hard in two areas:</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">1. The path resolution was still common.js based. It didn't know about our public/js folder. It didn't look for "module" in a relative path by default like AMD does, it looked for it in NPM. It ran into a lot of problems. Eventually I had to fork the transform in order to add support for this functionality, which was a pretty big pain in the neck.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">2. It didn't support dependencies that weren't assigned to variables.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<b><span style="font-family: Courier New, Courier, monospace;">define('dependency', function() { }); </span></b><span style="font-family: Times, Times New Roman, serif;">was not translated to </span><b><span style="font-family: Courier New, Courier, monospace;">require('dependency')</span><span style="font-family: Times, Times New Roman, serif;"> </span></b><span style="font-family: Times, Times New Roman, serif;">as expected. It just broke the system.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br />While this wasn't too hard to fix, the module itself had been abandoned by the maintainer and despite multiple tries to help, at that time we weren't able to get any of this stuff fixed. (Thankfully <a href="https://github.com/tbranyen">Tim Branyen</a> has taken over the the deamdify module now and is very responsive to changes!) </span><br />
<span style="font-family: Times, Times New Roman, serif;"><br />While the AMD support was frustrating, ultimately we were able to fix it, but we ran into an even bigger hurdle, which ultimately ended our test with browserify.</span><br />
<b><br class="Apple-interchange-newline" /><span style="color: #666666; font-family: Arial, Helvetica, sans-serif;"><i>Async require()</i></span></b><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">I already covered how webpack supports AMD-style require() statements above. It turns out that there's nothing like that built in to browserify. The suggestion is to just basically load everything up front. This is all well and good for most apps. For an app the size of PayPal.com it turns out it's not such a good idea and leads to megabytes of extra code being loaded.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">Another alternative to async require() with browserify is to <a href="https://github.com/substack/node-browserify#multiple-bundles">manually bundle things</a>. This will actually work. You need to add two <script> tags to your page. One for the main bundle and another for the specific "sub-app" that you're working with. This is pretty great if you start your app this way, but going from async require() to a lt;script&gt tag approach for each sub-app was extremely hard. I didn't have time to completely re-factor our routing system and test everything, so eventually I gave up.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<span style="font-family: Times, Times New Roman, serif;">Webpack allowed me to get the benefits of browserify without having to re-factor major parts of our codebase.</span><br />
<br />
<h3>
<b><span style="font-family: Arial, Helvetica, sans-serif;">Conclusion</span></b></h3>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Times; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 1; word-spacing: 0px;">
<div style="margin: 0px;">
<br />
<span style="font-family: Times, Times New Roman, serif;">Webpack has been a huge help to our client-side code base and developer experience in general. It's allowed greater parity and reuse between our client-side and node code, it's made testing our code much easier and it's allowed us to cut way down on the config and extra support code needed to maintain two different module systems in the same code base. The most important thing it has provided though is access to the NPM ecosystem in the browser. Coding will never be the same again :)</span></div>
<div style="margin: 0px;">
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div style="margin: 0px;">
<span style="font-family: Times, Times New Roman, serif;"><u>Look for a follow-up post detailing the technical aspects of the migration in the next couple of weeks.</u></span><br />
<span style="font-family: Times, Times New Roman, serif;"><u><br /></u></span></div>
<div style="margin: 0px;">
<span style="font-family: Times, Times New Roman, serif; font-size: x-small;">* In case you wanted to know most of the UI code at the time of the migration was written in Backbone. Unmentioned in the article was that we're starting to move a lot of stuff to React and NPM support was crucial for us to do that well.</span></div>
</div>
Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com0tag:blogger.com,1999:blog-5538836060823517969.post-16008930980125700482014-03-14T12:55:00.000-07:002014-03-14T12:55:34.519-07:00Custom Error Objects in JavaScriptFor an upcoming talk at <a href="http://mtnwestjs.org/2014/sessions">Mountain West JavaScript</a> I'm going to talk about some basics in error handling. One of the advanced techniques that's a bit more advanced is the idea of creating custom errors objects to pass through your callbacks.<br />
<h4>
Why Error objects?</h4>
When following the callback pattern it's really nice to be able to depend that your error will return data in a consistent format. Error objects give you 2 things that are super important: message and a stack trace. Here's an example:<br />
<br />
<img height="176" src="https://i.cloudup.com/Jz3ecGURQ2-3000x3000.png" style="-webkit-user-select: none;" width="400" /><br />
<br />
The stacktrace is extremely important and is likely the reason why using standard error objects is so popular in node.<br />
<h4>
Why Custom Error Objects?</h4>
<div>
Despite the awesomeness of general error objects, sometimes you need more information. You may need an array of error messages or some kind of error code in addition to the error message. This is where custom error objects come into play.</div>
<div>
<h4>
Creating a Custom Error Object</h4>
</div>
<div>
This is the most succinct constructor I've found to create a custom Error in Node. Importantly it satisfies several criteria:</div>
<div>
<ul>
<li>There is a <span style="background-color: white; color: #999999; font-family: 'Courier New', Courier, monospace;">message</span> property which includes the error message</li>
<li>There is a stack track (as a string in the <span style="background-color: white; color: #999999; font-family: Courier New, Courier, monospace;">stack</span> property)</li>
<li>The error type is included in the stack trace (which is based on the <span style="background-color: white; color: #999999; font-family: 'Courier New', Courier, monospace;">name</span> property)</li>
<li><span style="background-color: white; color: #999999; font-family: 'Courier New', Courier, monospace;"><span style="color: black; font-family: Times;">You legitimately inherit from </span>Error<span style="color: black; font-family: Times;"> (i.e. </span>instanceof Error <span style="color: black; font-family: Times;">is </span>true)</span></li>
</ul>
</div>
<div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; display: inline; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px; vertical-align: middle; word-wrap: normal;">function MountainError(message) {
Error.captureStackTrace(this);
this.message = message;
this.name = "MountainError";
}
MountainError.prototype = Object.create(Error.prototype);
</code></pre>
</div>
<span style="font-family: inherit;">So go ahead and create this new object and see what you get!</span><br />
<div>
<code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; display: inline; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px; vertical-align: middle; word-wrap: normal;"><br /></code></div>
<div>
<code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; display: inline; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px; vertical-align: middle; word-wrap: normal;"><br /></code></div>
<div>
<img height="191" src="https://i.cloudup.com/4FUCanJXjM-3000x3000.png" style="-webkit-user-select: none;" width="400" /></div>
<div>
<span style="font-size: 12px; font-weight: normal;"><span style="font-family: inherit;"><br /></span></span>
<span style="font-weight: normal;"><span style="font-family: inherit;">Your new error object includes all of the standard stuff that you'd see in an error (including the stack trace, but you can start fiddling with it, adding extra properties, etc).</span></span><br />
<span style="font-weight: normal;"><span style="font-family: inherit;"><br /></span></span>
<span style="font-weight: normal;"><span style="font-family: inherit;">Happy erroring!</span></span><br />
<h4>
Resources</h4>
</div>
<div>
<ul>
<li><a href="http://www.devthought.com/2011/12/22/a-string-is-not-an-error/">Guillermo Rauch: A String is not an Error</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error">MDN: Error Object</a></li>
<li><a href="https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi">V8 JavaScript Stack Trace Api</a></li>
<li><a href="https://github.com/v8/v8/blob/master/src/messages.js#L1050">V8 Source Code: Error Objects</a></li>
</ul>
</div>
<div>
<br /></div>
Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com3tag:blogger.com,1999:blog-5538836060823517969.post-86599498721493797572013-08-15T09:57:00.001-07:002013-08-15T10:02:01.856-07:00Stop Complaining about the Angular.js Docs<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<i>This article assumes you've used github but that you're not a pro at contributing to large projects.</i></div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
After rolling out some internal angular.js apps for my team I found a lot of people can be confused by the angular docs. Its documentation covers many things, but the examples and explanations don't always make sense to those new to angular. Just the same as with any open-source project, the poor documentation is just as much my responsibility as it is the responsibility of anyone on the Angular team.</div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
How much documentation had I written? How many pull requests had I sent? The answer, obviously was none, so I went to github, forked the project and decided to make the<span class="Apple-converted-space"> </span><a href="http://docs.angularjs.org/api/ng.directive:ngClass" rel="noreferrer" style="color: #4183c4; text-decoration: none;">ng:class docs</a><span class="Apple-converted-space"> </span>suck a little bit less.</div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
I was pretty sure at this point that it would take me about 5 minutes to come up with a better example, but it ended up taking me many nights to get it just right and finally get some small changes merged back into the main project.</div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
I don't want you thinking it's too hard or a waste of time.<span class="Apple-converted-space"> </span><i>It is totally worth it!</i><span class="Apple-converted-space"> </span>I'll share what mistakes I made and what I found complicated, so you can help improve the docs right away!</div>
<h2 style="-webkit-text-stroke-width: 0px; border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: black; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; font-style: normal; font-variant: normal; font-weight: bold; letter-spacing: normal; line-height: 1.7; margin: 1em 0px 15px; orphans: auto; padding: 0px; position: relative; text-align: start; text-indent: 0px; text-rendering: optimizelegibility; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<a class="anchor" href="https://gist.github.com/xjamundx/862795aa46ad0d5bbcae#getting-setup" name="getting-setup" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; position: absolute; text-decoration: none; top: 0px;"><span class="mini-icon mini-icon-link"></span></a>Getting Setup</h2>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
Before starting you'll need to have reasonably new versions of the following software installed on your machine:</div>
<ul style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; padding: 0px 0px 0px 30px; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<li><a href="http://nodejs.org/" rel="noreferrer" style="color: #4183c4; text-decoration: none;">http://nodejs.org/</a></li>
<li><a href="https://gist.github.com/xjamundx/862795aa46ad0d5bbcae" rel="noreferrer" style="color: #4183c4; text-decoration: none;">http://gruntjs.com/</a></li>
<li><a href="http://bower.io/" rel="noreferrer" style="color: #4183c4; text-decoration: none;">http://bower.io/</a></li>
</ul>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
Then you'll need to fork the<span class="Apple-converted-space"> </span><a href="https://github.com/angular/angular.js" rel="noreferrer" style="color: #4183c4; text-decoration: none;">project</a><span class="Apple-converted-space"> </span>and check it locally. You may also want to create a branch where you can do your work.</div>
<pre style="-webkit-text-stroke-width: 0px; background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: black; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 19px; margin: 15px 0px; orphans: auto; overflow: auto; padding: 6px 10px; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;">git clone https://github.com/YOURNAME/angular.js.git`
cd angular.js
git checkout -b docs_update
</code></pre>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
Now typing the<span class="Apple-converted-space"> </span><code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">grunt package</code><span class="Apple-converted-space"> </span>command will prepare your files for being viewing and testing locally.</div>
<pre style="-webkit-text-stroke-width: 0px; background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: black; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 19px; margin: 15px 0px; orphans: auto; overflow: auto; padding: 6px 10px; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;">grunt package
</code></pre>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
With that in place you should now be able to browse the current docs by going to<a href="http://localhost:8000/build/docs/" rel="noreferrer" style="color: #4183c4; text-decoration: none;">http://localhost:8000/build/docs/</a>.</div>
<h2 style="-webkit-text-stroke-width: 0px; border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: black; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; font-style: normal; font-variant: normal; font-weight: bold; letter-spacing: normal; line-height: 1.7; margin: 1em 0px 15px; orphans: auto; padding: 0px; position: relative; text-align: start; text-indent: 0px; text-rendering: optimizelegibility; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<a class="anchor" href="https://gist.github.com/xjamundx/862795aa46ad0d5bbcae#where-is-the-documentation" name="where-is-the-documentation" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; position: absolute; text-decoration: none; top: 0px;"><span class="mini-icon mini-icon-link"></span></a>Where is the documentation?</h2>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
All of the documentation, including examples, is found within the angular.js source code. For example, the ng:class source code is located inside of the<span class="Apple-converted-space"> </span><code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">src/ng/directive/ngClass.js</code><span class="Apple-converted-space"> </span>file. The code examples use custom markup (angular directives) that look something like this:</div>
<pre style="-webkit-text-stroke-width: 0px; background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: black; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 19px; margin: 15px 0px; orphans: auto; overflow: auto; padding: 6px 10px; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><example>
</example></code><span style="font-family: "Courier New",Courier,monospace;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><example><code style="background-color: transparent; border-radius: 3px 3px 3px 3px; border: medium none; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; border-radius: 3px 3px 3px 3px; border: medium none; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><example></span></code></code></example></code></span></pre>
<pre style="-webkit-text-stroke-width: 0px; background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: black; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 19px; margin: 15px 0px; orphans: auto; overflow: auto; padding: 6px 10px; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px;"><span style="font-family: "Courier New",Courier,monospace;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><example><code style="background-color: transparent; border-radius: 3px 3px 3px 3px; border: medium none; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; border-radius: 3px 3px 3px 3px; border: medium none; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"> </span></code></code></example></code></span><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><example><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><file name="index.html">…</span></code></code></example></code><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><example><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><</span></code></code></code>/file>
</span></code></code></example></code><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><example><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><</span></code></code></code>file name="style.css">…</span></code></code></example></code><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><example><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><</span></code></code></code>/file>
</span></code></code></example></code><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><example><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><</span></code></code></code>file name="scenario.js">…</span></code></code></example></code><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><example><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><</span></code></code></code>/file>
</span></code></code></example></code><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><example><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: "Courier New",Courier,monospace;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; border-radius: 3px 3px 3px 3px; border: medium none; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><code style="background-color: transparent; border-radius: 3px 3px 3px 3px; border: medium none; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"><span style="font-family: "Courier New",Courier,monospace;"></example></span></code></code></code></span></span>
</code></code>
<code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;"> </code> </example>
</code></pre>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
These correspond with the tabs generated in the example portion of the documentation.</div>
<h2 style="-webkit-text-stroke-width: 0px; border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: black; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; font-style: normal; font-variant: normal; font-weight: bold; letter-spacing: normal; line-height: 1.7; margin: 1em 0px 15px; orphans: auto; padding: 0px; position: relative; text-align: start; text-indent: 0px; text-rendering: optimizelegibility; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<a class="anchor" href="https://gist.github.com/xjamundx/862795aa46ad0d5bbcae#making-changes" name="making-changes" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; position: absolute; text-decoration: none; top: 0px;"><span class="mini-icon mini-icon-link"></span></a>Making Changes</h2>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
When you are ready to start editing the documentation examples you can just press the "Edit" button included on each page. This button will take you to a working version of that example on a 3rd party site that you can modify to your liking.</div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
Once you've modified the code enough paste it back into the comments of your source code, comment it out and make sure it works.</div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
After you make a change you should</div>
<pre style="-webkit-text-stroke-width: 0px; background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: black; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 19px; margin: 15px 0px; orphans: auto; overflow: auto; padding: 6px 10px; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;">grunt docs:process
</code></pre>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
Once that's done you can check your work locally by running a webserver and viewing the documentation.</div>
<pre style="-webkit-text-stroke-width: 0px; background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: black; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 19px; margin: 15px 0px; orphans: auto; overflow: auto; padding: 6px 10px; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;">grunt webserver
</code></pre>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
This will start a local webserver on port 8080 and allow you to visit the docs at a URL similar to this one:<a href="http://localhost:8000/build/docs/api/ng.directive:ngClass" rel="noreferrer" style="color: #4183c4; text-decoration: none;">http://localhost:8000/build/docs/api/ng.directive:ngClass</a></div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<i>Some of the CSS used in the angular docs may be different from what is used on the site where you edited your example, so make sure you check out your example on the actual documentation site.</i></div>
<h2 style="-webkit-text-stroke-width: 0px; border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: black; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; font-style: normal; font-variant: normal; font-weight: bold; letter-spacing: normal; line-height: 1.7; margin: 1em 0px 15px; orphans: auto; padding: 0px; position: relative; text-align: start; text-indent: 0px; text-rendering: optimizelegibility; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<a class="anchor" href="https://gist.github.com/xjamundx/862795aa46ad0d5bbcae#testing-your-work" name="testing-your-work" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; position: absolute; text-decoration: none; top: 0px;"><span class="mini-icon mini-icon-link"></span></a>Testing your work</h2>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
Angular has this amazing things where the tests are run from within the documentation. This is done inside of the<span class="Apple-converted-space"> </span><code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;"><file name="scenario.js"></file></code><span class="Apple-converted-space"> </span>area in the<span class="Apple-converted-space"> </span><code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;"><example></example></code>. More than likely the example you're updating already has some basic test, but you'll want to update it with your current code, process the docs, and the run them:</div>
<pre style="-webkit-text-stroke-width: 0px; background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: black; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 19px; margin: 15px 0px; orphans: auto; overflow: auto; padding: 6px 10px; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;">grunt docs:process
grunt test:e2e
</code></pre>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
Try to make your tests as comprehensive as possible.</div>
<h2 style="-webkit-text-stroke-width: 0px; border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: black; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; font-style: normal; font-variant: normal; font-weight: bold; letter-spacing: normal; line-height: 1.7; margin: 1em 0px 15px; orphans: auto; padding: 0px; position: relative; text-align: start; text-indent: 0px; text-rendering: optimizelegibility; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<a class="anchor" href="https://gist.github.com/xjamundx/862795aa46ad0d5bbcae#submitting-your-work" name="submitting-your-work" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; position: absolute; text-decoration: none; top: 0px;"><span class="mini-icon mini-icon-link"></span></a>Submitting your work</h2>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
Once you have made sure your tests pass and you've got what you think is a better example than before you can submit your pull request to the team. Point your pull request at angular.js/master.</div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
You will probably receive a lot of feedback about your pull request. My small changes to ng-class took about a month of back and forth before being merged which<span class="Apple-converted-space"> </span><a href="https://github.com/angular/angular.js/pull/3084" rel="noreferrer" style="color: #4183c4; text-decoration: none;">you can read yourself</a>.</div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
But I was super happy when they finally went live recently. Hurary</div>
<h2 style="-webkit-text-stroke-width: 0px; border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: black; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; font-style: normal; font-variant: normal; font-weight: bold; letter-spacing: normal; line-height: 1.7; margin: 1em 0px 15px; orphans: auto; padding: 0px; position: relative; text-align: start; text-indent: 0px; text-rendering: optimizelegibility; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<a class="anchor" href="https://gist.github.com/xjamundx/862795aa46ad0d5bbcae#helpful-tips" name="helpful-tips" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; position: absolute; text-decoration: none; top: 0px;"><span class="mini-icon mini-icon-link"></span></a>Helpful Tips</h2>
<ul style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; padding: 0px 0px 0px 30px; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<li>The team suggests that you rebase your code into a single commit. More info about using<span class="Apple-converted-space"> </span><a href="https://sandofsky.com/blog/git-workflow.html" rel="noreferrer" style="color: #4183c4; text-decoration: none;">git rebase</a>.</li>
<li><a href="http://www.blogger.com/null" rel="noreferrer" style="color: #4183c4; text-decoration: none;">Angular.js Contribution Guidlines</a><span class="Apple-converted-space"> </span>are a good place to start.</li>
</ul>
<h2 style="-webkit-text-stroke-width: 0px; border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: black; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; font-style: normal; font-variant: normal; font-weight: bold; letter-spacing: normal; line-height: 1.7; margin: 1em 0px 15px; orphans: auto; padding: 0px; position: relative; text-align: start; text-indent: 0px; text-rendering: optimizelegibility; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<a class="anchor" href="https://gist.github.com/xjamundx/862795aa46ad0d5bbcae#solving-problems" name="solving-problems" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; position: absolute; text-decoration: none; top: 0px;"><span class="mini-icon mini-icon-link"></span></a>Solving Problems</h2>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<b style="font-weight: bold;">What happens if my branch gets really out of date with the main angular branch?</b></div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
In these cases you should rebase with the main angular repo and resolve any conlicts. The first step to doing that is setting up a new remote origin called<span class="Apple-converted-space"> </span><code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">upstream</code><span class="Apple-converted-space"> </span>that will let you hook directly into the angular repo.</div>
<pre style="-webkit-text-stroke-width: 0px; background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: black; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 19px; margin: 15px 0px; orphans: auto; overflow: auto; padding: 6px 10px; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;">git remote add upstream https://github.com/angular/angular.js.git
</code></pre>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
The second step will be to rebase your branch against that repo:</div>
<pre style="-webkit-text-stroke-width: 0px; background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: black; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 19px; margin: 15px 0px; orphans: auto; overflow: auto; padding: 6px 10px; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px;"><code style="background-color: transparent; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 0px; padding: 0px; white-space: pre;">git rebase upstream/master
</code></pre>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
For more information about rebasing please check out github's suggested regarding this matter.</div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<b style="font-weight: bold;">What about filling out the CLA?</b></div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 25px; margin: 15px 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
A community licensicing agreement is basically a way for Google's lawyers to be sure that you're not going to claim you have any rights to the code after you give it to them. You can't sue them for abusing your pull request. It's unclear for contributing docs if it's required, but the instructions can be found here:<a href="http://docs.angularjs.org/misc/contribute" rel="noreferrer" style="color: #4183c4; text-decoration: none;">http://docs.angularjs.org/misc/contribute</a></div>
Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com8tag:blogger.com,1999:blog-5538836060823517969.post-30556801077117469222013-01-19T13:21:00.000-08:002013-01-21T12:11:52.997-08:00Versioned APIs with ExpressAfter reading and watching some of the stuff coming out from Apigee about <a href="http://blog.apigee.com/detail/api_design_third_edition_video_slides">API Design</a>, I've been convinced that APIs should probably be versioned in the URL using a scheme like this:<br />
<a href="http://api.whatever.com/v1/jobs">http://api.whatever.com/v1/jobs</a>
<br />
<br />
By the time I realized this the API I had been building with node.js was basically already complete, so I was a little bit sad that I'd have to come up with some complicated versioning scheme.<br />
<br />
After a bit of fretting I realized that <a href="http://expressjs.com/">express</a> can handle this super easy using an interesting feature of the express() instance that it can be mounted to another route using app.use().<br />
<br />
<b>This means that your routes don't know which version they are!</b><br />
<br />
Here's your main app file.<br />
<br />
<b>app.js file</b><br />
<script src="https://gist.github.com/4575166.js"></script>
<br/>Then you'll put each version of your API in the routes/ folder named after the version.<br />
<br />
<b>routes/v0.js file</b><br />
<script src="https://gist.github.com/4575184.js"></script>
Each version of the API can have middleware (such as unique authentication) that only works on that version of the API.
One thing that this doesn't cover is unique models per version, but I'll leave that up to you to figure out how to accomplish, because the needs of each app very quite a bit by app.
Hope this tip helps!
Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com2tag:blogger.com,1999:blog-5538836060823517969.post-5439776494703518332012-08-14T13:08:00.002-07:002012-08-14T13:21:15.653-07:00Everything I Know About SVG
<br />
<style>
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote {
margin: 0;
padding: 0;
}
body {
font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", Arial, sans-serif;
font-size: 13px;
line-height: 18px;
color: #737373;
margin: 10px 13px 10px 13px;
}
a {
color: #0069d6;
}
a:hover {
color: #0050a3;
text-decoration: none;
}
a img {
border: none;
}
p {
margin-bottom: 9px;
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: #404040;
line-height: 36px;
}
h1 {
margin-bottom: 18px;
font-size: 30px;
}
h2 {
font-size: 24px;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
font-size: 13px;
}
hr {
margin: 0 0 19px;
border: 0;
border-bottom: 1px solid #ccc;
}
blockquote {
padding: 13px 13px 21px 15px;
margin-bottom: 18px;
font-family:georgia,serif;
font-style: italic;
}
blockquote:before {
content:"\201C";
font-size:40px;
margin-left:-10px;
font-family:georgia,serif;
color:#eee;
}
blockquote p {
font-size: 14px;
font-weight: 300;
line-height: 18px;
margin-bottom: 0;
font-style: italic;
}
code, pre {
font-family: Monaco, Andale Mono, Courier New, monospace;
}
code {
background-color: #fee9cc;
color: rgba(0, 0, 0, 0.75);
padding: 1px 3px;
font-size: 12px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
pre {
display: block;
padding: 14px;
margin: 0 0 18px;
line-height: 16px;
font-size: 11px;
border: 1px solid #d9d9d9;
white-space: pre-wrap;
word-wrap: break-word;
}
pre code {
background-color: #fff;
color:#737373;
font-size: 11px;
padding: 0;
}
@media screen and (min-width: 768px) {
body {
width: 748px;
margin:10px auto;
}
}
</style>
<title>Everything I Know About SVG</title>SVG is arguably going to be the main image format of the modern web. I recently wrote an article for Safari Books Online called <a href="http://blog.safaribooksonline.com/2012/06/21/svg-icons-for-new-devices/">SVG Icons for New Devices</a> that covers some of the basics of dealing with SVG. This article tries to focus more on the pain points of using SVG in production and it turns out there are many. Read along to see what I’m talking about.
<h2>
Plain Old <code><img></code> Tags</h2>
Here’s how you link to your amazing vector image.<br />
<pre><code><img src="/images/logo.svg">
</code></pre>
This is pretty straightforward and should work very well for most of your cases. You can also specifcy <code>width</code> and <code>height</code> this way or in your CSS.<br />
<h2>
Vector is XML</h2>
SVG files are supposed to be human readable, but XML is terrible, so it’s been slow going for me. That being said I have noticed one strange thing about the content of SVG files, notably the fact that there are <code>width</code> and <code>height</code> attributes:<br />
<code><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="15px" height="15px" viewBox="0 0 15 15" enable-background="new 0 0 15 15" xml:space="preserve"></code><br />
Sometimes in Firefox when you scale up SVG images (browser-zoom) you get some weird bluriness. If you change the <code>width</code> and <code>height</code> from <code>px</code> to <code>em</code> this bluriness seems to myseriously dissappear. Because in this case it’s a sqaure <code>1em</code> and <code>1em</code> work great. I’m not sure if this is a bug in Firefox or if it’s even still a problem in the newer versions. Your mileage may very.<br />
<h2>
Basic Icons</h2>
It’s pretty easy to use SVG for icons and just make them background images. This is probably your standard use case for SVG anyway.<br />
<pre><code>.icon {
height: 16px;
width: 16px;
background-size: 16px 16px;
}
.icon-arrow {
background: url("/images/icon-arrow.svg") no-repeat;
}
<div class="icon icon-arrow"></div>
</code></pre>
<h2>
Relatively Easy EM Icons</h2>
EM’s are relative to <code>font-size</code>, so you can easily use them to size your SVG images relative to some text, which can be really helpful in a lot of cases. Here’s an exaple of putting them next to some headings.<br />
<pre><code>h1 {
font-size: 24px;
}
h2 {
font-size: 18px;
}
.icon-before {
background-size: 1em 1em;
padding-left: 1em;
}
.icon-arrow {
background: url("/images/icon-arrow.svg") no-repeat;
}
<h1 class="icon-before icon-arrow">Header 1 w/Icon</h1>
<h2 class="icon-before icon-arrow">Header 2 w/Icon</h2>
</code></pre>
<h2>
Fallbacks</h2>
If you care about IE8 or Android 2.x or other browsers that don’t support SVG images you can combine some fanciness like this with a <a href="http://modernizr.com/download/#-svg-shiv-cssclasses">simple custom build of modernizr</a> to easily fallback without too much ugliness.<br />
<pre><code>.icon {
height: 16px;
width: 16px;
background-size: 16px 16px;
background-repeat: no-repeat;
}
/* only show svg icon */
.svg .icon-arrow {
background-image: url("/images/icon.svg");
}
/* don't show anything but png */
.no-svg .icon-arrow {
background-image: url("/images/icon.png");
}
<div class="icon icon-arrow"></div>
</code></pre>
<b>Note:</b> With this approach we do have to wait until modernizr adds our <code>svg</code> or <code>no-svg</code> classes to the pages <code><body></code> before the images show up. Other fallback approaches can avoid the wait, but this guarantees the right thing will show up in the right browser.<br />
<b>Another Note:</b> Someone really needs to write a Sass plugin one day to create the PNG images directly from the SVG, so that we don’t have to bug the designer for it.<br />
<h2>
Data-URIs</h2>
Data URI’s are this amazing way to embed binary image content directly into your CSS files. This results in a reduce number of seperate downloads and can improve performance in some situations (hover states, etc).<br />
<pre><code>.icon {
height: 16px;
width: 16px;
background-size: 16px 16px;
background-repeat: no-repeat;
}
.icon-arrow {
background-image: url(data:image/svg+xml;base64,..);
}
<div class="icon icon-arrow"></div>
</code></pre>
Compass provides the <code>inline-data()</code> helper to assist with base64ing images and bringing them into your stylesheets. You can also drag and drop your SVG images into the <a href="http://xjamundx.github.com/SVG-is-Rad/">SVG is Rad</a> tool I wrote to get the proper CSS.<br />
<h3>
Weird Bugs</h3>
In some versions of WebKit (Chrome < 18 and Safari < 6) you cannot combine SVG data-URIs and <code>background-repeat: repeat-x</code>. The image turns into a blurry mess! Because of this bug in some cases I had to switch to referencing the SVG from a file and it was fine.<br />
<h2>
MIME Types</h2>
If you try to link to external SVG files for background images and don’t specify the correct mime type they just won’t show up. This happened to us after we switched from base-64 encoded SVGs to linking to files, because the so-called magic mime type detection script in our version of Apache noticed that our SVG files looked like XML and served them up as text/xml.<br />
You can add this to your .htaccess file if Apache is doing the same thing to you (hat tip: <a href="http://kaioa.com/node/45">this 5 year old article</a>):<br />
<pre><code>AddType image/svg+xml svg svgz
AddEncoding gzip svgz
</code></pre>
If you’re on NGINX check out <a href="http://stackoverflow.com/questions/3695409/nginx-offers-of-downoload-svg-instead-of-showing-it">this stackoverflow question about SVG MIME types and NGINX</a>.<br />
<h2>
Related Research</h2>
Other people are doing a lot of interesting things with SVGs at the moment. Here are some topics I don’t know quite enough about at the moment:<br />
<ol>
<li><a href="http://coding.smashingmagazine.com/2012/01/16/resolution-independence-with-svg/">Spriting with SVG Images</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/CSS/Getting_Started/SVG_and_CSS">Styling SVG with CSS</a></li>
<li><a href="http://simurai.com/post/20251013889/svg-stacks">SVG Stacks</a></li>
</ol>
Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com1tag:blogger.com,1999:blog-5538836060823517969.post-37120923925099158172012-05-03T21:24:00.001-07:002012-05-03T21:25:29.821-07:00Sass MixinsI was asked to make some buttons at work. I thought it might be a good idea to play with Sass mixins to generate the buttons. I got really excited about this and tried my hardest to generate the buttons from a single base color. It was fun and hard at the same time. It ended up being kind of crap.<br />
<br />
In <a href="http://j-query.blogspot.com/2012/02/sass-color-palette-generator.html">my earlier post about Sass color palettes</a> I was playing with keeping all my colors in a single file. this way we can keep track of them and easily change them in the future. We won't get lost in slightly difference shades of blue everywhere. It's a good idea.<br />
<br />
Not being an expert at color theory, I couldn't have told you how to get from one shade to another with anything other than<a href="http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html"> Sass's built-in lighten() and darken() functions</a>. That quickly got me a really terrible looking button, so I had try more complicated approaches.<br />
<br />
I had the original CSS styles to work with already, so I tried to make legitimate guesses as to how to get from say the top of the gradient color to the bottom of the gradient color. I did math around the rgb() and though about adding or subtracting various amounts of red, green, or blue. The gradients weren't too bad, but the borders were tricky, they weren't just darker, they were redder, but only for the orange button.<br />
<br />
So then I had the idea to inspect the hue, lightness, and saturation. I could get each colors hsl() codes, I could then figure out how different each part of the button was from the other colors, hopefully finding a way to pick a good base color. This proved a decent approach, and it seemed to work pretty much universally, well you know, it worked out okay for most of the darker colors, but lighter colors presented another problem.<br />
<br />
It turns out that the trick is dealing with gray, especially light
gray, because all of the sudden all of the supporting colors have to
change and where you were once lightening colors, you may have to darken them. A decent measure of grayness was to look at saturation. Once I established grayness I also had to look at lightness before deciding which color text to use.<br />
<br />
I spent maybe 1 to 3 hours on this and found it a fairly tedious process. It not something I'd wish on anyone else, but I did learn a bit. What do you think of the results?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1h02ZIR5m7K-WLqYqnKG9rBcAJIZJMoJv31X3V_ebYRGImkn2C2tBgxN9rUvOl1NGhnwbo6UQb9kEQqU8GYtJMwLlkaX4uu9TbCXi_noKyL7MaFDlTkQmbi5r9MAQ9D4vhQDCYO3r9vs/s1600/Screen+Shot+2012-05-03+at+9.51.15+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1h02ZIR5m7K-WLqYqnKG9rBcAJIZJMoJv31X3V_ebYRGImkn2C2tBgxN9rUvOl1NGhnwbo6UQb9kEQqU8GYtJMwLlkaX4uu9TbCXi_noKyL7MaFDlTkQmbi5r9MAQ9D4vhQDCYO3r9vs/s1600/Screen+Shot+2012-05-03+at+9.51.15+PM.png" /></a></div>
<script src="https://gist.github.com/2591857.js?file=Sass-buttons.scss"></script>
<b>Conclusion</b><br />
<br />
This experiment was worthwhile, but we ended up just sass-ifying the webkit-only CSS the designer gave us and hard-coding each button separately. Two different buttons might have different box-shadow lengths or border sizes, not to mention hover states. I'm still glad I learned about how mixins work and that this sort of thing was definitely doable. Like a lot of approaches to design (grids, etc) I think it really takes working with a designer that can see the value in the approach and work closely with you on it from the start. Anyone have suggestions on how it could been done better?Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com1tag:blogger.com,1999:blog-5538836060823517969.post-91830020773803040262012-04-06T12:02:00.000-07:002012-04-06T12:09:33.629-07:00JSConf 2012 Notes<br />
<div class="blob instapaper_body" id="readme">
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhte0QyWwM9VgIN4aL_1AOWGr8oVGBiMBD0c_tHBJ4sQZ9Lom-9ybn7RFB1P-g11DwcZCnOo_sXXC7waKyFlToOFOmlHq2gD84dOfj1mjXg1V4b6Ot7iwdlaCL2ML0Rgb4OW1PYg_W7z7U/s1600/IMG_1975.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhte0QyWwM9VgIN4aL_1AOWGr8oVGBiMBD0c_tHBJ4sQZ9Lom-9ybn7RFB1P-g11DwcZCnOo_sXXC7waKyFlToOFOmlHq2gD84dOfj1mjXg1V4b6Ot7iwdlaCL2ML0Rgb4OW1PYg_W7z7U/s400/IMG_1975.jpg" width="298" /></a></div>
<br />
<br />
<br />
<article class="markdown-body" itemprop="mainContentOfPage"><h1>
Introduction</h1>
JSConf has a pretty great reputation. Node.js, Phonegap and other
huge projects were announced in JSConf's past. There are legendary
parties and fantastic speakers. This year was no exception. As such, the
250 conference tickets sold out in less than 5 minutes. We dressed up like cowboys, <a href="http://substack.net/images/mechanical_bull.png">rode a mechanical bull</a> and even had shoot-outs. It was a pretty awesome adventure. <h2>
Themes</h2>
I try to look for themes in the talks that I saw to figure out what's
going on in the community right now. Other than the cowboy theme there were a couple of ideas I heard repeated more than once:<br />
<ol>
<li>Experimentation / play leads to great ideas.</li>
<li>Everyone is working really hard to make JS faster.</li>
<li>We need more women/diversity in JS.</li>
<li>Build tools / dependencies let us do cooler things.</li>
</ol>
<h2>
My Talk</h2>
I gave a quick talk about some things I think are important to the JS community. <br />
<ol>
<li>Open Source</li>
<li>Respecting Diversity</li>
<li>Helping New People</li>
<li>Startup/DIY Culture</li>
</ol>
You can see the slides here: <a href="https://docs.google.com/presentation/pub?id=1M_7q8Jp2t14I3N-iZggzU1rELfcuCNw4-L2SC6FLiuo&start=false&loop=false&delayms=3000">JavaScript as a Subculture</a></article><article class="markdown-body" itemprop="mainContentOfPage"><br />
While no one mentioned it directly, this popular tweet was in response to my talk:
<a href="https://twitter.com/#%21/felipc/status/186950981383237633">Talk
about JS culture: "we hated ie, we hated tables,.. What's the new
enemy? Someone answers: the threat of native" good answer @jsconf</a>. I'm famous!<br />
<h2>
Big Announcements</h2>
People look to JSConf for big announcements. There were a few of
those. It usually takes a few years though to see which ones actually
end up panning out.<br />
<ol>
<li>
<a href="http://bike.sh/">Project Bikeshed</a> client/server-side flash -> js converter by <a href="http://uxebu.com/">UXEBU</a>
</li>
<li>
<a href="https://github.com/cowboy/grunt/blob/master/docs/getting_started.md">Grunt</a> ant/rake for JS, by <a href="https://twitter.com/cowboy">Ben Alman</a>
</li>
<li>Everyone got <a href="http://www.mozilla.org/en-US/b2g/">Mozilla B2G</a> Phones for hacking. Sooo cool.</li>
<li>
<a href="http://no.de/">Joyent's node.js PaaS</a> is being phased out and moved to <a href="http://jit.su/">Nodejitsu</a>.</li>
<li>The Chrome team introduced <a href="http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/">Source Maps</a> for debugging minified code.</li>
</ol>
<h2>
Best Talks</h2>
I obviously couldn't see all of the talks, so I may have missed a few
things, but here were some of the top talks I saw at least in relation to front-end development, which is what I do at my day job. Many other amazing talks were also given about improving JS Performance in the browser, other programming languages and politics.<br />
<h3>
</h3>
<h3>
Jeff Archibald: AppCache is a Douchebag</h3>
<div>
<strong><br /></strong></div>
<div>
<strong>Summary:</strong> AppCache is not progressive enhancement, it completely changes how the browser handles your site.</div>
</article><article class="markdown-body" itemprop="mainContentOfPage"><br /></article><article class="markdown-body" itemprop="mainContentOfPage">
This talk laid out basically all of the flaws and awkward parts of
using HTML5 AppCache and how to use localStorage, iframes and other
hacks to get it to behave properly. He made a separation between "get
stuff" sites like blogs and wikipedia, and "do stuff" sites like drawing
apps and games. It was really entertaining, but ultimately a depressing
talk, because of how hard it is to get the AppCache to work properly :(
<br />
<ol>
<li><a href="http://lanyrd.com/2012/jsconf-us/sqxcz/">http://lanyrd.com/2012/jsconf-us/sqxcz/</a></li>
<li><a href="http://dl.dropbox.com/u/2501978/appcache-diagram.svg">http://dl.dropbox.com/u/2501978/appcache-diagram.svg</a></li>
</ol>
<h3>
Remy Sharp: Build Anything</h3>
<strong><article class="markdown-body" itemprop="mainContentOfPage"><strong><br /></strong></article>Summary:</strong> Use HTML5's JS APIs to progressively enhance your site.</article><article class="markdown-body" itemprop="mainContentOfPage"><br /></article><article class="markdown-body" itemprop="mainContentOfPage">
A good reminder of the latest crop of fairly well supported HTML5/JS
APIs. We're already using most of these at oDesk, but some that we are
not yet using include the HTML5 Drag'n'Drop API to handle file
uploading, HTML5 input types, HTML5 History/PushState API, and
EventSource (Server sent events).<br />
<ol>
<li><a href="http://lanyrd.com/2012/jsconf-us/sqxcb/">http://lanyrd.com/2012/jsconf-us/sqxcb/</a></li>
</ol>
<h3>
Paul Irish: Tools</h3>
<strong><article class="markdown-body" itemprop="mainContentOfPage"><strong><br /></strong></article>Summary:</strong> There are a lot of great tools out there to help web development. Embrace these dependencies.</article><article class="markdown-body" itemprop="mainContentOfPage"><br /></article><article class="markdown-body" itemprop="mainContentOfPage">
Paul Irish laid out every testing framework, css pre-processor, debug
tool, and CI Server you could use to maintain your site. He also talked
about source maps and some mobile web tools. He mentioned Jenkins and
Travis as decent CI Servers.<br />
<ol>
<li><a href="http://dl.dropbox.com/u/39519/talks/jsconf-tools/index.html">http://dl.dropbox.com/u/39519/talks/jsconf-tools/index.html</a></li>
</ol>
<h3>
Jacob Thornton: Brûlons les musées</h3>
<strong><article class="markdown-body" itemprop="mainContentOfPage"><strong><br /></strong></article>Summary:</strong> We can make things better by starting over,
but we need to have clear specs/tests to make sure our new thing still
works the same.</article><article class="markdown-body" itemprop="mainContentOfPage"><br />
The creator of Twitter Bootstrap and ender.js took us for a journey
through Dada-ism and french futurism, finally landing on a quote by a
famous futurist which translates to "burn the libraries". Jacob tried
doing that with ender.js and it ended up being kind of awesome, but
quickly people realized small differences with jQuery made it really
hard to use. He took a similar approach at re-writing mustache.js in a
project called Hogan, which ended up being really successful, because
mustache.js had unit tests that could be used to ensure it was perfectly
compatible. His approach ended up being significantly faster than the
original and eventually was accepted into the mustache.js core. Super
funny talk. Final quote "Burn all the libraries without tests".<br />
<ol>
<li><a href="http://lanyrd.com/2012/jsconf-us/sqxkg/">http://lanyrd.com/2012/jsconf-us/sqxkg/</a></li>
</ol>
<h2>
New Friends</h2>
<ol>
<li>I briefly talked to <a href="http://www.stubbornella.org/content/">Nicole Sullivan</a>, who was nice nice nice and seemed pleased as punch we were doing <a href="http://coding.smashingmagazine.com/2011/12/12/an-introduction-to-object-oriented-css-oocss/">OOCSS</a> at oDesk. She gave me some tips on grids and HTML5, which I'll share with my teams. </li>
<li>I spoke with <a href="http://www.twitter.com/substrack">@substack</a> who made <a href="http://www.browserling.com/">browserling</a>/<a href="http://www.testling.com/">testling</a> who said we could get it to work behind our VPN if we opened up an SSH Tunnel somehow.</li>
<li>I spoke with Devrim Yasar from <a href="http://www.koding.com/">Koding</a> about his in-browser IDE. It's very similar to <a href="http://www.c9.io/">Cloud9</a>,
but it's more friendly to non-js environments. I'd really love to give
something like this a try, but it will take some work to tweak our dev
infrastructure.</li>
<li>
<a href="https://twitter.com/cowboy">Ben Alman</a> from Bocoup showed me his command-line tool <a href="https://github.com/cowboy/grunt">Grunt</a>, which is similar to my odesklint tool. It has built-in jshint, minification, and can run <a href="http://docs.jquery.com/QUnit">qUnit</a> on the server-side with <a href="http://www.phantomjs.org/">PhantonJS</a>
out-of-the box (almost). Grunt has a plugin architecture and may be a
good starting point for future tools. I'm going to try and get the unit
testing thing working and integrated with a CI server. :)</li>
<li>I talked to the guys from <a href="http://www.twilio.com/">Twillio</a>,
who make a really easy to use API for building SMS sender/receivers.
Think user-verification and other use-cases. Fantastic stuff.</li>
<li>
<a href="http://allyoucanleet.com/">John David Dalton</a> had some ideas about how to speed up ES5 shims for things like <code>Array.forEach</code>. Really smart guy!</li>
<li>I had a two total fanboy moments when I got to meet <a href="http://rmurphey.com/">Rebecca Murphey</a> and <a href="http://www.twitter.com/rem">Remy Sharp</a>, but you know everyone has their favorites. I met a <a href="https://twitter.com/#%21/j_root">bunch</a> <a href="https://twitter.com/#%21/joeldart">of</a> <a href="https://twitter.com/#%21/thorsteinsson">other</a> <a href="https://github.com/davidguttman">cool</a> <a href="https://twitter.com/#%21/geddesign">people</a> <a href="https://github.com/nathanielksmith">too</a>. Yay for talking to people. It can be so hard!</li>
</ol>
</article></div>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com3tag:blogger.com,1999:blog-5538836060823517969.post-77717685266056608242012-03-20T12:54:00.002-07:002012-03-20T13:13:54.245-07:00Mobile Web Anti-PatternsI'm trying to capture some common mobile web anti-patterns, so we can learn from the mistakes of others. I found a couple of mobile-optimized sites that were well, not so optimized. Please add to the comments if you've seen some of these or other anti-patterns!<br />
<br />
<b>Beware the Landing Page</b><br />
<br />
<a href="http://www.flickr.com/photos/jamund/7000807643/" title="Untitled by Jamund Xcot Ferguson, on Flickr"><img alt="" height="640" src="http://farm8.staticflickr.com/7253/7000807643_28de1488d7_z.jpg" width="427" /></a><br />
<br />
Contrary to what ABC News had in mind, I was trying to find the website not the app. I may have even clicked on a link from Google News or something, but when I saw this I immediately thought, "nevermind".<br />
<br />
<b>Native-only</b><br />
<br />
<b></b><br />
<b></b><br />
<b><a href="http://www.flickr.com/photos/jamund/6854689254/" title="Untitled by Jamund Xcot Ferguson, on Flickr"><img alt="" height="640" src="http://farm7.staticflickr.com/6232/6854689254_afceee7270_z.jpg" width="427" /></a></b><br />
I clicked a link from my Twitter client to hear a song and what do I get? There's no way to actually listen to the song without downloading the app and presumably searching for it. I don't even know the name of the song, just the artist. Worthless. :(<br />
<br />
<b>Where Are You?</b><br />
<br />
<a href="http://www.flickr.com/photos/jamund/7000808959/" title="Untitled by Jamund Xcot Ferguson, on Flickr"><img alt="" height="640" src="http://farm7.staticflickr.com/6091/7000808959_415089f15b_z.jpg" width="427" /> </a><br />
<br />
Use your Google Maps app to look up the Post Office, because there website is too hard to use in a mobile situation. It's a mobile optimized site. It knows where I am. One of the biggest mobile web anti-patterns is asking for information you already have.<br />
<br />
<b>Too Small Text</b><br />
<b> <a href="http://www.flickr.com/photos/jamund/7000808593/" title="Untitled by Jamund Xcot Ferguson, on Flickr"><img alt="" height="320" src="http://farm8.staticflickr.com/7184/7000808593_6aed386265_n.jpg" width="213" /></a></b><br />
<br />
<b><a href="http://www.flickr.com/photos/jamund/6854689718/" title="Untitled by Jamund Xcot Ferguson, on Flickr"><img alt="" height="320" src="http://farm8.staticflickr.com/7180/6854689718_aa324cc78a_n.jpg" width="213" /></a></b><br />
<br />
There sandwiches are great, but if I can't read what they sell or even zoom-in, what is the point of having a website. Your site may be pretty and small, but it's not optimized for mobile if I'm not able to read anything. In this case the problem was using images instead of CSS3 and web fonts to create its look. Other sites don't let you zoom in at all.<br />
<br />
<br />
<b>Need Examples...</b><br />
<br />
<b>Phone Numbers as Text</b><br />
<b>Forgetting Map Links on Addresses</b><br />
<b>Serving Tablet's the Phone Site</b><br />
<b>What else? </b><br />
<b></b><b><br /></b>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com2tag:blogger.com,1999:blog-5538836060823517969.post-88105866893206859872012-03-12T15:52:00.000-07:002012-03-12T15:52:38.541-07:00On Tech Reading & LearningI recently posted <a href="http://j-query.blogspot.com/2012/03/short-tech-book-reviews.html">mini-book reviews of tech books</a>. This got me thinking about the worth of books in the digital era for learning new technologies.<br />
<br />
<span style="font-size: large;">Personal E-Book History</span><br /><br />
When the iPad came out I was working at <a href="http://www.mediarain.com/">Rain</a> and they were happy to buy me e-books to help me learn the latest and greatest technology, but I was slow to embrace the digital medium for reading, Mostly I really liked the way holding a book in-front of me felt. After I left Rain and wanted some new tech books and I noticed that <a href="http://www.ora.com/">O'Reilly</a> regularly offered significant discounts on their e-books. Similarly, the fantastic <a href="http://www.abookapart.com/">A Book Apart</a> series offers e-book versions of their mini-books for $8. It quickly became difficult to justify purchasing the paperback version of these books, so I started reading technical e-books pretty much exclusively.<br />
<br />
<span style="font-size: large;">Learning Styles</span><br />
<br />
A great thing about books, both the paper and e
varieties, is that they have fewer distractions than a lot of other
modern learning mediums. Think about Twitter. It encourages distraction.
You might see 5 interesting links since yesterday. You have no way to
read all of them thoroughly. The topics are wide ranging and the content
decent, but you also want to stay up with the latest posts. Each of those links may have comments and a sidebar full of interesting stuff. It's not a
focused experience. Books offer the ability to dive into something a
little deeper and stay focused on them a little longer.<br />
<span style="font-size: large;">Portability</span> <br />
<br />
Moving to e-books I found that I could read books wherever I went. I read most of <a href="http://www.abookapart.com/products/css3-for-web-designers">CSS3 for Web Designers</a> on my iPhone while my wife and I were shopping at Old Navy. Suddenly the structured learning experience that books provide was completely portable. <br />
<br />Lately, I've been doing a lot of reading at the gym as I run on the
treadmill. Normal tech books just don't work well in this environment, because they're either too big or the pages don't stay open. E-books on the other hand are perfect. Being able to read a book at the gym helps me stay focused and not think about the treadmill moving under my feet, so I read a lot and get fit while I'm doing it!<br />
<br />
<span style="font-size: large;">Negatives<span style="font-size: small;"> </span></span><br />
<br />
<span style="font-size: large;"><span style="font-size: small;">In this age of digital learning</span></span> are books still the ideal way to learn? I'll just lay out some of their obvious downsides.<br />
<br />
Technical books are inherently static. You can't interact with them physically. They don't update automatically. They're essentially out of date the day they're published.<br />
<br />
They're one sided. Web discussions and forums provide many viewpoints, because anyone can comment. A book may have one or two preset ways of looking at things, but it can hardly be expected to cover all of them. <br />
<br />
Books can be really long. They must think they're really important for technical books to venture into the 800 page range (actually the post-120 range is pretty much enough for me). I don't have a huge attention span. While books encourage focus, they can only hold that focus for so long. It's hard to wade through all of the content of the multi-hundred page book.<br /><br />
<br />
<span style="font-size: large;">Book I Like Best</span><br />
<br />
While I'm really good at looking up information on the Internet. I don't always know what I should be looking up. For all of their former glory books with titles like Bible, Definitive Guide and Cookbook are losing the battle for my attention span. In this I'll much more likely hit up the <a href="https://developer.mozilla.org/en/JavaScript">MDN</a> or <a href="http://stackoverflow.com/users/496735/jamund-ferguson">StackOverflow</a> than pull out of a paper-back guide when I'm in the middle of a project. Despite their waning appeal there are still books out there that I value greatly.<br />
<br /><span style="font-size: small;"></span><br />
Books that appeal to me are short.<br />
Books that appeal to me are specific.<br />
Books that appeal to me introduce me to new concepts.<br />
Books that appeal to me often use color to make them clearer.<br />
<br />
As I read books that meet these criteria I'm able to learn something new wherever I am and then when I get back to a computer somewhere I know what terms I want to use in my search for more about the topic. These books open my mind up to a new concept or idea. The practical knowledge of how to apply that idea to my specific situation is something I'd probably much rather find online where it will be up-to-date, littered with helpful comments, and checked out by the community at large. <br />
<br />
<br />
<br />Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com1tag:blogger.com,1999:blog-5538836060823517969.post-85660926607383377982012-03-09T20:58:00.000-08:002012-03-12T15:03:16.065-07:00Short Tech Book ReviewsInspired by this thread on Google+ <a href="https://plus.google.com/u/0/116910304844117268718/posts/c3A4wgSAgx4">https://plus.google.com/u/0/116910304844117268718/posts/c3A4wgSAgx4</a> I decided to put together a bunch of short book reviews of tech books I've read recently.<b><br /></b><br />
<br />
<b><a href="http://introducinghtml5.com/">Introducing HTML5</a></b><br />
Great overview of new tags and JS APIs w/some good examples and demos.<br />
<br />
<a href="http://html5forwebdesigners.com/"><b>HTML5 For Web Designers</b></a> <b><span style="color: red;">Loved it</span></b> <br />
Essential, short, colorful guide to HTML5.<br />
<br />
<a href="http://shop.oreilly.com/product/0636920016045.do"><b>Canvas Pocket Reference </b></a><br />
It's like the MDN for when you don't have a computer.<br />
<br />
<a href="http://www.amazon.com/Developing-Web-Standards-John-Allsopp/dp/0321646924"><b>Developing with Web Standards</b></a><b style="color: red;"></b><br />
A bit long, but still it has great chapters on accessibility, CSS, and web standards. <br />
<br />
<a href="http://shop.oreilly.com/product/9780596517748.do"><b>JavaScript: The Good Parts</b></a><b style="color: red;"> </b><b><span style="color: red;">Loved it</span></b><br />
Essential, concise, opinionated guide to JavaScript. Covers regexes, functions, objects, etc.<b> </b><br />
<br />
<a href="http://shop.oreilly.com/product/9780596806767.do"><b>JavaScript Patterns</b></a><b style="color: red;"> </b><b><span style="color: red;">Loved it</span></b> <br />
Every JS developer should read this book. It's a fantastic review of types, patterns and best practices. Personally, I prefer the <a href="http://npmjs.org/doc/coding-style.html">NPM Coding Standards</a> for my server side JS, but this book is still essential.<br />
<br />
<b><a href="http://www.abookapart.com/products/css3-for-web-designers">CSS3 For Web Designers</a> [e-book]</b><b style="color: red;"> </b><b><span style="color: red;">Loved it</span></b><br />
Read the entire thing on my iPhone. Great, short read. Extremely useful.<b> </b>Totally sold me on rgba().<br />
<br />
<a href="http://www.abookapart.com/products/mobile-first"><b>Mobile First</b></a><b> [e-book]</b><br />
Decent essay on why mobile is becoming more and more important. Best given to someone who's not already steeped in mobile.<br />
<br />
<b><a href="http://shop.oreilly.com/product/0636920013044.do">Supercharged JavaScript Graphics</a></b><b> [e-book]</b><br />
Stupid title, but plenty of interesting tidbits. Wished it were shorter and only covered Canvas.<br />
<br />
<a href="http://shop.oreilly.com/product/0636920018421.do"><b>JavaScript Web Applications</b></a><b> </b><b>[e-book] </b><b><span style="color: red;">Loved it</span></b><br />
The best book on web app fundamentals (w/backbone.js, spine, etc.) Covers MVC, HTML5 drag & drop and other APIs. Read it mostly at the gym and on airplanes. <br />
<br />
<b><a href="http://shop.oreilly.com/product/0636920022466.do">Big Data Glossary </a>[e-book]</b><br />
Would be more useful as a PDF or even better a wiki.<br />
<b> </b><br />
<b> </b><br />
<b><a href="http://jsninja.com/">Secrets of the JavaScript Ninja</a> [e-book]</b><br />
Lots of crazy stuff about prototypes, testing, etc. Been in progress since like 2009 and it's not finished, so we get updates every couple of weeks.<br />
<br />
<b><a href="http://www.manning.com/ableson2/">Android in Action </a>[e-book]</b><br />
Too long<b>.</b><br />
<br />
<b><a href="http://hardboiledwebdesign.com/">Hardboiled Web Design [</a>e-book] </b><b><span style="color: red;">Loved it</span></b><br />
I wish I would have paid to get a hard copy of this, because it's beautiful. Great essay on why we should build cool websites <i>now</i> with some killer examples.<br />
<a href="http://www.blogger.com/goog_921624824"><br /></a><br />
<b> </b><br />
<a href="http://www.helloandroid.com/"><b>Hello Android</b></a><br />
Perfect for Android beginners who want the basics.<b> </b><br />
<br />
<a href="http://shop.oreilly.com/product/0636920001096.do"><b>MongoDB: The Definitive Guide</b></a><b> [e-book] </b><b style="color: red;"></b><br />
Awesome<b> </b>guide for anyone doing anything with Mongo<b>. </b><b></b><br />
<br />
<b><a href="http://shop.oreilly.com/product/0636920001133.do">Tapworthy</a> [e-book] </b><b><span style="color: red;">Loved it</span></b><b style="color: red;"></b><br />
Essential mobile UX & design. <b> </b><br />
<br />
<b><a href="http://recipeswithbackbone.com/">Recipes with Backbone</a> [e-book]</b><br />
A ton of great examples about design patterns and testing backbone apps.<b> </b><br />
<br />
<a href="http://shop.oreilly.com/product/0636920019893.do"><b>50 Tips & Tricks for MongoDB Developers</b></a><b> [e-book] </b><b><span style="color: red;">Loved it</span></b><br />
Essential MongoDB tips for n00bs<b> </b>like me.<b> </b><br />
<b> </b> <br />
<a href="http://shop.oreilly.com/product/0636920010265.do"><b>Learning the iOS 4 SDK for JavaScript Programmers</b></a> <b>[e-book]</b><br />
Basically worthless if you've read a beginner tutorial on using XCode. Used it once or twice as a reference for splitting strings into arrays.<br />
<br />
<b><a href="http://shop.oreilly.com/product/0636920020806.do">Getting Started with GEO, CouchDB and Node.js</a> </b><b>[e-book]</b><br />
I now know a little bit about GeoJSON and CouchDB. Kind of weird. Kind of cool. Don't know.<br />
<br />
<b><a href="http://shop.oreilly.com/product/9780596802806.do">High Performance JavaScript</a> [e-book]</b><br />
<span style="font-family: inherit;"></span>Informative. Don't forget to strike a balance between performance, readability, and maintainability.<br />
<div style="font-family: inherit;">
<br /></div>
<b><a href="http://www.abookapart.com/products/responsive-web-design">Responsive Web Design</a> [e-book]</b><span style="font-family: inherit;"> </span><br />
<span style="font-family: inherit;">Pretty nearly essential book about how the web is changing and how to react to it.</span>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com0tag:blogger.com,1999:blog-5538836060823517969.post-59445266379694199002012-03-09T20:06:00.000-08:002012-03-09T20:06:02.881-08:00Mobile Web RoundupThis week was a huge week week for mobile web development. Here are some quick summaries of the latest tools/announcements.<br />
<br />
<a href="http://labs.adobe.com/technologies/shadow/">Adobe Shadow</a> <br />
<br />
Live preview your sites in your desktop and mobile.<br />
<br />
<a href="http://googledevelopers.blogspot.com/2012/03/introducing-page-speed-mobile-analysis.html">Google Page Speed for Mobile</a> (requires ICS)<br />
<br />
If you're lucky enough to have Android for mobile you can now find out bottlenecks in your sites. It's hard to debug for mobile, so any help like this is welcome.<br />
<br />
<a href="http://www.sencha.com/products/touch/features/">Sencha Touch 2.0</a><br />
<br />
The best mobile-web framework just got way faster in Android and added a bunch of new features.<br />
<h2 style="font-weight: normal;">
<a href="http://www.mobilexweb.com/blog/ios-5-1-new-ipad-web-developers"><span style="font-size: small;">What iOS 5.1 and the new iPad mean for web developers</span></a></h2>
<h2 style="font-weight: normal;">
<span style="font-size: small;">Of course the iPad 3 was announced this week and <a href="http://www.twitter.com/firt">@firt</a> reminds of how to get our sites ready with a quick blog post.</span></h2>
<h1 style="font-weight: normal;">
<a href="http://bradfrostweb.com/blog/notes/ipad3s-retina-display-web/"><span style="font-size: small;">iPad3's Retina Display Will Wreak Havoc on the Web</span></a></h1>
<h1 style="font-weight: normal;">
<span style="font-size: small;">A follow up to the above article with some warnings about serving up high-res images in every context.</span></h1>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com1tag:blogger.com,1999:blog-5538836060823517969.post-90046201929843897332012-03-02T08:47:00.002-08:002012-03-02T08:58:36.951-08:00Taking a Leap into Cloud9 IDE<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
After a lot of high profile coolness from the C<a href="http://c9.io/">loud9</a> team, such as their work on <a href="http://zef.me/4030/avoiding-javascript-pitfalls-through-tree-hugging">Treehugger</a> I thought it might be cool to check out the <a href="http://c9.io/">Cloud9 IDE.</a> From what I understand it is an open source project with a paid version that offers additional features. Because it's written in node.js and JavaScript it's the first IDE I know of that really takes JavaScript seriously.</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
Some of things that I hoped to find in the IDE were:</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<ul>
<li>JavaScript code completion</li>
<li>easy collaboration / code reviewing</li>
<li>instant previewing</li>
<li>use on any machine</li>
<li>git integration</li>
<li>built-in JSHint support via Treehugger</li>
</ul>
<div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
I went to the site <a href="http://c9.io/">c9.io</a> and immediately signed in with <a href="http://www.github.com/xjamundx">my github account</a>. It was smart enough to show me all of the projects I had on github. </div>
</div>
<div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
</div>
<div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span id="goog_2103962215"></span><span id="goog_2103962216"></span></div>
</div>
<div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
</div>
<div>
<div class="separator" style="clear: both; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsPbgKsZyXWV3pgQgeKyQBaFb4n9MozHSEXhhdiY086OTz_I7T8ZurF6VyVufJcy4uJB8pGFcrU_8-8fL_nFVScdHbtbdSr0FHObmy-C0t8i83vcmKbdX_dpZqbhuKuyl9NDzvMqoo3lo/s1600/Screen+Shot+2012-03-02+at+9.14.07+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsPbgKsZyXWV3pgQgeKyQBaFb4n9MozHSEXhhdiY086OTz_I7T8ZurF6VyVufJcy4uJB8pGFcrU_8-8fL_nFVScdHbtbdSr0FHObmy-C0t8i83vcmKbdX_dpZqbhuKuyl9NDzvMqoo3lo/s400/Screen+Shot+2012-03-02+at+9.14.07+AM.png" width="400" /></a></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
Initially, I was hoping to use this for my day job at <a href="http://www.odesk.com/">oDesk</a>, but I quickly ran into a hurdle: all of our files our stored on servers behind a VPN. I reached out to some people from the Cloud9 team and found out that at the moment I'm not able to use this service to do work that's behind a private VPN. I think it's also the case that you cannot do work locally either.</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
It's a good thing I have all of those github projects. One that I'm particularly excited about working on is a Words with Friends inspired word game called <a href="https://github.com/xjamundx/words-vs-zombies">Words vs. Zombies.</a></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
I opened up the project and it seemed to clone the existing repo into one of the Cloud9 servers somewhere. It gave me a sort of shell as well as the files in the repo. From there I could preview my files, edit them, get good code feedback and more.</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div class="separator" style="clear: both; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJLtpfG-8tzcP-f-aKfeBMO7JH0QXOlLbOQTEir9G-36pfDnW2rUhoZwAqdBc-ZVGEumQYC-HHSIXekSFAw5EcGHJmAFzj-RZ5Us_-yPB909tWXmuncGx7QUe8SHCyKcPmI0zVrTcWpMo/s1600/Screen+Shot+2012-03-02+at+9.24.21+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="265" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJLtpfG-8tzcP-f-aKfeBMO7JH0QXOlLbOQTEir9G-36pfDnW2rUhoZwAqdBc-ZVGEumQYC-HHSIXekSFAw5EcGHJmAFzj-RZ5Us_-yPB909tWXmuncGx7QUe8SHCyKcPmI0zVrTcWpMo/s400/Screen+Shot+2012-03-02+at+9.24.21+AM.png" width="400" /></a></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b>Sharing</b></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b><br /></b></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
At this point I was pretty much blown away. I'd heard about the collaboration features, so my first instinct was to look for the "invite a friend" option. I couldn't find it anywhere! I just wanted to show my friends how cool the experience was and hopefully get them to help me finish this project. There was no easy way to do it.</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b>Doing Work</b></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b><br /></b></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
Okay the code editor is pretty much amazing. It doesn't seem to have that good of code-completion, but it does try to be helpful, though it's not super smart about typing or anything like that. I've heard that <a href="http://www.jetbrains.com/webstorm/">Web Storm</a> offers decent JS code completion, so maybe I can check that out in the future. Despite the imperfect completion the error checking stuff is amazing. It's completely configurable and basically gives you built-in JSHint while you're coding. It's incredible and the speed and ease of typing in this web IDE is fantastic. The syntax highlighting, customizable key-bindings, and other features really make this a great IDE.</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b>Updating the Project</b></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b><br /></b></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
So after playing around with the current files, I decided I wanted to take my basic HTML/JavaScript project and turn it into a full-fledged Node.js project. First thing I tried to do was move my files around. I created some sub-directories and tried dragging some files into them. This didn't work. I tried moving them with the command line. That didn't work either. I tried in Safari and the dragging worked, but the files didn't appear to actually be moved. It was a mess. Giving up I thought I'd try and use <a href="http://expressjs.com/">express</a> to create my files for me. I tried things like this:</div>
<span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;"></span><br />
<pre style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-size: 13px; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 19px; margin-bottom: 0px !important; margin-left: 0px; margin-right: 0px; margin-top: 15px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"><span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;"><code style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: none; border-color: initial; border-color: initial; border-left-style: none; border-right-style: none; border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: none; border-width: initial; border-width: initial; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 1.4em; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre;">npm install express
(some response)
./node_modules/express/bin/express --help
./node_modules/express/bin/express . </code></span></pre>
<span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;">
</span><br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
I basically got no response from any command I tried to run other than the initial npm install. So, I gave up, went back to <a href="http://www.panic.com/coda">Coda</a> made the changes and pushed them to github. I went back into my project and could not figure out how to pull the latest change. I tried re-loading the page, pressing the "refresh" button on the project, ran git pull, but nothing worked. git seemed mad because I had manually created files it wanted to create, but trying to remove files in the command line to prevent a conflict didn't actually seem to work.</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b><br /></b></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b>Conclusion</b></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br />
I can't tell you how frustrating it was to not be able to do work in the IDE. The potential is great, the idea of it is amazing, but if I can't do work it's worthless to me. Despite that frustration I really see the benefit of the Cloud9 IDE and I can't wait until this thing is ready to use. I really think it will transform the way we do collaborative work. The server and preview features are amazing. I can run my app on their servers. That's huge. For a node.js app or even an html/javascript project there is no editor I've seen that can compete. They just need to work out a few bugs first.<br />
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
Here's a more specific <a href="https://gist.github.com/1959288">bug report</a> I compiled if you're interested in what browser I was using when and what errors I received.</div>
<div>
<br /></div>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com2tag:blogger.com,1999:blog-5538836060823517969.post-35831765692688712322012-02-28T15:30:00.000-08:002012-02-28T16:38:05.643-08:00Sass Color Palette Generator<br />
<div class="separator" style="clear: both; text-align: left;">
Inspired by EngineYard's <a href="http://www.engineyard.com/blog/2011/front-end-maintainability-with-sass-and-style-guides/">Front-End Maintainability with Sass and Style Guides</a> article about how helpful it is to keep track of your Sass color variables in one place I sought to automate the process a little bit. There are a bunch of ways that this could be done, but I took a really simple client-side approach. Just make sure your _scss file is publicly accessible and this code should generate an always-up-to-date style guide from your Sass (scss) color file. What do you think?</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg17PabFyclOoS41JaGKReOaPMbAg0PmDhHwA58mI_i7cU91-HTaoxNrZyUm5FA-aCYcHnnQjFL-nemt8KsXstOmM0Xv2SJD7HStHdYhHMoeLjOeO52zbf7m4BdprRECSteKoU1vlkVXXc/s1600/Screen+Shot+2012-02-28+at+4.22.34+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="308" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg17PabFyclOoS41JaGKReOaPMbAg0PmDhHwA58mI_i7cU91-HTaoxNrZyUm5FA-aCYcHnnQjFL-nemt8KsXstOmM0Xv2SJD7HStHdYhHMoeLjOeO52zbf7m4BdprRECSteKoU1vlkVXXc/s320/Screen+Shot+2012-02-28+at+4.22.34+PM.png" width="320" /></a></div>
<div>
<br /></div>
<br />
<script src="https://gist.github.com/1936029.js">
</script>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com0tag:blogger.com,1999:blog-5538836060823517969.post-48982271642125530652012-02-25T21:23:00.000-08:002012-02-25T21:25:54.268-08:00Express.js Dynamic Helpers vs. Middleware<br />
<div style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 1.4em; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: 0px !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
If you're build a site in express.js that relies on variables being set on every single page you may be tempted to use middleware and for a long time I've done something like this. It works fine.</div>
<span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;"></span><br />
<pre style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-size: 13px; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 19px; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: 15px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"><span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;"><code style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: none; border-color: initial; border-color: initial; border-left-style: none; border-right-style: none; border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: none; border-width: initial; border-width: initial; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 1.4em; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre;">// app.js file
var middleware = require('./util/middleware')
app.use(middleware.setLocals)
// middleware.js file
exports.setLocals(req, res, next) {
res.local("currentPageName", applyFancyFormatting(req.url))
res.local("luckyNumber", Math.floor(Math.random() * 100))
next()
}
</code></span></pre>
<span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;">
</span><br />
<div style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 1.4em; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Express has something called dynamicHelpers that were built specifically with this in mind. It's a little bit simpler and in my opinion cleaner. It also doesn't make you next() after each one. You can put this anywhere you'd put an app.use statement. Check it out:</div>
<span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;"></span><br />
<pre style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-size: 13px; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 19px; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: 15px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"><span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;"><code style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: none; border-color: initial; border-color: initial; border-left-style: none; border-right-style: none; border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: none; border-width: initial; border-width: initial; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 1.4em; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre;">app.dynamicHelpers({
currentPageName: function(req, res) {
return applyFancyFormatting(req.url)
},
luckyNumber: function() {
return Math.floor(Math.random() * 100))
}
})
</code></span></pre>
<span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;">
</span><br />
<div style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 1.4em; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Or you can be like me and store them in different files like this:<br />
<span class="Apple-style-span" style="line-height: 22px;"></span><br />
<pre style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-size: 13px; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 19px; margin-bottom: 0px !important; margin-left: 0px; margin-right: 0px; margin-top: 15px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"><code style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: none; border-color: initial; border-color: initial; border-left-style: none; border-right-style: none; border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: none; border-width: initial; border-width: initial; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 1.4em; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre;">// app.js
var dynamicHelpers = require('./util/dynamicHelpers')
app.dynamicHelpers(dynamicHelpers)
// utils/dynamicHelpers.js file
exports.currentPageName = function(req, res) {
return applyFancyFormatting(req.url);
}
exports.luckyNumber = function() {
return Math.floor(Math.random() * 100);
}</code></pre>
<br />
<br />
See it's great isn't it? What do you think?</div>
<span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;">
</span>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com5tag:blogger.com,1999:blog-5538836060823517969.post-36520257832939621472012-01-26T12:57:00.001-08:002012-01-26T12:57:05.972-08:00HTML5 Canvas Performance Tips<br />
Here are some things we learned when building the i.TV grid with HTML5 Canvas, which was a fun experiment, but probably a terrible idea. You can decide if it was a good idea here: <a href="http://i.tv/guide">http://i.tv/guide</a>.<br />
<br />
1. Close your paths! (Avoid memory leaks)<br />
2. Don't draw when you don't need to! (Improve FPS)<br />
3. Profile! Profile! Profile!<br />
4. Keep your canvas as small as possible. (Decrease memory use and improve FPS)<br />
5. Check out requestAnimationFrame for animations. (Optimize FPS)<br />
6. Mobile browsers don't do well with canvas animations (5 FPS FTW!)<br />
7. Native scrolling > JS Scrolling!<br />
8. DOM = slow, Canvas may be slower.<br />
<br />
Here are some other techniques I've heard an help as well:<br />
9. Typed arrays <a href="http://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/">http://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/</a><br />
10. Consider variable re-use to prevent garbage collection. (Smoother animation)<br />
<br />
Here are some other general tips for dealing with Canvas:<br />
11. Make sure to handle touch events for mobile<br />
12. Test your offset code in multiple browsers as it can get a little hairy<br />
<div>
<br /></div>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com1tag:blogger.com,1999:blog-5538836060823517969.post-81483414535538086272012-01-20T21:16:00.000-08:002012-01-20T21:20:39.941-08:00LESS is awesomeLately we started using <a href="http://lesscss.org/">LESS</a> for some of our web apps at <a href="http://i.TV/">i.TV</a>. I just wanted to iterate some of the benefits and a few of the downsides. Initially we were using <a href="https://github.com/MartinodF/connect-less">connect-less</a> to compile our less dynamically when the app is started. We still use this for our development environment but now we use <a href="https://github.com/cloudhead/less.js/blob/master/bin/lessc">lessc</a> in our build scripts to compile it before hand. It's a little more stable that way.<br />
<br />
<b>AWESOME BITS</b><br />
<br />
<ol>
<li>We can use constants for z-indexes to prevent the endless desire of devs to make them higher forever. <span class="Apple-style-span" style="background-color: #999999;"><span class="Apple-style-span" style="color: lime;">z-index: @z-modal</span></span> > <span class="Apple-style-span" style="background-color: #999999; color: lime;">z-index: 999999999</span></li>
<li>Separate .less files for each UI widget makes for awesome code organization. <b>This is HUGE!</b></li>
<li>Makes CSS3 browser prefixes a little more manageable.</li>
<li>Using variables for color names makes it a lot easier to make sure you're on the same page with a designer.</li>
<li>CSS code re-use FTW!</li>
<li>Mixins are okay.</li>
</ol>
<div>
<b>NOT-SO AWESOME BITS</b></div>
<div>
<ol>
<li>Weird issue with using the <span class="Apple-style-span" style="background-color: #999999; color: lime;">@arguments</span> variable where there are commas. (See: <a href="https://github.com/cloudhead/less.js/issues/301">https://github.com/cloudhead/less.js/issues/301</a>)</li>
<li>connect-less initially had some trouble including files with includes inside them, but I think it's fixed now.</li>
<li>It would be really nice to be able to just include a full directory instead of manually including all x number of files (within the widgets directory for example) manually.</li>
<li>LESS doesn't encouraged any specific structure or good patterns, which is fine, but you sometimes get really excited about using the nesting feature, because it seems so convenient and clean, but it can actually can make your code less re-usable if you don't do it right.</li>
</ol>
</div>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com2tag:blogger.com,1999:blog-5538836060823517969.post-73121942374252284332012-01-20T20:38:00.000-08:002012-01-20T20:38:01.374-08:00NodeUp 11 Commentary<br />
<div style="font-family: Helvetica;">
Yesterday, I listened to the nodeup podcast (<a href="http://nodeup.com/">http://nodeup.com/eleven</a>) about databases and node.js. It was pretty epic. I wanted to write up my thoughts and summarize it as best as I could. There are some great things going on in the node.js and database worlds and I have my opinions about them too. Here we go...</div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
1) Postgres</div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
People like Postgres if they need relational database. This is because the community is really cool and they add lots of features (every feature!) There's even a JSON-store feature Postgres is working on. Yammer is the only known (big) company to be using Postgres with node in production, but the node-postgres library is supposedly pretty decent.</div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
2) Database people hate MongoDB, developers love it</div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
Supposedly, 10Gen is the hermit of the database world. They don't hang out with other database people, but what they do is listen to customers/end-users more than database engineers, so it's very developer friendly, but database people think it's technically inferior (??). People say "Mongo loses data", presumably because journaling used to be off by default, but Mongoose has become the de-factor ORM of simple node websites and MongoDB with node is obviously really popular. For the record <b>I LOVE MONGODB.</b></div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
3) Redis</div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
Redis has a lot in common with node. It's single threaded and fast, but it doesn't scale horizontally very well. It's also really really fast and has really good node support. There actually aren't a lot of good use cases for Redis, because it doesn't scale up super well. Because it's in memory it's going to be faster than most other databases. DShaw wrote a redistore for socket.io, but it's unclear how well it can scale, pretty darn well though, apparently. It does have a pubsub feaure that can be subscribed to with a regex (?), which people think is really cool.</div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
4) CouchDB</div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
So the node community has been huge on CouchDB for a long time. Isaac S. uses it for NPM and a lot of people use it for other things. <b>Pros:</b> HTTP Interface super easy to use, map reduces are incremental and therefore super fast. <b>Cons:</b> It scales terribly. Imagine 1,000,000 users each with their own DB doing master->master replication. It will blow up. A ton of data or a huge number of users don't make a good fit for couchDB. That being said, if you have less users and less data (say in the thousands of things) and want to experiment with master->master replication for example from a mobile app to the web it could be really cool. Again, it can actually be really slow.</div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
5) Riak</div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
Riak does map reduces really really slowly. They were joking that they should be called "work orders" instead of jobs. Someone, maybe it was Voxer? is using them heavily in production and converted their super slow couchDB instance to Riak and they found that it worked way better. Riak is a huge key/value store than is highly available and easy to scale horizontally. It's pretty simple and doesn't have a lot of fancy features, but it works well if you're trying to keep it simple. It has an HTTP interface (like couchDB). If you're trying to be fancy and do a lot of complex map reduces your life will suck and it would be faster to use carrier pigeons.</div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
6) The future</div>
<div style="font-family: Helvetica;">
<br /></div>
<div style="font-family: Helvetica;">
A lot of people are excited about building databases in Javascript. Isaac S. noted some technical limitations of JavaScript, but everyone else was arguing that there are limitations in any langauge and the ones JavaScript has (not good with large numbers, etc), may prove solvable or less important. It seems the idea is that building databases isn't that hard and people (with some helpful tools) will likely continue to build custom databases for their own needs. <b>Awesome.</b></div>
<div>
<br /></div>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com2tag:blogger.com,1999:blog-5538836060823517969.post-85800982410362664762012-01-19T15:51:00.000-08:002012-01-19T15:51:21.622-08:00Mongo Map Reduce Vs. QueryJust had something come up today where I had to take a bunch of users from an existing collection and put some of their data in a new collection with new IDs. Here are two approaches that do the same thing, with wildly different speeds.<br />
<b><br /></b><br />
<b>Map reduce is way faster than looping over a really fast query for this kind of work!</b><br />
<br />
<script src="https://gist.github.com/1643807.js?file=mongo-map-reduce-speed-test.js">
</script>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com0tag:blogger.com,1999:blog-5538836060823517969.post-25330600919893526432012-01-17T20:45:00.000-08:002012-01-17T20:45:47.521-08:00File upload with express.js<br />
Uploading files with express.js is insanely simple!<br />
<br />
FYI, there's something special called req.files when you upload multipart forms. In this case I upload the file with a form field with the name of "file".<br />
<br />
<script src="https://gist.github.com/1631025.js">
</script><br />
<br />
I found this stuff out here:<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<a href="http://www.senchalabs.org/connect/middleware-bodyParser.html">http://www.senchalabs.org/connect/middleware-bodyParser.html</a></div>
<a href="https://groups.google.com/forum/#!topic/express-js/vDmtdWMx5tY">https://groups.google.com/forum/#!topic/express-js/vDmtdWMx5tY</a><br />
<br />
<br />Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com2tag:blogger.com,1999:blog-5538836060823517969.post-52175962930099991632012-01-16T19:53:00.001-08:002012-01-17T08:15:47.863-08:00Installing node.js on Linode (Ubuntu 10.04)Never really used Ubuntu for a server before.<br />
<br />
Here's I was able to get it setup to run node.js and mongoDB.<br />
<br />
<b>Node:</b><br />
<br />
<div class="p1">
</div>
<pre style="background: black; color: green;"><div class="p1">
<span class="Apple-tab-span"> </span>apt-get update</div>
<div class="p1">
<span class="Apple-tab-span"> </span>apt-get install build-essential</div>
<div class="p1">
<span class="Apple-tab-span"> </span>apt-get install openssl</div>
<div class="p2">
<span class="Apple-tab-span"> </span>apt-get install libssl-dev </div>
<div class="p1">
<span class="Apple-tab-span"> </span>cd /root</div>
<div class="p1">
<span class="Apple-tab-span"> </span>mkdir builds</div>
<div class="p1">
<span class="Apple-tab-span"> </span>cd builds</div>
<div class="p1">
<span class="Apple-tab-span"> </span>wget http://nodejs.org/dist/v0.6.7/node-v0.6.7.tar.gz</div>
<div class="p1">
<span class="Apple-tab-span"> </span>tar xzvf node-v0.6.7.tar.gz</div>
<div class="p1">
<span class="s1"><span class="Apple-tab-span"> </span>cd </span>node-v0.6.7</div>
<div class="p1">
<span class="Apple-tab-span"> </span>./configure</div>
<div class="p1">
<span class="Apple-tab-span"> </span>make</div>
<div class="p1">
<span class="Apple-tab-span"> </span>make install</div>
<div class="p1">
</div>
</pre>
<br />
<b>Mongo:</b><br />
<br />
Follow these instructions:<br />
<a href="http://www.mkyong.com/mongodb/how-to-install-mongodb-on-ubuntu/">http://www.mkyong.com/mongodb/how-to-install-mongodb-on-ubuntu/</a><br />
<span class="Apple-style-span" style="color: #38761d;"><br /></span><br />
<br />
<pre style="background-attachment: initial; background-clip: initial; background-color: black; background-image: initial; background-origin: initial; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><div class="p1">
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: #38761d;"><span class="Apple-style-span" style="font-size: 12px; line-height: 15px;">cat 'deb http:<span style="font-weight: bold;">//</span>downloads-distro.mongodb.org<span style="font-weight: bold;">/</span>repo<span style="font-weight: bold;">/</span>ubuntu-upstart dist 10gen' >> </span><span class="Apple-style-span" style="font-size: 12px; line-height: 15px;"><span style="font-weight: bold;">/</span>etc<span style="font-weight: bold;">/</span>apt<span style="font-weight: bold;">/</span>sources.list</span></span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: #38761d;"><span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 14px; line-height: 20px; white-space: normal;"></span></span></div>
<pre class="bash" style="clear: none; font-family: monospace; font-size: 12px; line-height: 1.333; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: visible; overflow-y: visible; white-space: pre; width: auto;"><span class="Apple-style-span" style="color: #38761d;"><span style="font-weight: bold;">sudo</span> <span style="font-weight: bold;">apt-get</span> update</span></pre>
<span class="Apple-style-span" style="color: #38761d;"><span class="Apple-style-span" style="font-size: 12px; line-height: 15px;"><span style="font-weight: bold;">sudo</span> <span style="font-weight: bold;">apt-key</span> adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10</span></span><br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: #38761d;"><span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 14px; line-height: 20px; white-space: normal;"></span></span></div>
<pre class="bash" style="clear: none; font-family: monospace; font-size: 12px; line-height: 1.333; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: visible; overflow-y: visible; white-space: pre; width: auto;"><span class="Apple-style-span" style="color: #38761d;"><span style="font-weight: bold;">sudo</span> <span style="font-weight: bold;">apt-get</span> <span style="font-weight: bold;">install</span> mongodb-10gen</span></pre>
</div>
</pre>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com0tag:blogger.com,1999:blog-5538836060823517969.post-49439912386434702292011-11-12T13:08:00.001-08:002011-11-12T13:34:42.299-08:00Strategies for Scaling Real-Time Web Apps<span class="Apple-style-span" style="font-size: large;">Using Polling</span><br />
Don't setup any cacheing and run a normal server. Poll for whatever data you need at any time, by as many users as possible. This puts load on your app and your database server. You can probably do a couple of thousand updates per minute before you get into trouble with a standard node.js / mongodb setup. On our Rackspace 1gb (of ram) cloud servers, we can handle a max of around 2000/second per box. Even a hefty Mongo setup won't be able to sustain that kind of load very long and the DB connections will start piling up and your servers will melt after a few minutes. With our setup of regular short polling every second (which is crazy) you'll want to scale things up around 200 simultaneous users.<br />
<span class="Apple-style-span" style="font-size: large;"><br /></span><br />
<span class="Apple-style-span" style="font-size: large;">Using Polling and Varnish </span><br />
The strategy here is simply to setup polling between set periods of time. So you can say get me everything that happened between this 10 second chunk and that 10 second chunk. Setup varnish to cache those calls , so everyone gets the same stuff. This puts the main responsibility for handling the load on the cacheing layer, which are usually built to handle a lot of load. Your database will only have to do one update every 10 seconds. It can handle that really easily. If there is some initial data you need to load to start your app that may get out of date, you can cache that bigger query for like 1 minute and again your database will be happy as pie. You can use Varnish here as a load balancer as well, but you probably will have to scale the load balancer before you'll need to scale up your app servers. A hefty Varnish box (say 8gb of ram) should be able to handle thousands of hits per second of cached data.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Using Socket.IO for polling</span><br />
<span class="Apple-style-span" style="font-size: large;"></span><br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span">If your app server can safely handle ~2000 users per second, but your database cannot, you may want to try a setup like this. Let your app server "cache" the data from your database and push updates using web-sockets/long polling with socket.io. When the user initially connects to your page you can send down the cached data (or even a buffer of say the last 1000 messages). As the database gets updated using normal POSTs and things the frequent polling of the database for updates can happen at the app-server (say one 5-second poll per app server), which will then send updates to all the users they know about. Obviously, this depends on how much data your pushing through (and how much ram you have), but with small amounts of stuff you can do the caching at the app level instead of on a special cache server, sparing both a new server and your database. Theoretically this setup would scale horizontally as well with multiple app servers, either through a load-balancer or some other round-robining approach. </span></div>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com0tag:blogger.com,1999:blog-5538836060823517969.post-85875922020978592212011-11-01T21:27:00.000-07:002011-11-17T21:18:36.137-08:00Notes on a failed launch with node.js and mongodb<b><span class="Apple-style-span" style="font-weight: normal;"><b><span class="Apple-style-span" style="font-size: large; font-weight: normal;"><b>Intro</b></span></b></span></b><br />
<br />
What follows is a play-by-play of <a href="http://i.tv/">i.TV'</a>s launch of <a href="http://www.ew.com/viewer">viWEer</a> with <a href="http://www.ew.com/">Entertainment Weekly</a>. We were using all of the latest technologies: <a href="http://www.nodejs.org/">node.js</a>, <a href="http://www.mongodb.org/">MongoDB</a>, <a href="http://nginx.org/">nginx</a>, <a href="https://www.varnish-cache.org/">Varnish</a>. And yet our initial launch was a complete disaster. If you want you can skip straight to the <a href="http://www.blogger.com/blogger.g?blogID=5538836060823517969#lessons">lessons learned</a>.<br />
<br />
<b><span class="Apple-style-span" style="font-weight: normal;"><b><span class="Apple-style-span" style="font-size: large; font-weight: normal;"><b>Day 1</b></span></b></span></b><br />
<br />
After a hurried, but exciting 4 weeks we were finally ready to launch our app. We had spent plenty of time tweaking the CSS3 box-shadows, working through all the <a href="http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/">CORS</a> (cross-origin AJAX) bugs in Safari and IE, and were ready to launch our awesome production ready app. We passed our partner's load test requirement like pros, the security audit was likewise a success. Primetime here we come.<br />
<br />
Launch party ensures. It was a pretty mild get together. Brad (CEO) hands everyone's wive's gifts for our hard work (See's Candy, delicious). We all get to feel good about ourselves and there we go live in 3..2..1..........crap. NGINX errors or Varnish errors. jQuery AJAX errors. Yeah, we're screwed.<br />
<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
So while the family and friends chatted away, we all got into a room and tried to figure out why our servers were dying. I mean Mixpanel is telling us we have maybe 400 people hitting the servers. I'm pretty sure my 256mb LAMP box could bench press that many people without breaking a sweat. But we've got VARNISH and MONGODB and NODEJS, NOSQL / JAVASCRIPT to the ends of the earth. Why do we suck so much? I mean we even have an API for our iOS app built on the same infrastructure that gets tons of hits without any of these problems.</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
Okay, turns out, we needed to learn a little bit more about Varnish. <a href="https://www.varnish-cache.org/wiki/VCLExampleCacheCookies">Varnish hates cookies</a>. We originally built the API powering our app with sessions, because it was going to serve itself up. Instead, we decided to go the CORS-based single-page-web-app approach. Our API and our front-end would be totally separate. Just throw our HTML/JS up anywhere and we're good to go. So scalable and awesome and un-coupled. We're amazing! So we have a backend that's supposed to be de-coupled, but ends up relying on cookies and sessions. Not so amazing.<br />
<br /></div>
<b><span class="Apple-style-span" style="font-weight: normal;"><b><span class="Apple-style-span" style="font-size: large; font-weight: normal;"><b>Day 2</b></span></b></span></b><br />
<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<div>
So the problem is we can't handle the load. Our not very good load testing, wasn't very good. Varnish can't cache things well that use cookies. So we quickly write up something that tells express to not use the cookies middleware on certain routes. We also figured out how to look at the <a href="http://www.mail-archive.com/varnish-misc@projects.linpro.no/msg02281.html">AGE header</a> from varnish to make sure that things are really caching. Our tests show things are caching. We're pretty confident things are going to be awesome today. So we chill out.</div>
<div>
<br /></div>
<div>
Nighttime comes (app gets used from like 6-9PM) and yeah it's basically like yesterday, which is complete crap. Not as complete crap, but basically it sucks. A lot of errors in different browsers, generally a lot of things go down from time to time, but overall they work throughout the night. We're live troubleshooting with the partner who we're launching this with. They have some few problems, but after refreshing their browser or retrying things, things basically worked.</div>
<div>
<br /></div>
<div>
We still aren't very expert at Varnish. We're not sure things are really cacheing properly. It looks like things are caching a little bit, but not very much. What the heck. varnishstat, be my friend, please. Our database is being murdered. Mongostat is showing hundreds of concurrents and it's really slow. We also use that database to do a lot of hard queries for our iPhone app. db.currentOp() on mongo shows tons of things being blocked. We go to our servers, we find out that nginx, which is in front of all of our node servers (to handle SSL, forwarding, and some other stuff) is running out of connections. </div>
<div>
<br /></div>
<div>
We quickly hypothesize that our database is too small to handle both a barrage of connections AND our really intense map reduces that are bogging it down every 20 minutes. Maybe the (fairly small) database is blocking, so NGINX is queuing up the connection, waiting for them to return and because they never return it eventually just has thousands of opened connections. So sometimes things work, but mostly it sucks.</div>
<div>
<br /></div>
</div>
<b><span class="Apple-style-span" style="font-weight: normal;"><b><span class="Apple-style-span" style="font-size: large; font-weight: normal;"><b>Day 3</b></span></b></span></b><br />
<br />
We make the database HUGE, first thing. Rackspace was like, screw you, you can't even make a database that big in your "huddle", but we said, no way, we want it. 16gb of ram. In your face wait times and blocked servers and NGINX too many connections / timeout errors, because we were waiting on the database. After a few hours of working with Rackspace we got our new production-ready database in order. Copy the image of the old db. Start up the new one. Manually move over any critical updates that happened during the process and we're good to go. Hallalujah. Finally off to NOSQL heaven!<br />
<br />
Come showtime no one can post or do anything, except us. Our partners who we're launching this experience with can't do anything. We can't reproduce it. This sucks. We get our whole team on Skype, finally some people start having the problem. The sessions are just basically constantly restarting. It appeared to work, because you could login and do one thing that required sessions. After that your session would restart and you couldn't do it anymore. We had two things going on that we're possibly giving us grief:<br />
<br />
1) We're still doing our auth over CORS, which is generally weird and send about 4 times more requests than is necessary.<br />
2) We migrated our database to a new server and didn't clean out our old sessions collection.<br />
<br />
We struggled, we stayed up really late. Our partner on this launch calls our CEO. They're about to drop our partnership. Another conference call with the whole team. We have basically have a day to show them that we're not complete idiots or the entire thing falls through.<br />
<br />
Our CTO mounts our totally de-coupled api server on-top of our web server using some express middleware craziness app.use('/viewer', require('api').createServer()). No more CORS.<br />
<br />
Next up we clear the sessions collection on our new database.<br />
<br />
<b>Wow, everything seems to work now</b>. Too bad we've had 3 days of crap. So we decide that to prove that we're awesome we need to get a bunch of users to test this <b>NOW</b>. We split the team in 2. Half the team stays up all night and tries to get people from the Internets to test this / put in place some legit load testing. The other team shows up at work at 5:30AM and carries on making sure that everything is solid.<br />
<br />
I'm pretty sure at this point that I'm going to lose my job.<br />
<br />
<b><span class="Apple-style-span" style="font-size: large; font-weight: normal;"><b>Day 4</b></span></b><br />
<br />
First off: don't try <a href="https://www.mturk.com/mturk/welcome">mechanical turk</a> at 4 in the morning to try and get a bunch of users to test your site. It doesn't work.<br />
<br />
Secondly, the middle of the night team setup a <a href="http://www.phantomjs.org/">PhantomJS</a> server that's able to properly emulate hunderds of concurrent users, actually using the app. It's pretty awesome and it really was the first thing we were able to use to make sure our scaling was working properly.<br />
<br />
I'm part of the early in the morning team. We get their too early. We can't open the door, so we hang out in Einstein's Bagel's nearby talking about what we're going to do. We weren't able to get online (other than our phones) for like 30 minutes, so we talked and talked and talked.<br />
<br />
Here are my ideas:<br />
- Get more app servers<br />
- Get NGINX out of the way of node?<br />
- Figure out Varnish, varnishstat, etc<br />
- Lots and lots and lots and lots of load testing<br />
- Get a less hacky way to get varnish to ignore sessions<br />
<br />
So we did all of those. I learned how to read <a href="http://nwlinux.com/interpreting-varnishstat/">varnishstat</a>. I did some basic apache-bench testing. I figured out that things still weren't being cached. We found the limits of our server setup. I added 5 app servers. I changed the varnish config to round-robin across all of our servers. We figured out how to get varnish and not express to handle routing /viewer to the api server. We made a plan to completely git rid of sessions, because they were the only thing left in the stack that we didn't build ourselves. Things are looking pretty good.<br />
<br />
We're still nervous about how things are going to go with our partner. We're expecting a call with them to hear about how they're canceling the partnership. The call never came. The night comes. The app seems to work okay.<br />
<br />
By this point though, we've lost most of our users :-/<br />
<br />
<b><span class="Apple-style-span" style="font-size: large; font-weight: normal;"><b>Day 4 and beyond</b></span></b><br />
<b><span class="Apple-style-span" style="font-size: large; font-weight: normal;"><b><br /></b></span></b><br />
I did a lot of testing and found that NGINX was no more performant than node.js directly on our small Rackspace servers. In fact, things were a lot better when NGINX was out of the situation.<br />
<br />
After this, our partner's amazing Load Testing team helps us really kill our servers. We tweak a few things with our client code, add more caching, and are able to consistently handle thousands of concurrent connections. Finally we're getting somewhere.<br />
<br />
For the next few days we dig into everything we can, constantly working on improving our load capacity and learning about. We find bottlenecks all over the place, but under the highest loads, Varnish, on a a cloud server with 4gb of ram, eventually starts to choke, but we're still pretty happy with say 2000 concurrents connections (which roughly translates to maybe 10,000 users at a time).<br />
<br />
Finally, the ad campaigns run, we get back to features instead of bug fixes and things start to slowly settle down. For the next few weeks we're paranoid and really careful that we're going to break things. After a while, they just keep working. We're happy and we can sleep at night.<br />
<br />
<span class="Apple-style-span" style="font-size: large;"><b id="lessons">Lessons Learned</b></span><br />
<b><br /></b><br />
<b>1. It's really important to understand your stack.</b><br />
<div>
<br /></div>
<div>
At the begging we didn't know if Varnish was caching anything. We couldn't tell you why we should or shouldn't have NGINX in front of Node. We weren't sure why the sessions collection was growing endlessly. It would have been really helpful to understand that stuff before we launched. It was really hard to learn how to debug and load test these components on the fly. You need to understand the tech before you run into problems with it.</div>
<div>
<br /></div>
<div>
<b>2. Load testing is really freaking important.</b></div>
<div>
<b><br /></b></div>
<div>
Without knowing the limits of our stack it was really hard for us to know how to scale when the time came. Day after day we made assumptions about how to improve things (grow the database, put static files on a CDN). They were useful, but they may not have been necessary had we figured out before we tried to launch where things were likely to break. We know now that 5 app servers is complete overkill for us. That's a couple grand we probably didn't need to spend on hosting. Also a lot of problems show under heavy load that don't show up otherwise. <a href="http://www.phantomjs.org/">PhantomJS</a>, <a href="http://code.google.com/p/httperf/">httpperf</a>, and <a href="http://httpd.apache.org/docs/2.0/programs/ab.html">apachebench</a> were all helpful tools to help us figure out our server stack.</div>
<br />
<br />
<b>3. Real-Time is hard. </b><br />
<br />
Part of our problem was that we were polling for updates every single second. With 500 users, that meant our database was getting hit 30000 times per minute from this one app. Later we changed the polling interval to 5 seconds and got Varnish to cache those requests properly. Next time, we might just look at using <a href="http://socket.io/">Socket.io</a>.<br />
<br />Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com4tag:blogger.com,1999:blog-5538836060823517969.post-69877866086778116972011-11-01T19:44:00.000-07:002011-11-01T19:44:36.812-07:00Analytics For Web Appsi.TV recently launched a hot new web app called <a href="http://ew.com/viewer">Viewer</a> along with <a href="http://www.ew.com/">Entertainment Weekly</a>. This app required some pretty specific analytics. The kind that a typical page-views style tracking system won't count.<br />
<br />
The big diff with webapps vs. typical web pages is it's usually only one page, so instead of capturing pages you have to capture events. Logins, posts, filtering, replies, replay in our case. We also need to know how long people are using the app. These are all events (or at least can be).<br />
<br />
Thankfully we found <a href="http://mixpanel.com/">MixPanel</a>, which provides some awesome graphs along with an easy to use event-capturing system.<br />
<br />
In addition to some pretty awesome support. MixPanel lets you bookmark reports that you create and they e-mail them to you regularly. You can't yet bookmark funneled reports (the fancy kind that show you the drop-off between various events: log in, post, etc), but you can send multiple reports.<br />
<br />
The monthly prices start at free, which is what we started with! That can log up to 25,000 events.<br />
<br />
All in all <a href="http://www.mixpanel.com/">MixPanel</a> is awesome for doing analytics for web apps!Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com2tag:blogger.com,1999:blog-5538836060823517969.post-19154043485749336982011-11-01T19:29:00.000-07:002011-11-01T19:29:02.361-07:00Mongoose and MongoHQ<br />
A while ago I wrote a <a href="http://j-query.blogspot.com/2011/04/mongohq-and-node.html">post</a> about how to hook up node.js with the <a href="http://www.mongohq.com/">MongoHQ</a> database hosting service. I still love <a href="http://www.mongohq.com/">MongoHQ</a>, but my mongo wrapper around the node mongodb native driver sucked! I recently switched <a href="http://wereviewutah.com/">wereviewutah.com</a> over to use <a href="http://www.mongoosejs.com/">Mongoose</a> and just wanted to share with everyone how easy it is to use Mongoose with MongoHQ.<br />
<br />
Sample Model File (models.js)<br />
<script src="https://gist.github.com/1332681.js">
</script><br />
<br />
Sample Config File (./config/db.js)<br />
<script src="https://gist.github.com/1332683.js">
</script><br />
<br />
Sample Route File (server.js)<br />
<script src="https://gist.github.com/1332694.js?file=sample-route.js">
</script>Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com2tag:blogger.com,1999:blog-5538836060823517969.post-78078763482898209292011-10-05T20:15:00.000-07:002011-10-05T20:16:14.286-07:00Remove passphrase from SSL keyWe have NGINX with SSL and automatically deploy with a script. We recently changed SSL certs and they were setup with a password, which makes it pretty hard to automatically deploy. I found this awesome tip on this blog <a href="http://blog.sosedoff.com/category/nginx/">http://blog.sosedoff.com/category/nginx/</a> for removing the password:<br />
<br />
<span class="Apple-style-span" style="background-color: #f9f9f9; color: #110000; font-family: 'lucida grande', verdana, arial, helvetica, sans-serif; font-size: 12px;"></span><br />
<pre class="bash" style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; border-bottom-color: silver; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial !important; border-left-color: silver; border-left-style: solid; border-left-width: 1px; border-right-color: silver; border-right-style: solid; border-right-width: 1px; border-top-color: silver; border-top-style: solid; border-top-width: 1px; clear: none; color: #111111; font-family: monospace; font-size: 12px; font: normal normal normal 12px/normal 'bitstream vera sans mono', monaco, 'lucida console', 'courier new', courier, serif; line-height: 1.333; overflow-x: auto; overflow-y: auto; padding-bottom: 8px; padding-left: 8px; padding-right: 8px; padding-top: 8px; white-space: pre; width: auto;">$ <span style="color: #c20cb9; font-weight: bold;">cp</span> server.key server.key.org
$ openssl rsa <span style="color: #660033;">-in</span> server.key.org <span style="color: #660033;">-out</span> server.key
The newly created server.key <span style="color: #c20cb9; font-weight: bold;">file</span> has no <span style="color: #c20cb9; font-weight: bold;">more</span> passphrase <span style="color: black; font-weight: bold;">in</span> it.</pre>
Jamund Fergusonhttp://www.blogger.com/profile/07860084493038965694noreply@blogger.com0