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

Proxy chance.js methods when custom fake-eggs methods don't exist #132

Closed
wants to merge 1 commit into from

Conversation

mattpetrie
Copy link
Contributor

@mattpetrie mattpetrie commented Mar 26, 2021

Background

We have established use of fake-eggs as the Good Eggs best practice for fake data generation. However, fake-eggs has many known gaps in the types of data it can generate (such as geolocation coordinates, currency, etc.). Meanwhile chance.js the library that powers fake-eggs under the hood, has dozens of additional fake data generators not currently used by fake-eggs.

Currently the process when you have a need for a generator that is not currently provided by fake-eggs is to open a PR to that manually adds a generator for that specific type of data, merge the PR, and release a new version of fake-eggs. Also, frequently the added generator is a thin wrapper that just forwards arguments to chance.js.

Changes

This PR proposes using a Proxy to fall back to calling chance.js's methods whenever a custom fake-eggs method does not exist, e.g., fake.ssn() will call chance.ssn() since fake-eggs does not have a custom ssn method defined, but fake.integer() would still use the custom-defined integer method. The proxy uses the seeded instance of chance so seeding is preserved.

Some benefits of this are:

  • Vast increase in the types of fake date fake-eggs is capable of generating, making it much more likely that fake-eggs can be used in most contexts without the need for additional fake data libraries.
  • We only need to create custom generators when we have a specific need to -- if we need the function signature to be different, we have specific desires for the returned data, or chance.js doesn't implement it. Otherwise, we can simply use chance.js's method for free.

@mattpetrie mattpetrie changed the title Proxy chance.js methods when custom fake-eggs don't exist Proxy chance.js methods when custom methods fake-eggs don't exist Mar 26, 2021
@mattpetrie mattpetrie changed the title Proxy chance.js methods when custom methods fake-eggs don't exist Proxy chance.js methods when custom fake-eggs methods don't exist Mar 26, 2021
@mattpetrie mattpetrie requested review from serhalp and piercebb March 26, 2021 01:29
return Reflect.get(target, property, receiver);
}

return Reflect.get(chance, property, chance).bind(chance);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This felt a little hacky, but chance internally relies on this context to call methods, e.g. this.integer, and this was what I came up with to ensure it has the correct this instead of calling our custom methods.

For example, chance.latitude calls this.integer internally, and but we need integer to be chance.integer and not our custom integer method in this context since our custom method has a different argument signature.

Copy link
Contributor

Choose a reason for hiding this comment

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

Omg this is so cool. 🤩

Just curious, if something like this was used instead, would the proxy then use our custom methods?

Suggested change
return Reflect.get(chance, property, chance).bind(chance);
return target[property] || chance[property];

if (property in target) {
return Reflect.get(target, property, receiver);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we wanted to specifically exclude any chance.js method we could also add more conditions here.

@@ -108,6 +108,23 @@ export const createFakeEggs = ({
word: createWordGenerator(chance),
zip: createZipGenerator(chance),
};

type FakeEggsCustomMethods = typeof customMethods;
interface FakeEggs
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A little awkward to have the type definition here, but it does achieve the desired result of a return type that overrides chance's method types for each of our custom methods.

Copy link
Contributor

@piercebb piercebb left a comment

Choose a reason for hiding this comment

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

Amazing!! 🎉

Copy link
Contributor

@shermam shermam left a comment

Choose a reason for hiding this comment

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

I really like the idea of having access to all of chance's methods. But my suggestion here would be to expose them with fake.chance.someMethod(). In my opinion this would make it clearer that we are actually using chance, and I would know that I can just consult the documentation on https://chancejs.com/ for any of those methods. Also, by doing this, we could take advantage of using chance's implementation of methods with the same name if we choose too. An example of that is integer that in chance generates the full range of safe JS integers and uses named args min and max.

I understand that we might want to have the control over the implementation of some methods. @mattpetrie mentioned the example of lat and long that in fake-eggs we might want to limit to the state of California. But I would argue that whenever we decided to change an implementation of a method we should actually consider creating a more specific method like fake.california.lat and fake.california.long and keep the raw chance method available for other more general uses. This would even be better so that code that already rely on a current implementation won't break.

This is more of personal preference though.

Copy link
Contributor

@shermam shermam left a comment

Choose a reason for hiding this comment

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

Oh, and as I mentioned, I really like the idea of having access to all of chance's methods.

And even though I have some personal preferences that diverge a bit from this current implementation, I don't feel strongly enough about them to block this PR.

This is an awesome initiative @mattpetrie !

@aliceslin91
Copy link
Contributor

+1 bump to push this through - just went through exactly this process with #233 and its upcoming minor release.

I lean toward agreeing with @shermam 's suggestion of using fake.chance.xyz() for any pass-through functions. It'd explicitly tell an engineer "look at Chance's documentation" vs "look at fake-eggs to see if there's a definition, if not then look at Chance"

@serhalp
Copy link
Contributor

serhalp commented Nov 16, 2021

Funny enough @aliceslin91 Developer Enablement has prioritized pushing this through! https://www.pivotaltracker.com/story/show/180313615

@mattpetrie
Copy link
Contributor Author

I'm excited to see this finally getting pushed through!

Explicitly exposing chance.js in the way suggested was actually something I was intentionally avoiding -- my thinking was that there was some benefit to not coupling fake-eggs' interface to a specific underlying library. In other words, if we were ever to decide that we wanted to use some thing other than chance.js to power fake-eggs (and we have switched random data libraries before), having fake.chance.xyz methods would no longer make sense. We would be in the situation of having to choose between either continuing to support fake.chance.xyz methods for backwards compatibility (leaving engineers to wonder why that is the interface), or update every use of fake.chance.xyz in our codebase to fake.newLibrary.xyz in order to update to the new version of fake-eggs. But, if other folks find that to be a preferable trade-off to not being able to immediately know whether fake.xyz is a chance.js or custom-implemented method, I'm ok with conceding on this one.

@shermam
Copy link
Contributor

shermam commented Nov 16, 2021

I'm excited to see this finally getting pushed through!

Explicitly exposing chance.js in the way suggested was actually something I was intentionally avoiding -- my thinking was that there was some benefit to not coupling fake-eggs' interface to a specific underlying library. In other words, if we were ever to decide that we wanted to use some thing other than chance.js to power fake-eggs (and we have switched random data libraries before), having fake.chance.xyz methods would no longer make sense. We would be in the situation of having to choose between either continuing to support fake.chance.xyz methods for backwards compatibility (leaving engineers to wonder why that is the interface), or update every use of fake.chance.xyz in our codebase to fake.newLibrary.xyz in order to update to the new version of fake-eggs. But, if other folks find that to be a preferable trade-off to not being able to immediately know whether fake.xyz is a chance.js or custom-implemented method, I'm ok with conceding on this one.

If in the future we decide to change libraries, we need to make sure that either all of chance's methods that we exposed are ported to the new library and have the same behaviour as chance.js, because that is what clients of fake-eggs will come to expect. Or we could find all uses of direct chance.js methods and only port those. The first option seems quite risky and potentially entails a lot of extra work that might not be strictly necessary. And for the second option having chance be explicitly exposed make it easier in my opinion. So I definitely prefer explicitly exposing it, but I am also ok conceding on this one if you guys decide otherwise.

@serhalp
Copy link
Contributor

serhalp commented Nov 17, 2021

@shermam I was going to write almost exactly the same comment! 👍🏼

@serhalp
Copy link
Contributor

serhalp commented Nov 29, 2021

Superseded by #237

@serhalp serhalp closed this Nov 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Consider exposing Chance.js methods when no Good Eggs-specific method is defined
5 participants