How to reset the EmberJS router namespace with this.route()

With this.route() being fully nestable as of Ember 1.7.0, the best practice has been to drop use of this.resource() altogether. I highly recommend always using this.route() for one reason in particular: preventing conflicting route names.

Example

In our sample app, we might have a route called workspaces, with an administrative area at admin.workspaces. If we were to follow the older convention of declaring any route with a noun as name as a resource, the route file should look like this:

// router.js using this.resource()

Router.map(function() {
  this.resource('workspace');
  this.route('admin', function () {
    this.resource('workspace');
  });
});

This router will generate route names of workspace, admin, and workspace.

Wait. Hold up. Two routes with workspace as the name?

You bet. Using this.resource() resets the namespace of the route. You can override the nested workspace route name by doing this.resource('admin.workspace', { path: /workspace }), but why bother when this.route() can take care of this for us?

// router.js using this.route()

Router.map(function() {
  this.route('workspace');
  this.route('admin', function () {
    this.route('workspace');
  });
});

Now we get generated route names of workspace, admin, and admin.workspace. Neat!

If in the event that you absolutely must reset the namespace of a nested route, you can use the { resetNamespace: true } option on the route.

// router.js using this.route() with reset namespace

Router.map(function() {
  this.route('workspace');
  this.route('admin', function () {
    this.route('workspace');
    this.route('project', { resetNamespace: true });
  });
});

We get the same three route names generated as the previous example, as well as a new route at project. That's right, no admin. is prepended. Resetting the namespace can be used prudently for lengthy route names. Be careful only to reset when you can guarantee that you'll likely never have a naming collision with another route.

Lessons growing product revenue

Joseph Walla writing for The Next Web,

I remember meeting with Garry Tan, who is an incredible designer. He mentioned that he hasn’t seen a strong correlation between good design and success of startup companies. So, if you have to choose, pick great user experience, then add great design later.

Conversely, focusing on pixel perfection in the early days may even prevent you from getting the revenue you need.

There are times I find myself over-designing or over-engineering a feature. It's a good reminder to focus on the core experience, rather than wish a magical fairy of beautiful and delightful design will bestow upon us the killer interface. User experience trumps.

Effective error reporting with EmberJS and Bugsnag

An important part of a professional application is catching and reporting bugs. In Candidio, we report bugs from our EmberJS application to Bugsnag. Bugsnag has great documentation and a good amount of language support with their first-party notifier libraries. We're quite happy with their service and look forward to using them for the forseeable future.

Raygun recently detailed how to send error reports to their service, so I thought I'd pass along how we accomplished a similar feat with Bugsnag.

Bugsnag.js

Get Bugsnag's Javascript notifier with Bower using the command bower install --save bugsnag. Import the bugsnag.js source into your app in Brocfile.js with app.import('bower_components/bugsnag/src/bugsnag.js');.

In .jshintrc add "Bugsnag": true to the predef hash, so you don't get squawked at every time you use the Bugsnag global variable.

Initializer

Create a new initializer at app/initializers/bugsnag.js on the command line with the ember g initializer bugsnag command. In the initialize function, we're going to declare the Bugsnag API key, the releaseStage (current environment), and the stages we want to notify Bugsnag from. In Candidio, we only send Bugsnag errors from our staging environment and the production site.

Bugsnag.apiKey = config.APP.BUGSNAG.API_KEY;
Bugsnag.releaseStage = config.environment;
Bugsnag.notifyReleaseStages = ["staging", "production"];

Routing Errors

The first type of error we want to report is routing and edge cases, which we'll catch with Ember.onerror. In this error report and the following, we're sending along the current path, rather than letting Bugsnag grabbing the URL from location.href. I've found that in the context of EmberJS development, this is more useful.

Ember.onerror = function(error) {
  Bugsnag.context = appController.get('currentPath');
  Bugsnag.notifyException(error);
  console.error(error.stack); // Still send the error to the console
};

Promise/AJAX Errors

We should also catch errors from rejected Promises, which we can do similarly with the RSVP.on('error') method.

Ember.RSVP.on('error', function (error) {
  Bugsnag.context = appController.get('currentPath');
  Bugsnag.notifyException(error);
});

Ember Logger Errors

Let's also send any errors logged by Ember's internal logger service and make sure they still get sent to the console.

Ember.Logger.error = function (message, cause, stack) {
  var currentRoute = appController.get('currentPath');
  Bugsnag.context = currentRoute;
  Bugsnag.notifyException(new Error(message), null, { cause: cause, stack: stack });
  console.error(stack);
};

Bonus: Javascript Sourcemaps

Sourcemaps are enabled by default on the ember-cli development build, but to get a sourcemap on every build, including production, add the following sourcemaps hash in Brocfile.js. Bugsnag will get the sourcemap declaration at the bottom of our concatenated source and give you a much improved stack track over the minified source.

// Brocfile.js
var app = new EmberApp({
  ...
  sourcemaps: {
    "enabled": true,
    "extensions": ["js"]
  }
});

Gist of the Bugsnag integration

Other useful details you can send to Bugsnag


Updated March 18, 2015: We should still log out appropriate errors to the console. Thanks to Ben Holmes for the tip!

Intercom + EmberJS: A Match Made in Heaven

With the release of Candidio 3.0 in early January, we wanted to make a change away from Zendesk as our helpdesk software. I've had my eye on Intercom for a quite a few months now. Not only is Intercom a robust customer service application, but they seem to be great people who know what they're taking about, not only on the customer service side of the business, but with product management and developing great software. Lastly, the Intercom web app is built with EmberJS, so that can't hurt.

Setting up Intercom in EmberJS

We took a fairly simple course of action to intergrate the Intercom tools into our ember-cli application.

Include the JS snippet

In app/index.html add the Intercom JS library snippet (don't forget to update the APP_ID in this snippet):

<script>(function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',intercomSettings);}else{var d=document;var i=function(){i.c(arguments)};i.q=[];i.c=function(args){i.q.push(args)};w.Intercom=i;function l(){var s=d.createElement('script');s.type='text/javascript';s.async=true;
s.src='https://widget.intercom.io/widget/<INSERT APP_ID HERE>';
var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);}if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})()</script>

We've also added Intercom to our global variables list in .jshintrc so we can call that variable anywhere in the app.

Initialize Intercom

Intercom provides a method to boot up the service and give them context, which you could add a number of places. We had it in an initializer previously, but currently we include it in an observer on the application controller, which watches the current user object. In our circumstance, this isn't an issue, because we never change the authenticated user. The boot method tells Intercom details about the current user, which is ultimately what you're using Intercom for in the first place.

Intercom('boot', {
  app_id: app_id,
  user_id: user_id,
  user_hash: hashed_user_id, // This is for secure Intercom connections
  name: user_full_name,
  email: user_email,
  created_at: create_at_in_utc
});

At minimum, you must send the user_id and email for Intercom to function. The documentation on setting up Intercom in a single-page app has more details.

Notify Intercom of route changes

Every time a user navigates to a new page, we want to notify Intercom. We can easily do this by reopening the router class in router.js.

Router.reopen({
  notifyIntercom: function () {
    Intercom('update');
  }.on('didTransition')
});

Other Useful Intercom Methods

  • Tracking events and providing context
  • Javascript API
    We use the JS API to implement our own button to open the chat panel. The chat bubble overlay they provide by default interferes with our user interface. In route/application.js, we've added the following action, which can be trigger from anywhere in the app.
// route/application.js
actions: {
    openIntercomWidget: function () {
      Intercom('show');
    },
    ...
}

// Usage in a template
<button {{action 'openIntercomWidget'}}>...</button>

Feel free to response in the comments below, or hit me up on Twitter at @ToddSmithSalter with any.

On Thoughtfulness

As I come to the summit of a large project, I've been reflecting on a few errors in thinking I made along the way. I was in a time crunch and finishing the project became the sole goal in my head. Being so focused on the end goal, I lost the plot a bit stopped being thoughtful about my decisions, prattling on through tasks and compounding decision upon decision.

If there's one thing I could take away from what happened, it'd be that before I dive into solving the problem at hand, I'd think first about how I can tackle the problem, how it can affect all the other moving parts of the project, and only then move forward with implementation.

For example, I've written a tonne of CSS in the OOCSS flavour. When I look at my code structure, I see many classes that are tied to others, breaking the single responsibility principal, which states that every class should have one responsibility and doesn't need to know about anything in the other classes. I've broken that guideline numerous places, leading to fragile code. Future changes could mean making modifications in numerous files instead of the one or two places where they could have been.

Note to self: Don't throw away personal guidelines to finish a project quickly. Make thoughtful decisions so it doesn't bite you later.

Planning with Ease

Mindmap

Since the beginning of the year, we've been planning on a rebuild of Candidio. Tim Loyer has been working away on a REST API, while I'be been patching the current version of Candidio in production. I've also been working on designs and a style guide for the new version and we're almost ready to begin development of the front-end web application.

The problem I was running into, which I think was completely internal, is I didn't really know where to start with the build. We have a really good idea of what the application will look like and function, but sitting down to implement the designs, is a new ballgame. Having the style guide almost finished is a huge help. I think I should be able to hammer out the templates for the main areas of the application fairly quickly, since the guts of the designs are already written in code.

Early this week, I sat down with my notebook and focused entirely on brainstorming and planning, through a basic mindmap, all the general features to make Candidio 3 function at it's barest level. As I move through the build, I would think we'll be adding more and more granular features to our backlog, but we've got a great start.

If I was to learn one thing from this, is that taking a step back and getting something, anything, on paper is useful. The simple act of focusing and brainstorming without self-critiquing should be my step 1.

A simple Gulp build task

I've been using Gulp as my task runner of choice for a few months now. Slowly, I've refined my gulpfile to a version that I'm happy with.

My gulpfiles have typically had a few tasks: compile Sass, hint and minify Javascript, and watch for changes and recompile while I work. I'd have a few tasks to queue up a build and watching for changes. While that is probably enough for most, I wanted a slightly more sophisticated task for building development and production files. Ideally, I want to have one task that I can add some flags to, like this:

$ gulp build --production

The difference being, if I add this --production flag, I want the compiled files to be minified.

Where it clicked for me

A couple of days ago, Headstart, an automated front-end setup tool was released. As I usually do with this type of project, I jumped over to Github and started scouring the source code. The gulpfile that Florian wrote was a thing of beauty.

I immediately latched on to a few Gulp or Node plugins to help my in my quest: gulp-if, run-sequence, and minimist.

Gulp-if allows you to conditionally run a task if a a condition is met. In my case, I want to test if I'm creating a production build, so my files get minified. Here's an example of where I'm testing if we're creating a production build and adding a .min suffix to the filename.

.pipe(gulpif(isProduction, rename({suffix: '.min'})))

Run-sequence allows you to run a series of dependent tasks in the order you choose. The best part of all, is that you can run tasks in parallel. In the example below, the clean task is run, then all the other task are run in parallel.

sequence(
        'clean',
        [
            'sass-main',
            'sass-ie',
            'scripts-main',
            'scripts-ie',
            'images',
            'other'
        ],
        function() {
            console.log(chalk.green('✔ All done!'))
        }
    )

Minimist is what allows us to write flags on the build task from the command line. As above, if I add the --production flag, a minified production build is created. I'm also recognizing a --watch flag, which queues up the watch task after the initial build.

If you want to see the full gulpfile with all tasks, I've created a Gist for your viewing.

In Closing

I hope this can be useful to you. If there's one thing to take away, it's to be curious and read other developers source code. You can learn much from other's work if you take the time to explore.

If you have any feedback or suggestions, feel free to hit me up on Twitter @centerdrive – I'd be interested in learning about your build processes and tasks using Gulp or any other tools.

How to be a Great Client

Hiring a creative agency or freelancer to help your company with design can be a daunting task. There are a lot of factors to consider and finding the right personality and process match in an someone can be difficult. Openly and honestly discuss your company and goals for the future, and who you are as a leader are crucial to finding the best pairing.

Put aside your preconceived notions

A designer's job is to take your story, dreams, hopes, ideas and transform them into a work that represents who you are as a company. Let them do what they do best and translate those thoughts into something awesome. You'll be much happier with the final product. You'll likely be surprised with the angles you never thought about.

Don't ask for spec work

You don't go to a hair salon or barber and ask them to cut your hair and only offer to pay if you like it. Don't ask an agency or designer to do the same for you. Expect your request to be rejected outright. If you need to insist on exploratory designs, pay the designer their going hourly rate.

Be prepared to talk about money

The bottom line for a designer is that they need to put food on the table, for them and their family. Go into preliminary discussions with an idea of how much you're willing to spend on hiring someone. Also consider who you're hiring. Agency's will cost more, but you're hiring a hive mind to research, design and develop. Freelance designers may be less expensive, but no less qualified.

Going into negotiations with your budget helps you and the designer establish what parts of the project will fit into your budget. Without the knowledge of what you're ready to spend, plan for a longer negotiation and project exploration process.

Judge someone's abilities by what they've put into the world

All designers have good intentions to create awesome; to design or build something that will give great acclaim. Judge a designer or agency on the fruits of the previous labour. Look over their portfolio and see if the works they've released to the world fit your fancy and business model.


I believe that most of this is just using common sense. Going into meetings and negotiations with an open mind, prepared to listen and think critically, will go a long way to finding the right fit in a designer.

Go forth and find someone awesome.

On Paralysis

I've come to realize that I'm scared. I'm scared what others will think of me. I'm scared that I won't be liked or what I say or think won't be of value to others. In truth, I'm paralyzed by the thought that what I want to contribute won't be appreciated by others, or at the very least, they'll disagree.

Who cares what others think? I've been letting the fact that other people have their own opinions persuade me from letting mine be heard. For what? The fear of them disagreeing with me? That's just silly.

It's time to stop to letting the fear of anonymity, of being ignored, of being disagreed with, prevent me from just documenting my own thoughts. I'll be honest with myself: there's the likelihood that no one is going to read this, and that's ok. I'm going to write for myself. I'm going to document my own processes for my own good, not for anyone else.

Paralysis begone.

Fixing File Upload Error - exceeds MaxRequestLen

As you may know, web hosts limit file upload sizes via the php.ini file. It's fairly simple to change the directives in said file to increase the maximum file upload size.

The site I'm working on for GP Church of Christ gives the church content administrator the ability to upload sermon audio files. Being that most sermons are 30+ minutes, the mp3 audio files tend to be larger than 20 MB.

After a few attempted uploads, we were getting this weird error in the server log: mod_fcgid: HTTP request length 16777353 (so far) exceeds MaxRequestLen (16777216). The WordPress uploader showed an HTTP Error dialog, but no elaborate description of the issue.

After a couple days of digging, experimenting, failing all over the place, I came upon this article, which explains perfectly how to overcome this problem.

But let's step back for a minute and examine what really is the problem. My server, is using the FastCGI module for PHP support, rather than Apache. The php.ini file still has directives that govern the ability to do actions like uploading files. In addition to those php.ini directives, there are two locations that govern the FastCGI module and it's upload limits.

  1. The FastCGI global config file, probably at /etc/httpd/conf.d/fcgid.conf.
  2. A domain specific FastCGI config file at /var/www/vhosts/your_domain_name_here/conf/last_httpd_ip_default.include.

Vim into both those files and ensure that the following lines are there and have a value, any value.

<IfModule mod_fcgid.c>
FcgidMaxRequestLen 1073741824
</IfModule>

As above, I've got mine set to the default of 1 GB (1 GB = 1073741824 bytes).

Apparently, fcgid settings are also in your virtual hosts file at /usr/local/psa/admin/conf/templates/default/domain/domainVirtualHost.php. Find the same IfModule mod_fcgid.c element and make the same change.

Next, let's reconfigure your domain with Plesk:

/usr/local/psa/admin/bin/httpdmng --reconfigure-domain yourdomain.com

Lastly, restart PSA and Apache:

service httpd restart
service psa restart

Now, you should be good to go! Try uploading a large file under 1 GB.

Sources