-
Notifications
You must be signed in to change notification settings - Fork 120
Style Guide
Add to underscore, not to the native object prototypes.
Single quotes are the preferred convention for defining strings. Only use double quotes when you're using interpolation:
# do
staticString = 'a string'
dynamicString = "url: #{window.location.url}"
# don't
staticString = "a string"
# can't do b/c single quotes don't allow interpolation.
dynamicString = 'url: #{window.location.url}'
# this
string = "url: #{window.location.url}"
# not this
string = "url: " + window.location.url
When you need to write blocks of text, use triple quotes """
. You can interpolate values within triple double quotes (as opposed to triple single quotes '''
):
color = 'red'
styles = """
body, html
background: #{color}
"""
Be careful though, triple quotes don't preserve indentation at the beginning of the text, so either add it to the end of the line, or escape it with backslashes \
.
# do this
console.log 'user.id:', user.get('id'), 'action:', @params.action
# instead of this (only to make your life easier)
console.log "user.id: #{user.get('id')} action: #{@params.action}"
- Don't use any "JS_KEYWORDS" words that CoffeeScript allows you to use, because even though you can do
user.delete()
in CoffeeScript, you'd have to writeuser["delete"]()
in JavaScript. These words are:
true
false
null
this
new
delete
typeof
in
instanceof
return
throw
break
continue
debugger
if
else
switch
for
while
do
try
catch
finally
class
extends
super
- Maximum of 1 blank line between chunks of code.
- First line of indented code should not have a blank line above it in most cases.
Do this:
class App.User extends Tower.Model
@field "email"
@hasMany "posts"
@hasMany "comments"
activate: (callback) ->
@updateAttributes activatedAt: new Date, callback
Not this:
class App.User extends Tower.Model
@field "email"
@hasMany "posts"
@hasMany "comments"
activate: (callback) ->
@updateAttributes activatedAt: new Date, callback
- Use the full word for the method name (
width
instead ofw
) - Only use abbreviations if the abbreviation is the preferred way of expressing the term in the industry. But even then, if the spelled out version is easier to read, use that.
"width" > "w"
"height" > "h"
"x"
"y"
"JSON" > "JavaScriptObjectNotation"
"src" == "source"
"directory" > "dir"
"createDirectory" > "mkdir"
"background" > "bg"
[todo] create a command to strip trailing whitespace.
Do this:
[one, two] = [1, 2]
Instead of this:
array = [1, 2]
one = array[0]
two = array[1]
When using the double arrow operator =>
, coffeescript generates var _this = this
for the wrapping method, so wherever you use this.aMethod()
or @aMethod()
it will replace it with _this.aMethod()
, so you don't have to mess around with setting up the right binding context manually.
methods =
a: ->
self = @
defineSelfExplicitly = -> # single arrow
self.c()
defineSelfExplicitly()
b: ->
defineSelfImplicitly = => # double arrow
@c()
defineSelfImplicitly()
c: ->
console.log 'called c'
Put private methods at the bottom of the file, even if they're named after a public method. Reason: it's easier to read the code, you get the API first, then the implementation.
Correct:
set: ->
get: ->
_set: ->
_get: ->
Incorrect:
set: ->
_set: ->
get: ->
_get: ->
- align semicolons on the left
- align equal signs on the right, 2n tabs in (falls on an even number of whitespaces)
- less code to manage
- fewer methods to memorize
- smaller footprint (less code for the browser to download)
- differs from Rails
- opt-in helper method generation
model.buildRelation("user") # can opt into
# vs.
model.buildUser()
Convert model.store()
to a store()
method on the class ONLY IF the number of times you use the method is such that creating the "wrapper" method would save on the number of characters in the js/coffee file (so when it's minimized, it's maximally minimized). That is, if you only call the long method once, don't wrap it, just deal with it. Or, if the method is sufficiently complex and needs to be tested, and is still only used once, then make it a method and test it. Better to test than to not.
- put
module.exports = X
at the bottom of each file so it can be stripped with a regular expression.
- write helpers so they are independent of underscore but can be swapped.
This makes it so you don't have to use require
everywhere on the client, setting the same variable over and over again.
class MyApp.User
@include Tower.Model
or
class User
@include Tower.Model
MyApp.User = User
Instead of
# user.coffee
class User
@include Tower.Model
module.exports = User
# somewhere else
User = require('../app/models/user')
Because of the naming/folder conventions, you can get away with this without any worries. It also decreases the final output code :)
Here is the structure of a comment (using codo
):
# Title goes here, single line if possible,
# otherwise overflow to next line with no blank line
# in between.
#
# Can put some more description here,
# multiple lines if desired.
# You can also use **markdown** anywhere in the comment!
#
# @todo Mark it with todo just after the title and [optional] description,
# and indent if multiple lines (indent anything following `@[keyword]`).
#
# @example Optional example with optional title
# # This will be syntax highlighted
# add(1, 2) #=> 3
#
# @param [Integer] a First integer
# @param [Integer] b Second integer
#
# @return [Integer] Put the return value last
add = (a, b) ->
a + b
- Keep blank line above new comments
- except at the start of a file
- or a new level of indentation
- keep a single space between the starting
#
and your text
Good:
# This class maps a user to a group
class App.Membership extends Tower.Model
# I am a new level of indentation...
@field 'role'
# ...and I'm not
@belongsTo 'user'
@belongsTo 'group'
Bad:
# This class maps a user to a group
class App.Membership extends Tower.Model
# I am a new level of indentation...
@field 'role'
# ...and I'm not
@belongsTo 'user'
#need indentation
@belongsTo 'group'
You also should avoid using ###
block comments, because while they may prevent having to start every line with a #
, it breaks up your code in ways that make it hard to read. Only use block comments to comment out large portions of your code, not for writing documentation within.
Order executable class methods in this order: concerns (mixins, unless mixin depends on things defined later in class), field
, field helpers (i.e. timestamps
), associations (belongsTo
first since it adds a field for the association id, then hasOne
, and hasMany
), association helpers (i.e. acceptsNestedAttributesFor
, or custom plugins), mass assignment protection (i.e. @protected
and @accessible
), scopes, validations, class methods, instance methods. Separate each different section with a blank line, and keep related items with no blank line between. This is all optional, but is helpful for reading and understanding someone else's code. The most important thing is to use consistent spacing.
class App.User extends Tower.Model
if Tower.isServer
@include App.UserAuthenticationConcern
@field 'firstName'
@field 'lastName'
@field 'role'
@timestamps()
@belongsTo 'address'
@hasOne 'profile'
@hasMany 'memberships'
@hasMany 'groups', through: 'memberships'
@acceptsNestedAttributeFor 'memberships'
@protected 'role'
@scope 'admin', role: 'admin'
@validates 'firstName', 'lastName', presence: true
@welcome: (id, callback) ->
@find id, (error, user) =>
if user
App.Notification.welcome(user).deliver(callback)
else
callback.call(@, error)
welcome: (callback) ->
@enqueue 'welcome', @get('id'), callback
For example, if you're writing an example action for a controller, do this:
class App.PostsController extends App.ApplicationController
index: ->
App.Post.all (error, posts) =>
@render json: posts
instead of this:
index: ->
App.Post.all (error, posts) =>
@render json: posts
This way it's always 100% clear to the reader what is going on.
Do this:
``` coffeescript
class App.User extends Tower.Model
```
Instead of this:
class App.User extends Tower.Model
I guess this is better from a typography standpoint (we were all taught wrong in school).