2

I was reading Nats-Jetstream documentation that I encountered the following code:

var createModel = require('jetstream').model;

var Shape = createModel('Shape', function() {
    this.has('x', Number);
    this.has('y', Number);
    this.has('width', Number);
    this.has('height', Number);
    this.has('type', Number);
});

I am not sure what does this.has mean? Where does the has come from? How does this code work?

user20551429
  • 99
  • 1
  • 6

2 Answers2

3

The shape of the library is described in the docs. It has:

type JetStream : {
    model: (name: String, fn: ModelDefiner) => Model,

So the callback is a ModelDefiner, which is defined here:

type ModelDefiner : (this: Model) => void

So this is a Model, which is defined here:

type ModelObject : {
    has: (propertyName: String, PropertyType) => void,
    setScope: (scope: Scope, Callback<Error, void>) => void,
    getValues: () => Object,
    getAddSyncFragment: () => SyncFragment<"add">
}

-- A Model instance inherits from ModelObject.
type Model : ModelObject & {
    typeName: String,
    uuid: ObjectUUID,
    scope: Scope | null
}

As you can see above, a Model extends a ModelObject, and a ModelObject has a has method.

If you're curious how you would write code that could do this yourself, all you really need is to create an object with a has method, and .call the callback with it.

class Model {
  has(str, type) {
    console.log('has called with', str, type);
  };
}
const createModel = (str, callback) => {
  const model = new Model();
  callback.call(model);
};

var Shape = createModel('Shape', function() {
    this.has('x', Number);
    this.has('y', Number);
    this.has('width', Number);
    this.has('height', Number);
    this.has('type', Number);
});
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
1

When curious, it's always good to look at the source, which they've made available. First thing to check since this is a node project would be the package.json -- main is "index". All the exports are going to be defined in libs/index.js.

Looking at libs/index.js line 52, we see where model is defined as an export -- it's a function where you pass in a name and definition that get passed to ModelObject.model; if you scroll back up, you can see that that's included from libs/model_object.js.

Going over to libs/model_object, you can see what the passed definitions field (the lambda function you see in the example code) is used for: definitions.call(typeClass). typeClass is created earlier in the function. If you're unfamiliar with what call does, it lets a function be executed in another object -- the this you see in the passed function is going to refer to the typeClass object. Inside of libs/model_object.js, a has function is defined that adds ModelObjectProperties; this.has is being used to add properties to the ModelObject.

edit: CertainPerformance's answer is probably more clear, refer to that.

ZTKlein
  • 31
  • 7
  • Your answer was also great! But I still can't understand the syntax. For example `model: function(name, definition) { return ModelObject.model(name, definition); }` is kinda weird to me. I can't understand `model : ....` part. This is the first time I see (a word), then (colon), then *a function definition). – user20551429 Dec 05 '22 at 04:49
  • What do words before `:` do? It's like a label. – user20551429 Dec 05 '22 at 04:50
  • 1
    It's defining a map of named exports; model is the key, and the part after the semicolon is what you're getting. `require('jetstream')` gives you access to the module.exports, and then specifying `require('jetstream').model` means you're getting the export named `model` -- in this case it means that after running `var createModel = require('jetstream').model;`, createModel is now referring to that function after the semicolon. Does that make sense? – ZTKlein Dec 05 '22 at 04:58