ESLint plugin: code pureness

Hi JS (ok, ES) people!

Working on current project (Redux + React, ES6) we faced the issue with code pureness.

Say if the method receives all the variable data through parameters and there is no variable data injection from the outside, the method is pure. So if we run the method with similar parameters 1000 times in different environments, we get same result.

<nerd-mode>
Constants could be used by that method; it’s ok. The principle is, constants could reside inside the method, however we prefer to inject ’em because of architecture and code reusability.
</nerd-mode>

Why do we need the code to be pure?

  1. Replay-ability. Imagine you have initial Store state “A”, and after 20 consequent Actions it becomes state “B.” Imagine you recorded all the Action with their parameters. If you reset the Store to “A” and replay that Action sequence, you expect the Store state to be “B”.
    This won’t happen if the Store becomes data from different sources, not only from Actions.
  2. Testability: if the method is pure, you can cover it by unit-tests with no need to mock any data.
  3. Data trace-ability, or easiness to debug the code. If you have one source for data appearing in the Store, it’s easier to debug the code.

Some examples

distort() {
  this.x += Math.random() * 10 - 5;
  this.y += Math.random() * 10 - 5;
}

The method above is not pure. Math.random() brings variable data from the outer world. How should you unit-test it? Are you gonna mock the Math.random()?


distort(dx, dy) {
  this.x += dx;
  this.y += dy;
}

// later in another module
function _getRandom() {
  return Math.random() * 10 - 5;
}

ball.distort(_getRandom(), _getRandom());

We made the method pure. Now we can set the ball coords to {20, 20}, call ball.distort(3, -2) and be sure that ball coords are {23, 18}. Sounds like good scenario for unit tests! This could also help in debugging.

The ESLint plugin

Hope you are already aware about JS/ES code linting. Briefly, it’s the script you run against your *.js or *.es file and it reports if something does not conform rules you’ve set up. For instance, ESLint can report that line 12 contains 9 spaces but we expect 8 spaces there.

The ESLint utility supports many rules by itself and you can extend the list by adding plugins.

I created the one for verifying code pureness.
You can set up rules that forbid

  • using this or that expressions (standard like Math.random() or project-specific),
  • invoking new ...(),
  • importing several modules.

All the rules could be applied to different (configurable) files.

Hope it helps in bringing the light to your application architecture.


Feel free to use, contribute and raise issues on GitHub.

Advertisements

The life without the jQuery

The most used framework

Ask any web developer which JS library is the most used. Bet the answer will be “jQuery”. More than 700k questions on StackOverflow, 38+k stars on GitHub, they say for itself.

Moreover, a lot of ‘so called web developers’ know jQuery better than native JS. I don’t judge ’em, jQuery provides really convenient way of handling the content of the page.

Continue reading

Promises as architecture

Hi there,

recently I’ve got an interesting issue which could be resolved by making several consequent async actions. Say,

  • you ask the back end if the user is logged in (GET /api/me),
  • if they are not, you ask Facebook API if the user is logged in there, (FB.getLoginStatus(callback)),
  • then, if they are logged into Facebook, you try to log in ’em into your app (POST /api/authenticate),
  • after receiving truthy response, you redo the GET /api/me and eventually render the user section.
  • Since we live in real world and shit happens, any step can return error, so we should show appropriate message and discontinue.

Possible solutions

The very obvious solution, invoking next action inside the callback of previous one, quickly leads to the callback hell. We don’t want horizontal scrollbar right?

I though also about async.js, it has pretty rich interface for handling async things. I remember using it 2 years ago. Is there something new in the industry?

Captain Promise comes to rescue

Since Promises are more or less standardized and are supported pretty well, I decided to go with this solution.

Architectural background

  1. If you have the Promise, you can attach .then(onResolve, onReject) to it.
  2. .then() returns Promise as well, so it’s chainable (remember jQuery chains?).
  3. Both onResolve and onReject can return either sync values, or Promises. Second means that next .then() happens after that Promise is resolved or rejected.Two important outcomes here:
    • we can mix sync and async actios in adjacent .then() calls; they will be invoked in proper order;
    • we don’t need wrap syns results in Promise.resolve() inside of .then() callbacks. Just return ’em.
  4. If .then() went through onRejected scenario, it returns Promise anyway and invokes next .then(). So rejecting the Promise in case of error is not the most convenient way. We’ll talk about this later.
  5. Don’t forget to add trailing .catch() other wise you spend a glory night from Friday to Monday debugging.That .catch() is the final destination for all uncaught exception happened anywhere in the .then() chain.

Let’s go!

First, make sure all your async things return new Promise():

export default const Ajax = {
  get (url) {
    return new Promise((resolve) => {
      // here the Ajax request goes
      const xhr = new XMLHTTPRequest();
      xhr.onreadystatehange = () => {
        if (xhr.readyState === 4 && xhr.status === 200) {
          resolve(JSON.parse(xhr.responseText));
        }
      }
      // ...all the rest that we need to fulfill the AJAX call
    });
  }
}

Something like that.

Then let’s negotiate the inter-Promise data protocol. Say, each Promise

  • resolves data only and does not got through the reject scenario. Why? First, we are too lazy to write onRejected on every .then(). Moreover, if this Promise was rejected, the next .then() happens anyway. So why use extra scenario?
  • resolves data as{error: Boolean, data: *|null, message: String, debug: *|null}

    Say in normal case we return

    {error: false, data: /* payload */},

    otherwise

    {error: true, message: 'Not loggen in', debug: /* network response */}.

    That lets us always go through the resolve scenario but have exhaustive answer about what happened and when.

Finally, the code

const DO_NOTHING = 'do-nothing';

Ajax.get('/api/me') // remember, this returns {Promise}, right?
// is user logged into the app?
.then((response) => {
  if (response.error) {
    return response;
  } else if (response.data.id) { // user is logged in
    response[DO_NOTHING] = true;
    return response;
  } else {
    // this is the Facebook's FB.getLoginStatus(callback)
    // but wrapped in Promise
    return FbAdapter.getLoginStatus();
  }
})

// is user logged into Facebook?
.then((response) => {
  if (response.error || response[DO_NOTHING]) {
    return response; // pass the data further
  } else if (response.status !== 'connected') {
    // not logged into Facebook
    return {
      error: true,
      message: 'Not logged into Facebook',
      debug: response
    };
  } else {
    // logged into Facebook; try to log into the app
    return Ajax.post('/api/authenticate', {
      id: response.authResponse.userID,
      token: response.authResponse.accessToken
    });
  }
});

// tried to authenticate the Facebook user into the app
.then((response) => {
  if (response.error || response[DO_NOTHING]) {
    return response; // pass the data further
  } else if (response.status !== 'logged-in') {
    // we don't expect this to happen but who knows...
    return {
      error: true,
      message: 'Facebook userID/authToken do not match?',
      debug: response
    };
  } else {
    return Ajax.get('/api/me');
  }
});

// finally pass the result to the Renderer
.then((response) => {
  if (response.data.id || response[DO_NOTHING]) {
    // ok, render the user section in normal way
    renderUser(response.data);
  } else {
    // something went wrong.
    // Inform the user and the developer separately
    console.log(response.debug);
    renderErrorMessage(response.message);
  }
})

.catch((error) => {
  // the exception happened in any `.then()`
  console.log(err);
});

So you see the code looks vertical and pretty straightforward, however it is asynchronous. It’s a bit long, but that’s unavoidable ― you must handle possible errors.
You can also see that all the code about rendering resides in one place: at very last .then() call. That separates receiving the data and using it.
By the way, you might find more elegant way to pass data from the first .then() to the last one as response[DO_NOTHING] 🙂

May the force be with you, Javascript warrior!