Skip to content
This repository has been archived by the owner on Nov 17, 2017. It is now read-only.

Adding memoization #20

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Adding memoization #20

wants to merge 5 commits into from

Conversation

yachaka
Copy link

@yachaka yachaka commented Sep 28, 2016

Hello,

This is the PR discussed in issue #10.
API follows what has been discussed : { memoized: true } to activate memoization.

I've been rather busy so I just added few tests and the overall code could be refactored in some points somehow but it does the job.

Needs the docs to be partly rewritten though to explain how to use/the advantages of using memoization.

Note: the cache is for now global. In the future. It would be necessary to take is as argument to allow the user to maintain a cache for each set of entity he might be using.

@gpbl
Copy link
Owner

gpbl commented Sep 28, 2016

Awesome @yachaka, thanks a lot! Please give some some time to review the changes.

@gpbl
Copy link
Owner

gpbl commented Oct 4, 2016

@yachaka Thanks again for your PR. Yes, I see the need of a refactoring to avoid some code dupes... If you are have some time for a code review, we can work on this together, otherwise I can try updating the branch myself in the next days.

@gpbl gpbl mentioned this pull request Oct 4, 2016
@clessg
Copy link

clessg commented Oct 4, 2016

@yachaka Thanks for this. Really looking forward to this change. 🎉

@yachaka
Copy link
Author

yachaka commented Oct 4, 2016

@clessg Thanks for the feedback ! Really appreciated ! :)
@gpbl I can look onto refactoring a little next week, if no one does !

@gpbl
Copy link
Owner

gpbl commented Oct 9, 2016

Sorry, I've broken the PR fixing up some code in master :( The valuable code is still visible and reusable from the diff.

Copy link
Owner

@gpbl gpbl left a comment

Choose a reason for hiding this comment

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

Added some ideas for refactoring 😄

@@ -1,3 +1,3 @@
{
"presets": ["es2015"]
"presets": ["es2015", "stage-0"]
Copy link
Owner

@gpbl gpbl Oct 9, 2016

Choose a reason for hiding this comment

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

I don't think we need stage-0 💣 :)

Copy link
Author

Choose a reason for hiding this comment

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

Sure ! I don't remember why I added it, I used ...spread at some point I think.

@@ -6,6 +6,7 @@
"scripts": {
"prebuild": "rimraf dist lib",
"build": "babel src --out-dir lib",
"mocha": "mocha --compilers js:babel-core/register",
"test": "mocha --compilers js:babel-core/register --recursive",
Copy link
Owner

@gpbl gpbl Oct 9, 2016

Choose a reason for hiding this comment

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

thanks! indeed, no need to --recursive here!

@@ -1,12 +1,14 @@
import IterableSchema from 'normalizr/lib/IterableSchema';
import EntitySchema from 'normalizr/lib/EntitySchema';
import UnionSchema from 'normalizr/lib/UnionSchema';
import assign from 'lodash/assign';
Copy link
Owner

@gpbl gpbl Oct 9, 2016

Choose a reason for hiding this comment

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

Does babel.js transpile Object.assign()? 🤔
Because I'd better use it than adding another dependency.

Copy link
Author

Choose a reason for hiding this comment

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

No, you need a polyfill ! But that's probably better to include one than using a library function.

@@ -21,13 +23,15 @@ function resolveEntityOrId(entityOrId, entities, schema) {

if (isObject(entityOrId)) {
id = getIn(entity, [schema.getIdAttribute()])
entity = getIn(entities, [key, id])
} else {
entity = getIn(entities, [key, id])
}

Copy link
Owner

Choose a reason for hiding this comment

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

Alright! Then I'd better write

  if (isObject(entityOrId)) {
    id = getIn(entity, [schema.getIdAttribute()]);
  }
  entity = getIn(entities, [key, id]);

const itemSchema = schema.getItemSchema();
function denormalizeUnion(entity, entities, unionSchema, bag) {
if (!entity.schema)
throw new Error('Expect `entity` to have a schema key as a result from normalizing an union.')
Copy link
Owner

@gpbl gpbl Oct 9, 2016

Choose a reason for hiding this comment

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

Is this change related to the memoization? 🐺

Copy link
Author

Choose a reason for hiding this comment

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

I don't think so, I think I just wanted to simplify the function.

@@ -50,14 +73,40 @@ function denormalizeIterable(items, entities, schema, bag) {
* @param {object} bag
* @returns {object|Immutable.Map}
*/
function denormalizeUnion(entity, entities, schema, bag) {
const itemSchema = schema.getItemSchema();
function denormalizeUnion(entity, entities, unionSchema, bag) {
Copy link
Owner

Choose a reason for hiding this comment

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

We should add an options parameter here:

denormalizeUnion(entity, entities, unionSchema, options bag)

and pass it some lines below to denormalize(...

if (cache[key][id].entity !== entity) {
cache[key][id].entity = entity
cache[key][id].denormalized = entity
}
Copy link
Owner

@gpbl gpbl Oct 9, 2016

Choose a reason for hiding this comment

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

Caching should be abstracted in another file (e.g. cache.js) to simplify this part and to not expose the caching mechanism here. Something like cache.get(), cache.set(), cache.clean().

Copy link
Author

Choose a reason for hiding this comment

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

Sure ! Feel free to export this part of the code.

let referenceObject = cache[key][id].denormalized
let relationsToUpdate = {}

/* For each relation in EntitySchema */
Copy link
Owner

@gpbl gpbl Oct 9, 2016

Choose a reason for hiding this comment

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

I'm not sure what do you mean with relation. Is that an entity?

Anyway, here I'm lost 😄 I don't fully understand the reasoning behind the code below...

Copy link
Author

Choose a reason for hiding this comment

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

relation is a relation in the entity Schema, which are Entities, yes 👍 . I fill this object with all the relations that have been updated - in the loop below - because we want to return the original object if nothing changed !

/**
* Same as denormalize right above, but memoized
*/
function denormalizeMemoized(obj, entities, schema, bag = {}) {
Copy link
Owner

@gpbl gpbl Oct 9, 2016

Choose a reason for hiding this comment

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

If we add an options argument here, we could skip the various denormalize*Memoized() functions and merge their code into the existing denormalize*():

export function denormalize(obj, entities, schema, options, bag = {}) {
  if (obj === null || typeof obj === 'undefined' || !isObject(schema)) {
    return obj;
  }

  if (schema instanceof EntitySchema) {
    return denormalizeEntity(obj, entities, schema, options, bag);
  } else if (schema instanceof IterableSchema) {
    return denormalizeIterable(obj, entities, schema, options, bag);
  } else if (schema instanceof UnionSchema) {
    return denormalizeUnion(obj, entities, schema, options, bag);
  }
  // Ensure we don't mutate it non-immutable objects
  const entity = isImmutable(obj) ? obj : merge({}, obj);
  return denormalizeObject(entity, entities, schema, options, bag);
}

Copy link
Author

Choose a reason for hiding this comment

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

Sure !

return denormalize(
Object.assign({}, entity, { [entity.schema]: entity.id }),
trueEntity,
Copy link
Owner

Choose a reason for hiding this comment

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

Why do we need the trueEntity? I think a code comment here would be helpful.

@yachaka
Copy link
Author

yachaka commented Oct 10, 2016

I will be able to look into refactoring along your comments this friday. ;)

@diegohaz
Copy link

Hey guys. Any updates on this?

@yachaka
Copy link
Author

yachaka commented Nov 29, 2016

@diegohaz I've been a lot busy lately (just started a new job), but I will into refactoring this week (promess 😸). For now, you can use my fork (yachaka/denormalizr) ; it works with memoization using { memoized: true } as the first argument.

@coveralls
Copy link

Coverage Status

Coverage decreased (-8.4%) to 91.603% when pulling 8238d50 on yachaka:master into 08ede86 on gpbl:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-8.4%) to 91.603% when pulling ee2d493 on yachaka:master into 08ede86 on gpbl:master.

@yachaka
Copy link
Author

yachaka commented Dec 10, 2016

@gpbl Updated README, added some code comment. I didn't refactor the denormalize entry point function ; I think it is more readable this way - splitted into 2 functions. In term of performance, it also eliminates a check. Please review 👍

@coveralls
Copy link

Coverage Status

Coverage decreased (-8.4%) to 91.603% when pulling 95b7283 on yachaka:master into 08ede86 on gpbl:master.

@yachaka
Copy link
Author

yachaka commented Jan 26, 2017

@gpbl Sup ?

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.

5 participants