-
-
Notifications
You must be signed in to change notification settings - Fork 124
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
Add support for setting/getting Date objects #89
Conversation
The travis CI tests/builds are passing, but node v12 on Windows failed to download node.js v12, so the code should be fine I think. |
This needs to be documented in the readme and index.d.ts. Also make sure that |
Hi, sorry I've been busy yesterday, but here are the modifications. |
index.js
Outdated
this.extraTypes = [ | ||
{ | ||
name: 'Date', | ||
instance: Date, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be a function too. We should not assume all future types can be detected through an instance check.
index.js
Outdated
name: 'Date', | ||
instance: Date, | ||
convertFrom: val => val.getTime(), | ||
convertTo: val => new Date(val) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
val
=> value
(everywhere)
index.js
Outdated
stack.push(value); | ||
while (stack.length > 0) { | ||
const current = stack.pop(); | ||
Object.keys(current).forEach(currentItemKey => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Object.entries()
and for-of
loop.
readme.md
Outdated
#### .set(key, value) | ||
|
||
Set an item. | ||
|
||
The `value` must be JSON serializable. Trying to set the type `undefined`, `function`, or `symbol` will result in a TypeError. | ||
The `value` must be JSON serializable. Trying to set the type `undefined`, `function`, or `symbol` will result in a TypeError. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't do unrelated changes.
t.context.config.set('dateobj', {str: 'this is a string', date: new Date()}); | ||
const myDate = t.context.config.get('dateobj').date; | ||
t.true(myDate instanceof Date); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a lot more tests for this feature, including ensuring it handles circular objects, objects inside arrays inside objects (and circular for that too), and other things you can think of.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added more tests, but I'm not sure how to go about circular objects, becuase Conf.serialize
, which is JSON.stringify
by default, throws an exception is there's a circulation. So all I did for now is to detect if there's a circulation and stop handling that branch of the object, because it would cause an infinite loop, and cause the application to crash, but when Conf
gets to serializing the object with JSON
it still throws an error. I've also tested this behavior with string
so it's not a problem with how the new feature handles extra types.
index.d.ts
Outdated
interface ExtraType<T> { | ||
name: string, | ||
instance: T, | ||
convertFrom: (val: T) => string | number, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return value here would be a good candidate for https://github.com/sindresorhus/type-fest/blob/eb110db80ccd74e8f5141cb52b3e99d3603399ce/source/basic.d.ts#L53
index.d.ts
Outdated
@@ -248,7 +256,7 @@ declare class Conf<T = any> implements Iterable<[keyof T, T[keyof T]]> { | |||
Set an item. | |||
|
|||
@param key - You can use [dot-notation](https://github.com/sindresorhus/dot-prop) in a key to access nested properties. | |||
@param value - Must be JSON serializable. Trying to set the type `undefined`, `function`, or `symbol` will result in a `TypeError`. | |||
@param value - Must be JSON serializable. Trying to set the type `undefined`, `function`, or `symbol` will result in a `TypeError`. Additionally Conf automatically seralizes and deserializes `Date` objects |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo
index.d.ts
Outdated
@@ -211,6 +218,7 @@ declare class Conf<T = any> implements Iterable<[keyof T, T[keyof T]]> { | |||
store: T; | |||
readonly path: string; | |||
readonly size: number; | |||
readonly extraTypes: Conf.ExtraType<any>[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should not be any
.
// Sub-object handling of extra types | ||
const stack = []; | ||
stack.push(value); | ||
while (stack.length > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you using a while loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in case of nested objects I want to reuse the same logic to serialize Date
types, so there are two ways I can go about this. The first one is to use recursion, the other one is to use the iterative solution, which is the while loop.
I've decided to use the iterative solution over the recursive solution, because with really deeply nested objects, there's a possibility that the function could overflow the callstack.
readme.md
Outdated
Currently supported objects are: | ||
- `Date` - serializes the Date to milliseconds, then back to a `Date` object when the getter is called | ||
|
||
If you want support for a new object/type please open a new issue for discussion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs in the readme and index.d.ts should be in sync.
Any updates on merging this functionality? Would be great to have a simple way to store and retrieve dates. |
@delewis13 I've implemented the requested changes, but I didn't want to update the PR, because there are still questions, namely:
Other than that the rest of the requested changes are implemented |
Hi @sbencoding, there are other modules that can handle serialization and deserialization of circular objects automatically for you, such as flatted, maybe this is what Sindre had in mind... |
Convert dates when in a nested object Move conversion to store getter, setter
Add typescript tests for Date objects Add typescript definition of extraTypes array
Convert dates when in a nested object Move conversion to store getter, setter
Add typescript tests for Date objects Add typescript definition of extraTypes array
Hey, sorry for the late response.
|
@@ -1,9 +1,30 @@ | |||
/// <reference types="node"/> | |||
import {JSONSchema} from 'json-schema-typed'; | |||
import { JSONSchema } from 'json-schema-typed'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't do unrelated style changes. Applies in many places. If you upgrade the XO dependency and run npx xo --fix
these things will be fixed for you.
index.d.ts
Outdated
/** | ||
* Matches a JSON object | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** | |
* Matches a JSON object | |
*/ | |
/** | |
Matches a JSON object. | |
*/ |
Applies everywhere.
index.d.ts
Outdated
@@ -225,6 +268,10 @@ declare class Conf<T = any> implements Iterable<[keyof T, T[keyof T]]> { | |||
store: T; | |||
readonly path: string; | |||
readonly size: number; | |||
/** | |||
* Conf can automatically (de)serialize some objects/types, for more information please visit [Supported Types](https://https://github.com/sindresorhus/conf#Supported-Types) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The readme and index.d.ts docs should be in sync. That means the linked to text should be included inline here.
index.js
Outdated
let circularReferenceCounter = 1; | ||
while (stack.length > 0) { | ||
const {current, parent, key} = stack.pop(); | ||
if (objectSeenList.indexOf(current) === -1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (objectSeenList.indexOf(current) === -1) { | |
if (!objectSeenList.includes(current)) { |
} | ||
|
||
return value; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is pretty large. I would look into splitting it into multiple methods and maybe something here is generic enough to be moved out into a generic utility function. Same with _deserializeExtraTypes
.
index.js
Outdated
// seenList.indexOf(current) is the object it's pointing to | ||
const referenceTarget = objectSeenList[objectSeenList.indexOf(current)]; | ||
parent[key] = { | ||
[TYPE_KEY]: 'circularObject', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really see why you need the circularObject
thing. Circular objects can simply be detected by comparing references. I think this logic could be simplified a lot.
See how it's done here: https://github.com/sindresorhus/serialize-error/blob/master/index.js and here: https://github.com/sindresorhus/map-obj/blob/master/index.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The circularObject
type signals to the deserialize function that it was a circular object, so it can lookup the referenceNumber
set in the value of the object, and set it to the object it was referencing to before serialization.
I might have misunderstood this problem. I made it so that when deserializing the circulation is reconstructed, so you're going to end up with the same circular object after getting it from the store.
So should I just completely remove circulation like the destroyCircular
function does in the first example you've provided, or is there an easier way to reconstruct the circulation?
t.true(getCircular.c1.myDate instanceof Date); | ||
t.true(getCircular.c2.myDate instanceof Date); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There needs to be some tests for other types, just to ensure it works in general with custom types and other types than Date.
index.js
Outdated
console.log(referenceTarget[CIRCULAR_REF_KEY]); | ||
} | ||
|
||
console.log('skipping pontentially circulation'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modules should not log to the console.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm very, very sorry about this, sometimes I just add some log statements for testing, and I always try to keep in mind to remove them, but it seems like I've forgotten this time.
@@ -356,6 +358,15 @@ conf.store = { | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to document how to use the custom deserializer/serializer.
I've implemented most of the requested changes, I'm just going to re-work a bit the addition of extra types by the consumer program tomorrow, and then I'm going push the new changes. I'm really confused about what I should do with circular objects now, but that's explained in a comment in the code review. I'd be happy to implement another approach, if I'm missing the point of this feature. |
I've implemented the requested changes, now the build only fails for node v12 on macOS during the 2 Instead of directly updating the extraTypes array of a Conf instance, I chose to implement the addition of extra types through the options argument that's passed to the constructor. Let me know if there's anything else to change. |
readme.md
Outdated
- It must return the type that is defined. | ||
|
||
#### Example | ||
The following example demonstrates the configuration of the `Date` type, that is internally supported by default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to use a different type than the default.
There are still some unresolved inline comments. Also make sure that the docs in the readme is in sync with index.d.ts and the other way. |
I've updated the example and double-checked to docs to reflect each other as much as possible, let me know if there's anything else to change/update. |
I appreciate all the work done here, but I unfortunately don't think this is the optimal solution. Closing in favor of #154. |
Implementing a new feature described in issue sindresorhus/electron-store#18.
Type information is stored under
${INTERNAL_KEY}.type
as suggested, theget
method checks if there's a type key present, if there's it converts the value to the type before returning it.The
set
methods checks if the value passed in is aninstance of Date
, if it is it stores it as an object like:I've also added a test to
test.js
to test for the setting and getting Date objectsLet me know if there's anything I could change. For example I chose to include type information only for Date objects, but it might be better to store type information for every key-value pair to be more consistent, but it would also make the object and the file larger.