Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aravind/skip unchanged encode #153

Merged
merged 3 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion scripts/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ export default async function version(str) {
try {
const { stdout } = await git('tag');

console.log(stdout);

const [major = 0, minor = 0, patch = 0, pre = '', number = 0] = stdout
.split('\n')
.reduce((latest, vstring) => {
const version = vstring
.split(/[.-]/)
.map((seg) => (Number.isNaN(seg) ? seg : parseInt(seg)));
.map((seg, i) => (i === 3 ? seg : parseInt(seg)));

console.log('latest', latest, vstring, version);

for (let i = 0; i < 5; i++) {
const atPre = i === 3;
Expand All @@ -32,6 +36,8 @@ export default async function version(str) {
return latest;
}, []);

console.log({ major, minor, patch, pre, number });

switch (str) {
case 'major':
return `${major + 1}.0.0`;
Expand Down
6 changes: 5 additions & 1 deletion src/core/Graffy.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import {
} from '@graffy/common';
import { makeStream, mapStream } from '@graffy/stream';
import Core from './Core.js';
import { shiftGen, wrapProvider } from './shift.js';
import { shiftGen, unchanged, wrapProvider } from './shift.js';
import { validateCall, validateOn } from './validate.js';

export { unchanged } from './shift.js';

export default class Graffy {
constructor(path = [], core = new Core()) {
this.core = core;
Expand Down Expand Up @@ -104,3 +106,5 @@ export default class Graffy {
return unwrapObject(decodeGraph(writtenChange), path);
}
}

Graffy.unchanged = unchanged;
57 changes: 36 additions & 21 deletions src/core/shift.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,34 @@ async function mapStream(stream, fn) {
}
}

export const unchanged = Symbol('Payload or result unchanged by handler');

export function wrapProvider(fn, decodedPath, isRead) {
const decodePayload = isRead ? decodeQuery : decodeGraph;
const encodePayload = isRead ? encodeQuery : encodeGraph;
const path = encodePath(decodedPath);
return async function wrappedProvider(payload, options, next) {
let nextCalled = false;
let nextResult;
let remainingNextResult;
const porcelainPayload = unwrapObject(decodePayload(payload), decodedPath);
const remainingPayload = remove(payload, path) || [];

// This next function is offered to the provider function.
async function shiftedNext(porcelainNextPayload, nextOptions) {
nextCalled = true;
const nextPayload = encodePayload(
wrapObject(porcelainNextPayload, decodedPath),
);
if (remainingPayload.length) merge(nextPayload, remainingPayload);
const nextResult = await next(nextPayload, nextOptions);

let nextPayload;
if (porcelainNextPayload === unchanged) {
nextPayload = payload;
} else {
nextPayload = encodePayload(
wrapObject(porcelainNextPayload, decodedPath),
);
if (remainingPayload.length) merge(nextPayload, remainingPayload);
}

nextResult = await next(nextPayload, nextOptions);

// Remember the next() results that are not returned to this provider.
// These will be merged into the result later.
Expand All @@ -47,24 +57,29 @@ export function wrapProvider(fn, decodedPath, isRead) {
}

const porcelainResult = await fn(porcelainPayload, options, shiftedNext);
let result = encodeGraph(wrapObject(porcelainResult, decodedPath));
// console.log(result);

// TODO: Get rid of this special handling by requiring read providers to
// finalize results themselves.
if (isRead && !nextCalled) {
// This does the opposite of "remove"; "keep"?
const appliedQuery = wrap(unwrap(payload, path), path);
result = finalize(result, appliedQuery);
result = wrap(unwrap(result, path), path);
}

if (!nextCalled && remainingPayload.length) {
remainingNextResult = await next(remainingPayload);
}
let result;
if (porcelainResult === unchanged) {
result = nextResult;
} else {
result = encodeGraph(wrapObject(porcelainResult, decodedPath));

// TODO: Get rid of this special handling by requiring read providers to
// finalize results themselves.
if (isRead && !nextCalled) {
// This does the opposite of "remove"; "keep"?
const appliedQuery = wrap(unwrap(payload, path), path);
result = finalize(result, appliedQuery);
result = wrap(unwrap(result, path), path);
}

if (remainingNextResult?.length) {
merge(result, remainingNextResult);
if (!nextCalled && remainingPayload.length) {
remainingNextResult = await next(remainingPayload);
}

if (remainingNextResult?.length) {
merge(result, remainingNextResult);
}
}

// console.log('Shifted', path, format(payload), format(result));
Expand Down
60 changes: 59 additions & 1 deletion src/core/test/porcelain.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import GraffyFill from '@graffy/fill';
import { page, ref } from '@graffy/testing';
import { jest } from '@jest/globals';
import Graffy from '../Graffy.js';
import Graffy, { unchanged } from '../Graffy.js';

test('Porcelain read', async () => {
const store = new Graffy();
Expand Down Expand Up @@ -417,3 +417,61 @@ test('modified_next_options', async () => {
await store.read('user', query, { foo: 2 });
expect(mockOnRead).toBeCalledWith(query, { bar: true }, expect.any(Function));
});

describe('unchanged', () => {
let store;

const originalQuery = { foo: true };
const changedQuery = { foo: true, bar: true };
const originalChange = { foo: 5 };
const changedChange = { foo: 8, bar: 6 };
const originalResult = { foo: 10 };
const changedResult = { foo: 8 };

const cases = [
[false, false],
[false, true],
[true, false],
[true, true],
];

beforeEach(() => {
store = new Graffy();
});

test.each(cases)(
'read nextChanged:%j retChanged:%j',
async (nextChanged, retChanged) => {
store.onRead('example', async (_query, _options, next) => {
await next(nextChanged ? changedQuery : unchanged);
return retChanged ? changedResult : unchanged;
});

store.onRead('example', async (query, _options) => {
expect(query).toEqual(nextChanged ? changedQuery : originalQuery);
return originalResult;
});

const result = await store.read('example', originalQuery);
expect(result).toEqual(retChanged ? changedResult : originalResult);
},
);

test.each(cases)(
'write nextChanged:%j retChanged:%j',
async (nextChanged, retChanged) => {
store.onWrite('example', async (_change, _options, next) => {
await next(nextChanged ? changedChange : unchanged);
return retChanged ? changedResult : unchanged;
});

store.onWrite('example', async (change, _options) => {
expect(change).toEqual(nextChanged ? changedChange : originalChange);
return originalResult;
});

const result = await store.write('example', originalChange);
expect(result).toEqual(retChanged ? changedResult : originalResult);
},
);
});
Loading