node-task / spec

A specification for javascript tasks.
MIT License
153 stars 2 forks source link

Debate #4

Closed satazor closed 11 years ago

satazor commented 11 years ago

Braindump after reading the spec. I will only point out the things that feel rather strange or I disagree:

tkellen commented 11 years ago

Hey André, thanks for the feedback!

satazor commented 11 years ago

I view config as configuring the task as a whole. I can agree with getting rid of the options inside config, as that is a holdover from grunt's current configuration format. However, the spec needs to state that files is a reserved key which must hold a valid file listing as shown in the examples in the README. Assuming we do this, the only difference we have is that you want to call config options, yes?

Yep

The parseConfig method provides a clear separation of responsibilities in the lifecycle of a task, and it makes testing easy. Additionally, because node-task is written in a functional style, processing options in setup would require the setup method to returned the parsed config, which feels wrong to me.

The more things the spec has, the more confusing users get. I think that, besides testing, we could merge both into only setup. The setup does not need to return the options if users would be manipulating the options passed. Other way around, which I think it's better, is if the spec actually provide a way to define the task optons. Then, task runners will guarantee to fill in the default options into the initial options object. This way, users will rarely need to implement the setup method unless they want to modify or infer new ones (therefor the parseConfig could be deleted). This would also be good for task runners to present options in the task usages (cli).

I'm not sure I follow what you mean when you say you don't like how tasks are declared to be async. There is no declaration that a task is async. The only interface between task runners and tasks is the run method, which must return a value or a promise if the execution is async. It is up to the task runner to decide if it waits for the promise to resolve before kicking off another task, not the task itself.

What I'm saying is that we should follow node conventions. Passing callbacks in node is more popular than promises. Promises have advantages, but in this case they aren't needed (imo). Having in mind the task runners only know about the run method, I would make the run signature like this run: function (opts, next) always. For the task runner, the run is always async, but could be in fact sync.

The spec doesn't state how files should be read or written, it only specifies the method names which should be used when doing so.

So the actual spec defines things like readFile and writeFile. I'm not sure if those file only attributes should be part of the (base) spec. I see them a particular case of the spec, hence the suggestion about an extended spec.

tkellen commented 11 years ago

Boiling awful code like this: https://github.com/gruntjs/grunt-contrib-less/blob/master/tasks/less.js

...into something like this:

var Task = require('task');
var less = require('less');
var task = Task.create({
  name: 'less',
  description: 'example for less compilation',
  filterRead: function (config, input, filepath) {
    var defer = Task.defer(); // this is just a proxy to when.defer();
    var parser = new less.Parser(config.parse);
    parser.parse(input, function(err, tree) {
      defer = tree.toCSS(config.render);
    });
    return defer;
  }
});

Is a huge win for task writers and the maintainers of task runners. If an exception happens anywhere in this code it bubbles up to task.exception.

tkellen commented 11 years ago

Also, with regards to promises, my implementation of node-task makes it so task writers can return a value or a promise from literally any method defined in the spec and the run method will just work, resolving correctly or throwing an exception if needed.

This stands in stark contrast to passing callbacks, which would make filterRead and filterWrite (the primary interface most task-writers will use to process files) considerably more complex and error prone.

The practice of passing callbacks generates a sea of accidental complexity that has nothing to do with the problem you are trying to solve.

satazor commented 11 years ago

I've now understood the spec, and I was confusing the spec with the task implementation itself. I will start a new topic soon.

sindresorhus commented 11 years ago

:+1: on promises