<commit_before>[Ramda](https://www.npmjs.com/package/ramda) is quickly becoming an indispensible part of my node projects. Lodash is more accessible and beginner-friendly, but ramda is far more powerful and expressive once you wrap your mind around it. In this article, I'll take a look at the [`applySpec()` function](http://ramdajs.com/docs/#applySpec) and how it can replace dependency injectors like [wagner](http://npmjs.org/package/wagner-core).

What's Dependency Injection About?
==================================

The fundamental idea of dependency injection is to separate out business logic
and service initialization. For example, let's say you have some code that
runs a query against MongoDB:

```javascript
const mongodb = require('mongodb');

let db;

function getMongo() {
  if (db) {
    return db;
  }
  db = mongodb.MongoClient.connect('mongodb://localhost:27017/test');
  return db;
}

module.exports = function(id) {
  return getMongo().
    then(db => db.collection('User').findOne({ _id: id }));
};
```

Node.js beginners tend to write code that differs only superficially from the
tangled mess above. Little refactors like putting the `getMongo()` function
in a separate file, getting the connection string from environment variables,
and adding error handling don't help the fundamental issue that your query is
tied one-to-one to a mongodb database handle. A better approach is something
like this:

```javascript
module.exports = db => function getUser(id) {
  return db.collection('User').findOne({ _id: id });
};
```

This way, your database handle is completely decoupled from the query you run.
You can initialize one or many database handles and still use the same
business logic.

<img src="http://i.imgur.com/HXKxWYS.png" />

This is particularly important for MongoDB, which
[limits you to one operation per open socket at a time](http://api.mongodb.com/python/current/faq.html#how-does-connection-pooling-work-in-pymongo). In other words, unless you tweak the pool size in the
Node.js driver, MongoDB will only process up to 5 operations in parallel per
database handle.
This can be bad if you have a lot of fast operations queued up behind a few
very slow operations, like if a few heavy queries from your admin dashboard
are blocking user logins. Decoupling initialization from business logic makes
it easy to have separate database handles for queries you expect to be slow.

Where DI Goes Wrong
=========================

Dependency injectors are powerful tools for breaking code up into services
that depend on each other.

```javascript
// db service
function db(config) {
  // initialize db
}

// logger service
function logger() {
  // initialize logger
}

// query service
function queryBuilder(db, config, logger) {
  // initialize query builder
}
```

For example, in the above code, a dependency injector like [wagner](http://npmjs.org/package/wagner-core) would be smart enough to walk
the graph of dependencies and see that in order to initialize `queryBuilder`,
it needs to initialize `config` and `logger` first, then `db`, and then
`queryBuilder`. A DI tool lets you separate logic from initialization in a
convenient way where you don't really have to think about where the services
you depend on come from.

Unfortunately, convenience is a false god. Driving your car to the grocery
store 1 mile away rather than walking is convenient, but it's also
bad for your bank account, your health, and your waistline. Similarly, DI
tools often lead to building weak abstractions and "service soup." Each
individual service may be easy understand on it's own, but the high-level
structure is hard to understand because there's no effective way to group
services together. When everything in your codebase is a "service", the term
"service" becomes meaningless.

How Ramda Helps
===============

Enter ramda's `applySpec()` function. The general idea of `applySpec()` is that,
given an object whose keys are functions and some parameters, it calls each
function in the object and returns a new object whose keys are the return
values of each function. In code,

```javascript
const stringifyMilliseconds = {
  milliseconds: x => `${x}ms`,
  seconds: {
    exact: x => `${x / 1000}s`,
    rounded: x => `${Math.round(x / 1000)}s`
  }
};

/**
 * {
 *   milliseconds: '1234ms',
 *   seconds: { exact: '1.234s', rounded: '1s' }
 * }
 */
require('ramda').applySpec(stringifyMilliseconds)(1234);
```

In other words, `applySpec()` lets you execute a bunch of functions
with the same arguments and organize the return values. How does this help
with DI? Well, let's say that you have a bunch of functions like the user
query you had before.

```javascript
module.exports = {
  getUser: db => function(id) {
    return db.collection('User').findOne({ _id: id });
  },
  updateUser: db => function(id, update) {
    return db.collection('User').updateOne({ _id: id }, { $set: update });
  },
  deleteUser: db => function(id) {
    return db.collection('User').deleteOne({ _id: id }).then(res => {
      if (res.n === 0) {
        throw new Error('User not found');
      }
      return res;
    })
  }
};
```

These functions are nice and DI-friendly: they don't rely on any one mechanism
for initializing the database handle. They also have a couple things in
common: they all take exactly one parameter, a database handle. This makes it
easy to initialize all of them in a single function call with `applySpec()`.

```javascript
co(function * () {
  const db = yield mongodb.MongoClient.connect(process.env.MONGO_URL);
  const User = applySpec(require('./user')(db);
  // User is now a collection of functions that have access to the `db` handle.
  // In other words, you can do 'yield User.getUser(id)'
  return User;
});
```

This way, `User` is now an organized collection of functions and the `db`
dependency is closed over in a way that's transparent to clients using the
`User` module. You can also now think of the functions in `User` as a
distinct group as opposed to a collection of nebulous services.
For example, since these functions all return promises, it's easier to
instrument common error handling:
<commit_msg>fix(ramda_di): correct syntax error re: #22<commit_after>[Ramda](https://www.npmjs.com/package/ramda) is quickly becoming an indispensible part of my node projects. Lodash is more accessible and beginner-friendly, but ramda is far more powerful and expressive once you wrap your mind around it. In this article, I'll take a look at the [`applySpec()` function](http://ramdajs.com/docs/#applySpec) and how it can replace dependency injectors like [wagner](http://npmjs.org/package/wagner-core).

What's Dependency Injection About?
==================================

The fundamental idea of dependency injection is to separate out business logic
and service initialization. For example, let's say you have some code that
runs a query against MongoDB:

```javascript
const mongodb = require('mongodb');

let db;

function getMongo() {
  if (db) {
    return db;
  }
  db = mongodb.MongoClient.connect('mongodb://localhost:27017/test');
  return db;
}

module.exports = function(id) {
  return getMongo().
    then(db => db.collection('User').findOne({ _id: id }));
};
```

Node.js beginners tend to write code that differs only superficially from the
tangled mess above. Little refactors like putting the `getMongo()` function
in a separate file, getting the connection string from environment variables,
and adding error handling don't help the fundamental issue that your query is
tied one-to-one to a mongodb database handle. A better approach is something
like this:

```javascript
module.exports = db => function getUser(id) {
  return db.collection('User').findOne({ _id: id });
};
```

This way, your database handle is completely decoupled from the query you run.
You can initialize one or many database handles and still use the same
business logic.

<img src="http://i.imgur.com/HXKxWYS.png" />

This is particularly important for MongoDB, which
[limits you to one operation per open socket at a time](http://api.mongodb.com/python/current/faq.html#how-does-connection-pooling-work-in-pymongo). In other words, unless you tweak the pool size in the
Node.js driver, MongoDB will only process up to 5 operations in parallel per
database handle.
This can be bad if you have a lot of fast operations queued up behind a few
very slow operations, like if a few heavy queries from your admin dashboard
are blocking user logins. Decoupling initialization from business logic makes
it easy to have separate database handles for queries you expect to be slow.

Where DI Goes Wrong
=========================

Dependency injectors are powerful tools for breaking code up into services
that depend on each other.

```javascript
// db service
function db(config) {
  // initialize db
}

// logger service
function logger() {
  // initialize logger
}

// query service
function queryBuilder(db, config, logger) {
  // initialize query builder
}
```

For example, in the above code, a dependency injector like [wagner](http://npmjs.org/package/wagner-core) would be smart enough to walk
the graph of dependencies and see that in order to initialize `queryBuilder`,
it needs to initialize `config` and `logger` first, then `db`, and then
`queryBuilder`. A DI tool lets you separate logic from initialization in a
convenient way where you don't really have to think about where the services
you depend on come from.

Unfortunately, convenience is a false god. Driving your car to the grocery
store 1 mile away rather than walking is convenient, but it's also
bad for your bank account, your health, and your waistline. Similarly, DI
tools often lead to building weak abstractions and "service soup." Each
individual service may be easy understand on it's own, but the high-level
structure is hard to understand because there's no effective way to group
services together. When everything in your codebase is a "service", the term
"service" becomes meaningless.

How Ramda Helps
===============

Enter ramda's `applySpec()` function. The general idea of `applySpec()` is that,
given an object whose keys are functions and some parameters, it calls each
function in the object and returns a new object whose keys are the return
values of each function. In code,

```javascript
const stringifyMilliseconds = {
  milliseconds: x => `${x}ms`,
  seconds: {
    exact: x => `${x / 1000}s`,
    rounded: x => `${Math.round(x / 1000)}s`
  }
};

/**
 * {
 *   milliseconds: '1234ms',
 *   seconds: { exact: '1.234s', rounded: '1s' }
 * }
 */
require('ramda').applySpec(stringifyMilliseconds)(1234);
```

In other words, `applySpec()` lets you execute a bunch of functions
with the same arguments and organize the return values. How does this help
with DI? Well, let's say that you have a bunch of functions like the user
query you had before.

```javascript
module.exports = {
  getUser: db => function(id) {
    return db.collection('User').findOne({ _id: id });
  },
  updateUser: db => function(id, update) {
    return db.collection('User').updateOne({ _id: id }, { $set: update });
  },
  deleteUser: db => function(id) {
    return db.collection('User').deleteOne({ _id: id }).then(res => {
      if (res.n === 0) {
        throw new Error('User not found');
      }
      return res;
    })
  }
};
```

These functions are nice and DI-friendly: they don't rely on any one mechanism
for initializing the database handle. They also have a couple things in
common: they all take exactly one parameter, a database handle. This makes it
easy to initialize all of them in a single function call with `applySpec()`.

```javascript
co(function * () {
  const db = yield mongodb.MongoClient.connect(process.env.MONGO_URL);
  const User = applySpec(require('./user'))(db);
  // User is now a collection of functions that have access to the `db` handle.
  // In other words, you can do 'yield User.getUser(id)'
  return User;
});
```

This way, `User` is now an organized collection of functions and the `db`
dependency is closed over in a way that's transparent to clients using the
`User` module. You can also now think of the functions in `User` as a
distinct group as opposed to a collection of nebulous services.
For example, since these functions all return promises, it's easier to
instrument common error handling:

{'fraction_non_alphanumeric': 0.1028543705021428, 'fraction_numerical': 0.004851621250101076, 'mean_word_length': 4.299057412167952, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 23, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '793116', 'n_tokens_mistral': 3469, 'n_tokens_neox': 3283, 'n_words': 1613}