Skip to content
This repository has been archived by the owner on Aug 11, 2021. It is now read-only.

feat: update DAGLink and DAGNode to have an immutable API #6

Merged
merged 13 commits into from
Nov 24, 2016

Conversation

daviddias
Copy link
Member

@daviddias daviddias commented Nov 14, 2016

WIP

@dignifiedquire @victorbjelkholm @haadcode did a push on this so that we can get this final API stable, while the API for IPLD Formats are still hot, I'm afraid that if we give it a lot more time, people will start using all of that async ugliness because of the lack of immutability here and then it becomes harder to change.

I'm finishing this up, but API is there, would love your review ASAP

  • Agree with the API
  • Update remaining tests
  • Update README

note: This PR also cuts the umbilical chord to the merkleDAG version in Go, which created a funky api in JS

@daviddias daviddias self-assigned this Nov 14, 2016
Copy link
Member

@dignifiedquire dignifiedquire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to keep a toJSON method, to make it more consistent rather than a property .json. For both node and link

get () {
return data
},
set: immutableError
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make this nicer as you are already using class

class Hello {

  get myImmutableProp () {
    return this._secretProp
  }

  set myImmutableProp () {
    throw new Error('fail')
  }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, that is what I did, but defined the function above so that I could reuse it

Copy link
Member Author

@daviddias daviddias Nov 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, nevermind, I got what you meant now :) and agreed 👍

Name: this.name,
Size: this.size,
Hash: mh.toB58String(this.hash)
this.json = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be defined as immutable as the props in DagNode

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed 👍

@daviddias
Copy link
Member Author

I think it would be good to keep a toJSON method, to make it more consistent rather than a property .json. For both node and link

I tend to agree, will update

@haadcode
Copy link
Contributor

I have quite a few comments for this, but can't review it atm. Will get back to this asap.

// 3. create new node
}
}
callback(new Error('invalid arguments'))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daviddias
Copy link
Member Author

All right, this is ready to be reviewed so that I can go forward and bubble up updates across the other repos.

Big wins from this PR:

  • Got a tons of feedback from @haadcode
  • The whole dagPB became more functional
  • Code structure improved, smaller files with clear goals
  • Better test coverage
  • Documentation was also improved
  • Code overall improved a ton

@haadcode @dignifiedquire @victorbjelkholm would like a final 👍 so that I can move forward.

Thank you!

exports.DAGLink = require('./dag-link.js')
exports.resolver = require('./resolver.js')
exports.util = require('./util.js')
exports.DAGNode = require('./dag-node')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will make const d = new DAGNode() possible for users. I thought we didn't want that to be exposed directly so that users are forced to user .create and have no confusion as to how to create DAGNodes.

exports.resolver = require('./resolver.js')
exports.util = require('./util.js')
exports.DAGNode = require('./dag-node')
exports.DAGLink = require('./dag-link')

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, this will expose DAGLink to the consumers.

const dagNode = new DAGNode(buf, links)

callback(null, dagNode)
DAGNode.create(buf, links, callback)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@diasdavid we talked about the IPLD format API and this is pretty much what I meant how it forces sub-optimal code: deserialize depends on DAGNode and DAGNode depends on serialize (serialize and deserialize being both in the same file) essentially creating a cyclic dependency between the two. For the sake of getting this shipped, let's not focus on that but keep this in mind for future discussions.

Copy link
Member Author

@daviddias daviddias Nov 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could break util into separate files for each function, avoiding that 'cyclic' dependency, do note that in CJS, modules loaded are cached, solving this potential issue without any intervention.

Nevertheless, this is not a IPLD Format issue, if an issue at all, is just because we want the DAGNode instances to be immutable and easy to work with and so, we need the serialize version upon its creation.

@haadcode
Copy link
Contributor

Couple of small comments, otherwise LGTM!

Create a DAGNode.

```JavaScript
DAGNode.create("data", links, (err, dagNode) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quotes

#### Create a DAGNode

```JavaScript
// TODO
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: please fill or remove before merge

#### Add and remove a Link

```JavaScript
// TODO
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: please fill or remove before merge

const DAGNode = dagPB.DAGNode
```

#### DAGNode.create(data, links, hashAlg, callback)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document the argument types

}
```

#### addLink(node, link, callback)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document the argument types

}

return new DAGLink(l.name || l.Name,
l.size || l.Size,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation is off

}

return data
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be simplified to

function cloneData (dagNode) {
  assert(Buffer.isBuffer(dagNode.data), 'Not a valid dagnode')
  
  const data = new Buffer(dagNode.data.length)
  dagNode.data.copy(data)
  return data
}

}

function cloneLinks (dagNode) {
return dagNode.links.length > 0 ? dagNode.links.slice() : []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for the length check, [].slice() === []

}

function linkSort (a, b) {
return (new Buffer(a.name || '', 'ascii').compare(new Buffer(b.name || '', 'ascii')))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is super wasteful, the link names should be converted to buffers once and then sorted, not on every sorting call.

}

exports.serialize = serialize
exports.deserialize = deserialize
exports.cid = cid
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👎 on the whole util interface, it seems arbitrary and not very useful. If these methods are needed why are they not exposed at the top level?

Copy link
Member Author

@daviddias daviddias Nov 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dignifiedquire go through https://github.com/ipld/interface-ipld-format and ipld/js-ipld#60 (comment)

tl;dr; it is what the IPLD Resolver needs and what was defined on interface-ipld-format

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay :/

@daviddias daviddias merged commit 44c86a4 into master Nov 24, 2016
@daviddias daviddias deleted the feat/immutable branch November 24, 2016 12:20
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants