← Back to Envy Labs
jasonvanlue

Now Hiring: Senior Technical Project Manager

Envy is a leading web consultancy based in Orlando, FL, building digital products for the likes of GitHub, Google, O’Reilly, and Cisco. We’ve also built Code School, one of the most popular online educational platforms, teaching web technologies in the comfort of your browser. We help hundreds of thousands of people learn by doing, and partner with clients to solve problems through innovation and technology—we’re looking for a new Senior Project Manager. If you have experience leading large projects, love organization, and enjoy telling people the who, what, why, when, and where, this role might be for you. 

What it’s like to work at Envy Labs

View Envy Adventures

View Envy Office

Here’s What We’d Like to See

  • A problem solver — someone who loves discovering and applying solutions to client and internal projects
  • A critical thinker — someone who approaches problems as opportunities, and thinks hard about solutions
  • A self-starter — someone who doesn’t need to be micro-managed, and can take the initiative to present new ideas
  • Strong written and verbal communication skills
  • Several years of project and technical management experience, ideally at a web firm, creative agency, startup, or technology company
  • Proficient knowledge of web technologies, software development, and design
  • Experience with relevant project management tools, and best practices
  • Experience and competency in client relations and client service
  • Experience managing remote teams a plus

What You’ll Do

  • Take ownership of assigned projects and manage the team to help keep projects organized and on schedule. 
  • Help lead projects, and manage client relations and expectations.
  • Create organizational processes for new projects and initiatives. 
  • Monitor team progress to make sure deadlines are hit and goals are achieved.
  • Manage project calendars and milestones.
  • Help facilitate, document, and lead on-site client discovery sessions.
  • Help draft contracts, estimates and document communication throughout a project.
  • Manage and facilitate communication for multiple projects between departments.
  • Search for opportunities to make system improvements and create team efficiency.

More About This Position

  • This position is full-time and ideally on-site in our Orlando office (we just built out an incredible 10,000 square foot space complete with ping-pong, arcade, large common area space, catered Friday lunch, and an amazing work environment). 
  • Competitive compensation package including full benefits (health + dental + vision)
  • Conference / education allowance
  • Computer allowance
  • Regular company events
  • Flexible schedule / hours
  • Flexible vacation time
  • Profit sharing

How To Apply

  • Write or link to a post/article that describes your project management process. Bonus points if you can include information about a specific project, and how you solved specific problems in that time.
  • Describe your ideal project
  • Send your resume to jennifer@envylabs.com along with anything else you’d like to include.
  • Surprise us with something.
03.18.14
jacobswanner

Rake: Rule Tasks

This is the third post in a series on Rake, see previous posts for an introduction of the Rakefile format along with global tasks , and task dependencies along with file tasks.

At one point, I thought about writing about something else besides Rake (Rails 4.1 perhaps?). But, with the recent passing of Jim Weirich, I felt it better to continue showcasing one of his many contributions to the Ruby community.

So, to that end, in this post we’re going to look at another capability of Rake: rule tasks. We’ll cover how create them, how they work, and then we’ll expand on the example from the previous post to make it usable for more of our configuration files.

We’ll start off with this:

file 'config/database.yml' => 'config/database.yml.example' do |task|
  cp task.prerequisites.first, task.name
end

And, end up with this:

rule '.yml' => '.yml.example' do |task|
  cp task.source, task.name
end

Which is not only less code, but as you’ll see, more useful.

Rule Tasks

Rule tasks, also known as synthesized tasks, have the same characteristics as all other kinds of tasks: they have a name, they can have zero or more actions, they can have prerequisites, and if Rake determines the task needs to be run it will only be run once.

What makes rule tasks different is that you don’t actually give them a name — I know, I just said that rule tasks have names, just bear with me — instead when you declare the task you give it a pattern in place of a name.

Task Declaration

If that made no sense, hopefully looking at some code will clear things up. First, we declare the rule task, and for that we use the rule method:

rule /foo/ do |task|
  puts 'called task named: %s' % task.name
end

In the above example, we used a Regexp pattern to create a rule task that will match any task name with foo in it, and its action will report the task’s name. Let’s see it in action:

$ rake foobar
called task named: foobar

As you can see above, once executed, the task’s name was foobar, as that was the name given to the rake command, but the rule’s pattern is /foo/.

A common use for rule tasks is when dealing with files, especially when we don’t necessarily know what the file’s name will be, but we know what to do based on part of the file’s name, like its extension:

rule /\.txt$/ do |task|
  puts 'creating file: %s' % task.name
  touch task.name
end

In the above example, we used a Regexp pattern to create a rule task that will match any task name ending in .txt, and it’s action will create a file with that name. Let’s see how this task works in action:

$ rake hello.txt
creating file: hello.txt
touch hello.txt
$ ls
hello.txt

This approach of matching based on the end of the task’s name is so common in Rake, we actually don’t need to use a Regexp for it, as just a String of what the end of the task’s name will be will suffice:

rule '.txt' do |task|
  puts 'creating file: %s' % task.name
  touch task.name
end
$ rake world.txt
creating file: world.txt
touch world.txt
$ ls
hello.txt world.txt

Dependencies

As with any other kind of Rake task, rule tasks can have dependencies. These dependency tasks can be either regular, file, or other rule tasks. And, they are declared using the same Hash syntax we’ve seen before:

rule '.dependency' do |task|
  puts 'called task: %s' % task.name
end

rule '.task' => '.dependency' do |task|
  puts 'called task: %s' % task.name
end
$ rake rule.task
called task: rule.dependency
called task: rule.task

Rules for Files

Rule tasks don’t need to be about files, as some of the examples above have shown, but if there is a file with the same name as the task’s name, then that task will have the characteristics of a file task. There’s an entire post in this series about file tasks, if you need a reminder of their characteristics and how they are used. But, in a gist, it means: the task will only be executed if the file does not exist, or unless it has a file task dependency with a newer timestamp than itself. Also like file tasks, if the dependency is a rule task matching existing files, you do not need an explicit declaration for the dependency task itself.

rule '.txt' => '.template' do |task|
  cp task.prerequisites.first, task.name
end

For rule tasks, the Task object has an additional source method which, I believe, can improve the readability of our task declaration:

rule '.txt' => '.template' do |task|
  cp task.source, task.name
end

Let’s see this task in action:

$ ls
file.template               # file.txt does not exist
$ rake file.txt
cp file.template file.txt   # output from running task
$ ls
file.template file.txt      # new file has been created
$ rake file.txt
$                           # attempting task again, produces no output

Useful Example

In the prevous post of this series, we used a file task to copy example configuration files to their desired location:

file 'config/database.yml' => 'config/database.yml.example' do |task|
  cp task.prerequisites.first, task.name
end
$ rake config/database.yml
cp database.yml.example database.yml

And, while this certainly works, things start to get a bit unruly once we want to apply this technique to a number of files:

file 'config/database.yml' => 'config/database.yml.example' do |task|
  cp task.prerequisites.first, task.name
end

file 'config/newrelic.yml' => 'config/newrelic.yml.example' do |task|
  cp task.prerequisites.first, task.name
end

file 'config/sidekiq.yml' => 'config/sidekiq.yml.example' do |task|
  cp task.prerequisites.first, task.name
end

This is a great example of where a rule task really shines. All we need to do is create a single task with the correct patterns:

rule '.yml' => '.yml.example' do |task|
  cp task.source, task.name
end

And, we can apply it to any number of files:

$ rake config/database.yml
cp database.yml.example database.yml
$ rake config/newrelic.yml
cp newrelic.yml.example newrelic.yml
$ rake config/sidekiq.yml
cp sidekiq.yml.example sidekiq.yml

Have you found a helpful way to use rule tasks in your typical workflow?

- Jacob Swanner

(Source: jacobswanner.com)

03.17.14
tony-winn

Component-based Rails

A brief story

I’ve recently been working on replacing a billing system in a large rails app. Unfortunately, the billing system had become highly-coupled to the rest of the application so the change has had far reaching implications across the entire app. After lots of cursing and confessions, our team has worked up some ground rules to keep us out of this situation in the future:

  1. Callbacks are evil, avoid them like the plague.
  2. Favor the use of services (or interactors as some teams call them) for business logic instead of Active Record Models.
  3. Namespace related logic in our application.

These aren’t new concepts for Rails applications. If this project was started within the past two years I’m sure it would have been architected to avoid the pitfalls of 1 and 2. The pain of having to rewrite seemingly unrelated code during this large feature change had us longing for good and obvious boundaries within our application, which has led us to be more intentional with namespacing.

We picked a good name and started creating the models, controllers, views, services, presenters, and specs that we would need within our namespace. It started to feel a bit unnatural having our namespaced Ruby files spread across our project. This code was special, it had a name. It was its own concept and it needed to live all together in one place.

Around this time, I watched and shared Stephan Hagemann’s excellent talk from last year’s MountainWest RubyConf: Component-based Architectures in Ruby and Rails.

Getting started with components

There’s nothing really special going on here. All you do is add a component’s folder in the root of your Rails application.

Then create a Rails engine by running:

rails plugin new ./components/component_name --mountable

This will bootstrap your engine with everything you need to get started.

Finally, just provide a relative path to your Rails engine in your Gemfile.

Stephan also has a sample application that is a good reference when creating your first component.

How it’s working

After showing the team a proof of concept, everyone was on board. It’s a great compromise between the separation a full-on service-oriented architecture provides and the ease of working within a single application. We’re also in a good position to spin-off our component into a separate app or publish the gem to be shared in another project if that makes sense in the future. I’m sure there will be tweaks to the architecture as we go along, but we’re certainly in better shape to deal with change in our application now.

Have you used components in your Rails app before? We’d love to hear how it’s worked out, so please let us know in the comments.

03.10.14
drewbarontini

Flexible Sizing in CSS

With responsive designs now being commonplace, we as front-end developers need to take the time to build CSS systems that adapt and respond to the user’s viewport size and, in turn, any device. To help us accomplish this, we can use relative units (ems) for all of our margins, padding, and widths. Additionally, we can use unitless line-heights and percentages for our font-sizes to make our designs more flexible.

Ems: The Enigma

Ems have always felt a bit ambiguous in the CSS realm, but, with the importance of flexible styles within responsive web design, they have a more defined place.

So what is an em? An em is a relative unit equal to the current font-size. If the document font-size is 16px (as is the browser default), 1em is equal to 16px.

It’s important to know that altering the font-size on an element will affect the children of that element. For example:

body {
  font-size: 1em; /* 16px */
}

h1 {
  font-size:  1.75em; /* 28px */
}
h1 b {
  font-size: 1em; /* ? */
}

What do you expect the h1 b font-size to be? Because we redefined our relative em unit in the h1, the h1 b will be 28px, not the 16px value set on the body. This is an important concept of using ems, which can be powerful, but also cause headaches if you aren’t paying attention.

Note: There is also rem, which is like an em, but it’s always relative to the root font-size. In the same example, using rems, the h1 b font-size would be 16px.

If you want to learn more about ems (and rems), read this fantastic article by Jeremy Church.

Setting the Foundation

To get things started, we can create a few variables in our Sass (or equivalent CSS preprocessing language).

Note: If you’d like to learn more about Sass, check out the guide, or check out the courses at Code School.

$base-fontSize: 16px
$base-whitespace: 1.25em

Here we’re setting a base font size of our application, as well as a whitespace value in ems. To get this whitespace value, I just divided the target size that I wanted (20px) by the context size which, in this case, is 16px; 20/16 = 1.25. We now have the base variables set up for our styles. Let’s put them into action.

html
  font-size: $base-fontSize
  line-height: 1.5

body
  font-size: 100%

Using our $base-fontSize variable, we set the font-size on the html tag, and then set the font-size to 100% on the body for good measure. Also, we set a unitless line-height on the html tag, which will keep our line-heights flexible as the font-size changes.

Headings

Now let’s set up our headings.

h1, h2, h3
  margin-bottom: 0.25 * $base-whitespace

h1
  font-size: 300%
h2
  font-size: 200%
h3
  font-size: 150%

We add a bottom `margin` of our $base-whitespace multiplied by 0.25 (the advantage to using a preprocessor like Sass), which shrinks our margin down to 0.3125em. We then set our three headings with a percentage font-size. There’s no math here. It’s simply a what-looks-best situation, so make sure to play around with your font-sizes until you’re happy, or it satisfies the design requirements (if applicable).

Buttons

Another element where you can really see the responsiveness of this setup is buttons. Let’s write some base button styles.

.btn
  background: black
  border-radius: 3px
  color: white
  display: inline-block
  line-height: 2.5
  padding: 0 $base-whitespace
  text-decoration: none
  &:hover,
  &:focus
    background: grey

See the Pen Flexible Sizing in CSS - Button by Drew Barontini (@drewbarontini) on CodePen.

Most of this is just regular styling, but the important properties are line-height and padding. Again, we’re using a unitless line-height with a left and right padding value equal to our $base-whitespace.

Now, what if we wanted to create larger versions of our button? That’s really simple with this setup.

.btn-med
  font-size: 120%

.btn-lrg
  font-size: 140%

See the Pen Flexible Sizing in CSS - More Buttons by Drew Barontini (@drewbarontini) on CodePen.

How easy was that? All we did was increase our font-size percentage on our medium and large buttons. Since we’re using a unitless line-height and ems for our padding, the buttons will scale nicely.

Media Query Adjustments

If we wanted to adjust our overall size based on the viewport, we could simply alter the font-size percentage set on our body tag.

body
  font-size: 90%

  // ~700px
  @media screen and (min-width: 43.75em)
    font-size: 100%

  // ~1000px
  @media screen and (min-width: 62.5em)
    font-size: 120%

With those quick adjustments, our site will now scale responsively at various viewport widths. Hopefully you can see how useful this is when dealing with responsive sites.

That’s All, Folks

With some simple consideration for the sizing properties in our CSS, we can lay a foundation for a responsively sized design that can scale and flex based on small changes in our styles.

Here is a CodePen with the ideas and setup:

See the Pen Flexible Sizing in CSS by Drew Barontini (@drewbarontini) on CodePen.

03.03.14
greggpollack

Founder’s Talk

Last year I was encouraged to do a talk in the Starter Studio Founder’s talk series.  I ended up creating a talk where I walk through 25 lessons I learned in business in the last 8 years.  If you’re curious about the story behind Envy Labs and Code School, definitely start here.

The video itself is over on Code School, but it’s one of the few free CodeTV screencasts we have over there, hope you dig it!

image

Oh, and Starter Studio (the tech accelerator) is currently doing a Kickstarter to raise money for our second class, and we’re also accepting applications for tech startups who want to work along side us in Orlando.

- Gregg Pollack (@GreggPollack)

02.24.14
aimeeboothsimone

Deploy Notifications for HipChat

Here at Envy Labs, we use HipChat for a lot of our communications, including letting each other know when application deploys are happening.

The importance of deploy notifications is obvious when a number of people are collaborating on the same project, like Code School. Supposing two of us deploy at the same time, we’d run the risk of exploding the Interwebs:

Explode all the things!

And nobody wants that.

For a while, we tried using the (rocket) [app_name] convention, but it turns out that as humans, sometimes we forget. Better to let computers do it for us.

rocket production

Enter: Deploy notifications for HipChat. Note that this configuration assumes we’re using capistrano for deploys.

Step 1. edit config/deploy.rb

require "hipchat/capistrano"

set :hipchat_token, "your token"
set :hipchat_room_name, room_id
set :hipchat_announce, true
set :hipchat_color, 'green'
set :hipchat_failed_color, 'red'

Step 2. Add hipchat to your gemfile

gem 'hipchat'

That’s it! Now whenever we deploy Code School, and whenever the deploy finishes, a HipChat notification pops up automatically. Good job, computers.

hipchat capistrano webhooks

Does your team employ any useful communication hacks? Leave a comment and let us know!

- Aimee Simone (@aimee_simone)

02.17.14
caikesouza

Token Based Authentication in Rails

Token based authentication is when an API client uses a token identifier to make authenticated HTTP requests.

A lot of popular services offer token based authentication for connecting with their web API, like HipChat, Campfire, Backpack, Last.fm and many others. It’s not yet a standard, but there is an official draft that specifies the scheme.

image

Token based authentication offers many benefits over HTTP Basic and Digest Authentication:

  • More convenience, as we can easily expire or regenerate tokens without affecting the user’s account password.
  • Better security if compromised, since vulnerability is limited to API access and not the user’s master account.
  • The ability to have multiple tokens for each user, which they can use to grant access to different API clients.
  • Greater control for each token, so different access rules can be implemented.

Getting an API token usually means visiting a profile settings page on the service’s website and requesting an access key. Some might already have a key generated for us.

image

The Authorization header format for Token based authentication looks like so:

GET /episodes HTTP/1.1
Host: localhost:3000
Authorization: Token token=123123123

Rails Authentication

Rails offers the authenticate_or_request_with_http_token method, which automatically checks the Authorization request header for a token and passes it as an argument to the given block:

authenticate_or_request_with_http_token do |token, options|
  # authenticate user...
end

Inside that block is where we implement our authentication strategy. In the following example, we’ll authenticate our requests for the EpisodesController class.

class EpisodesController < ApplicationController
  before_action :authenticate

  def index
    episodes = Episode.all
    render json: episodes, status: 200
  end

  protected
    def authenticate
      authenticate_or_request_with_http_token do |token, options|
        User.find_by(auth_token: token)
      end
    end
end

Using a before_action, we call the authenticate_or_request_with_http_token method. We only care about the first argument, which is the token we’ll use to look up the user.

It is very important that the auth_token is unique across all users. In our User model, we use a before_create callback to generate the token.

class User < ActiveRecord::Base
  before_create :set_auth_token

  private
    def set_auth_token
      return if auth_token.present?

      begin
        self.auth_token = SecureRandom.hex
      end while self.class.exists?(auth_token: self.auth_token)
    end
end

The token generation code is placed inside a while loop. If SecureRandom returns a token that’s already being used, we’ll keep on looping until it generates one that’s unique.

If we wanted to take it one step further, we could also add a unique constraint on the auth_token column in the database.

Using curl, we can test our token based authentication by passing a valid token in the Authorization header:

$ curl -IH "Authorization: Token token=16d7d6089b8fe0c5e19bfe10bb156832" \
  http://localhost:3000/episodes
HTTP/1.1 200 OK 
Content-Type: application/json; charset=utf-8

Unauthorized

If the authentication fails and our block returns false, the request is halted and our application immediately responds with a 401 - Unauthorized status code.

$ curl -IH "Authorization: Token token=fake" http://localhost:3000/episodes.json
HTTP/1.1 401 Unauthorized 
Content-Type: text/html; charset=utf-8
WWW-Authenticate: Token realm="Application"

According to the HTTP spec, a 401 - Unauthorized response must include a WWW-Authenticate header with a challenge applicable to the requested resource. The authenticate_or_request_with_http_token automatically includes that header for us:

WWW-Authenticate: Token realm="Application"

The Token part means that the given resource uses token authentication. The resource under that URI is currently part of the “Application” realm. The realm value allows protected resources to be partitioned into different sets of protection spaces, each with its own access policies.

The default realm value used by Rails is “Application”. To change it to a more descriptive value, we can pass the new name as an argument to the authenticate_or_request_with_http_token method.

authenticate_or_request_with_http_token('Premium') do |token, options|
  User.find_by(auth_token: token)
end

Limitations

One limitation we might come across when using authenticate_or_request_with_http_token is the fact that this method doesn’t allow for much customization. For example, it always responds with the Content-Type set to HTML regardless of the mime type requested by the API client. There’s also no easy way to add a custom error message to the response body if we wanted to.

For more flexibility, we can use the authenticate_with_http_token method and manually build the response ourselves:

class EpisodesController < ApplicationController
  before_action :authenticate

  def index
    episodes = Episode.all
    render json: episodes, status: 200
  end

  protected
    def authenticate
      authenticate_token || render_unauthorized
    end

    def authenticate_token
      authenticate_with_http_token do |token, options|
        User.find_by(auth_token: token)
      end
    end

    def render_unauthorized
      self.headers['WWW-Authenticate'] = 'Token realm="Application"'
      render json: 'Bad credentials', status: 401
    end
end

And that gives us a proper JSON response:

$ curl -IH "Authorization: Token token=fake" http://localhost:3000/episodes/1.json
HTTP/1.1 401 Unauthorized 
Content-Type: application/json; charset=utf-8

For more information about token based authentication, the draft is available at http://tools.ietf.org/html/draft-hammer-http-token-auth-01. The code examples for this blog post are available on the BananaPodcast project on GitHub.

We will be covering this and other topics in our upcoming “Surviving APIs with Rails” course on Code School, which will be out in March.

Have you ever implemented a token based authentication on your Rails API ? We’d love to hear your opinion in our comments!

- Carlos Souza (@caike)

(photo source: http://www.flickr.com/photos/kolix/2539213620)

02.03.14
markkendall

Now Hiring: Senior Developer

Envy Labs is a leading web consultancy and software development group based in Orlando, FL, building digital products for the likes of GitHub, Google, O’Reilly, and Cisco. We’ve also built Code School—one of the most popular online educational platforms, teaching web technologies from entirely within a web browser.

We’re a bootstrapped company and the success of things we build here is due largely to the team of top-notch developers and designers we’ve been able to assemble. We genuinely enjoy working together with our clients to build amazing things. And now, we need to add another Senior Developer to our growing team. If you love speaking and working with clients to understand their business, identify their needs, and partner with them to develop highly interactive web applications, then this role might be for you.

Here’s What We’d Like to See:

  • A problem solver—someone who loves working with clients to discover and develop solutions.
  • A constant learner. If you’re always excited to try out the latest technology or technique, you’ll fit right in.
  • Someone who truly enjoys working with clients and aims to exceed their expectations.
  • Solid experience building web applications and APIs with Ruby and Rails.
  • Experience working with modern JavaScript frameworks, especially Ember or AngularJS.

What You’ll Do:

  • You’ll work in small (usually 2-3 person) teams to design and build applications for Envy clients.
  • You’ll be responsible for communicating with clients to learn about their business and working with them to craft an ideal solution for their needs.
  • You’ll have the opportunity to work on varied projects and team up with different Envy developers. We love learning from each other while we work.
  • You’ll work closely with the Envy design team, and you’ll be amazed at the things they create.

More About the Position:

Ideally, we’re looking for someone to work in our Orlando office, however we will consider remote hires if you’re a great fit. Compensation is very competitive, and we offer a full benefits package (health + dental + vision included).

Learn more about our culture and check out this video to see what it’s like to work here.

How to Apply:

  • We’d love to see some samples of your work — link to your Github profile or recent work. Better yet, pick a project that you think showcases your best work and tell us about your development approach and design decisions you made.
  • Link to a blog post or article you’ve written or talk you’ve given in the past few months on a specific development topic, code technique, or something you’re working through.
  • Surprise us with something.

If you think we should consider you for the position, email us.

12.12.13
greggpollack

What it’s Like to Work Here

I could try to describe what it’s like to work at Envy Labs, but a video does far more than words on a page.    

A few weeks ago it became clear that we are short a few people for the goals we wish to accomplish in 2014. Both Envy Labs and Code School are now looking for a few people to join our teams. Before you ask… Yes, you’re going to need to live or relocate to Orlando to have a shot. Sorry, we believe in building a fun office culture, so remote is not something we’re looking for right now. Plus, the weather here is beautiful right now!

We are currently looking for:

To read more about the Code School Developer positions, please see our jobs page on Code School.

To contact us, email: jobs@envylabs.com

12.11.13
drewbarontini

JavaScript Conventions

With the increasing complexity and involvement of JavaScript in modern web applications, it has become even more important, while working on a team, to establish proper guidlines for writing JavaScript. Code School, our online education platform, has a very large codebase and, in that codebase, there is a lot of JavaScript. Both front-end developers and back-end developers write JavaScript, and it’s important that we all adhere to the same conventions.

With that, since it’s always fascinating to peer inside other’s workflow and setup, we wanted to share our JavaScript conventions on Code School.

General

  • Write JavaScript in CoffeeScript
  • Use soft-tabs with a two-space indent
  • Use camelCase for variables and methods
  • Use PascalCase for class names
  • Use single quotes for strings, unless using string interpolation in CoffeeScript

CoffeeScript

Writing Classes

class @Klass

Doing this replaces the need for the follow following:

class Klass
  # ...

window.Klass = Klass

jQuery

  • Always use .on()
  • Use .first() and .last() not :first and :last

Namespace

Everything is namespaced under the CS object.

  • A class is named CS.ThisIsMyClass
  • A component method is named CS.methodName

Deeper nesting of classes and components will be looked at in the future in order to improve readability.

Class

Larger, related pieces of functionality make sense as a class. If you find yourself writing multiple methods that are calling one another, it probably makes sense to write a class.

General Structure

class @Klass

  ###
  # Called when the object is instantiated, and it immediately
  # defers to an 'init' method, which can be called without having
  # to instantiate an object.

  # @options sets instance variables for all of the options that are
  # passed in.
  ###
  constructor: (@options) -> @init()

  init: ->
    # Initialize things here...

    ### 
    # Call 'setEventHandlers' method to set up all of the
    # individual event handlers for the elements of the class
    ###
    @setEventHandlers()

  setEventHandlers: ->
    options.element.on 'click', (e) => @doSomething()

  doSomething: ->
    # ...

  ###
  # Class methods start with an '@' sign before the name, and
  # they aren't called by the instantiated object but, rather,
  # from within the class

  # e.g. Klass.klassMethod()
  ###
  @klassMethod: ->
    # ...

Instantiation

The goal is to pass in the selectors through the options object, which modularizes the class, and makes it easier to update the selectors.

new Klass({
  element: $('.element')
})

Component

A component is essentially a categorization of related, but separate methods. For example, the utilities component is a set of related methods scoped under the CS namespace. Each method is intended to be a one-off piece of functionality that, when written abstractly, can be reused across the application. The general structure of a component method is as follows:

CS.methodName = (options) ->
  options.element.on 'click', (e) ->
    e.preventDefault()
    # ...

The methods follow similar conventions to classes, where an options object is passed in to make the method modular. Use abstract names in an effort to modularize the piece of functionality you are writing. The utilities component is the best example of this, as most of the methods are named and written generically in order to reuse across the application. Other components, such as teams, are written more specifically for a particular part of the application.

Inbox

If you’re not sure where something should go, add it to the JavaScript inbox section in application.js.coffee, and a front-end developer can help sort out the organization.

#-------------------------------------
#  Inbox
#-------------------------------------

This is as simple as adding a comment heading at the very bottom of application.js.coffee to signify the spot to drop temporary JavaScript that needs to be sorted. We use the same convention in our stylesheets. It’s insanely simple, but it’s super helpful.

Selectors

When passing in selectors via the options object, make sure to use selectors that are efficient. Please don’t use over-qualified selectors, such as .element-parent .element-child .element-grandchild when .element-grandchild (or a js- specific class name) will serve much better.

Adding Classes

If there isn’t a class or ID on an element that you can hook into, add a class with a js- prefix, followed by a hyphen-separated name that makes sense.

State

Toggling the state of an element is done through the use of is- prefixed class names, not through .hide(), .show(), etc. For example, toggling the class of .is-hidden on an element will change its visibility. There are global state classes set up in the application, but more specific ones can be applied by adding the element name into the class. For example, .is-element-toggled. These class names should be set up by the front-end developer to make toggling the state of an element via JavaScript much easier.

Calling Methods & Classes

There is one of two places to put a call to a method or class:

  1. The domready.js.coffee file
  2. The view file where the JS is run (inside a :javascript Haml block at the bottom of the file)

The domready.js.coffee file contains methods and class calls that occur in several places across the application, or globally across the application. The :javascript block inside a view file is for calls that will only occur on that page. Please avoid using a lot of logic in this location; it is only meant for setup of methods and classes.

Getting Acquainted

To get familiarized with the structure of the JavaScript, open up the application.js.coffee file. It acts as a loader for the application, so all the imports for the classes and components are located here. You can go through each of the files in turn in order to get a better feel for how things are written.

That’s All

We hope that you enjoyed this brief look into how we write and structure our JavaScript.

12.05.13
blog comments powered by Disqus