Architecture: are utils good for repeatable logic?


#1

I would like to keep my routers free from logic as much as possible. Have been looking at utils as a way to get there, but don’t see much documentation about them in the guides.

Let’s say, for an article kept in the store, I need to know the next article and the one that came before it. I don’t want to put it in a component, because I may need it in another one. I don’t want to stick it in my router, because eventually it’s going to violate DRY.

If not a util, where is the best place to stick repeatable logic?


#2

Take a look at Mixins, they may be what you are after. Sadly, we don’t have much info in the guides about them (yet). We’re working on it slowly, just haven’t gotten there yet.


#3

Mixins are very much an anti-pattern. I wouldn’t recommend their use. The reason is because Mixin properties end up on the host class and thus violates encapsulation. It is much better to encapsulate the repeatable logic under a new class and compose it under the host class. This way the properties are encapsulated and would not contaminate with other properties that may share the same name.

Also…

  1. Don’t be afraid to create utility controller/service.
  2. Don’t be afraid to create your own type (as in app/<your-type>/<your-class>.js) You can import any class that sits inside the project.
  3. Always consider components, even if it doesn’t render anything.

#4

This is really valuable feedback, thank you for the replies.

I have been using mixins for repeatable tasks, like scrolling to the top of the window on route changes and applying body tags. The reason I didn’t think about mixins for this use case is the fact routes and controllers can inherit their hooks, it implied they are more for modifying the state of other objects than acting as isolated agents.

If I understand utilities correctly, they are completely isolated from other objects. Is this correct?


#5

My problem with utilities is that I can’t stub/mock them. I have to create an alias just for the purpose of making it stub-able. So I avoided them as much as possible. At the moment, I still don’t know how to directly stub/mock them. :disappointed:


#6

Hm, I don’t understand. Can you make an example?


#7

Services are good place for such these things. Services are singleton and you should consider it


#8

For example, to use util, we import it like this:

import Route from 'ember-route';
import someUtil from 'app/utils/some-util.js';

export default Route.extend({
});

Using sinon.js, I’m not sure how I can stub that someUtil() method since it’s not inside the Route object. So as a workaround, I create an alias that just calls someUtil() so I can stub it. Like this:

import Route from 'ember-route';
import someUtil from 'app/utils/some-util.js';

export default Route.extend({
  someUtilAlias() {
    someUtil();
  }
});

Now in my unit test, I can simply stub the alias:

import sinon from 'sinon';

test('it works', function(assert) {
  let stub = sinon.stub().returns(true);
  let route = this.subject({someUtilAlias: stub});

  assert.ok(stub);
});

#9

@rmmpaderes why do you need to stub the util function? Utils should be pure/stateless functions so that there’s no reason to stub them in a test environment. If the util is stateful (e.g. it closes over some value, or issues an ajax call), or is impure (i.e. it references something in its parent scope), it should probably be rewritten to have all stateful and impure variables passed in as arguments, thus making the utility function itself stateless/pure.

@techsoldaten based on the above, I’d recommend you do use util functions if you can make them stateless (usually not hard to do). In the usecase you mentioned–getting the article before and after a specified article–this could be done by defining a function along the lines of:

// pseudocode...
getNeighboringArticles(store, articleId) {
  return [
    store.findRecord('article', articleId - 1),
    store.findRecord('article', articleId + 1)
  ];
}

You could also do the same thing with a service, which relaxes the requirement around being stateless.


#10

Sure, mixins can be an anti-pattern if used incorrectly, but many OOP languages have mixin-like features, which could be implemented as multiple inheritance, or something like traits. There’s nothing wrong per se with mixins. The whole point of mixins is that I don’t want to encapsulate–I do want additional properties/logic in the object I am mixing in to. Of course, mixin property names should be chosen so that they won’t conflict; so if I have a mixin called MyMixin, instead of giving it a property count, I would give the property the name myMixinCount. Behaviors such as scrolling, for example, are eminently good candidates for mixins. Of course, if something can be implemented cleanly as a utility or service, then sure that’s preferable.

In any OOP language, we always have the choice of adding functionality to an object by inheritance, by whatever mixin-like capability it provides, or by “composition” (holding a sub-object as a property on an object). Which to choose is not usually black and white.