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
Materialize configuration options parameters that are functions. #263
Conversation
src/scripts/core/contour-utils.js
Outdated
var curKeyPath = curPath ? curPath + '.' + key : key; | ||
var notInSkipList = skipList.indexOf(curKeyPath) === -1; | ||
var notSkipMatch = (skipMatch && skipMatch.test ? !skipMatch.test(curKeyPath) : true); | ||
var expectsParam = /(function)?\s?\(.+\)\s*({|=>)/.test((object[key] || '').toString()); |
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 you can just do object[key].length
to get how many parameters it takes.
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 do we need to separate functions which specify params? For the ones which matter, like the formatter, can't we just blacklist them with the skip lists? Seems a bit scary to think marker : { enable: ()=> true }
will work but marker : { enable: (randomCopyPasteParameter)=> true }
won't.
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 was thinking the same thing... But otherwise, for regular callback functions like formatters, you have to remember to add the skip list which would not be very intuitive. We could leave this option out if you want.
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 mean "you" as in "Contour developer", not "end user" right? i.e., we'd already know what to blacklist and it'll only be a problem if we wanted to add a new Contour options down the line which took in a function-with-param value, in which case we'd have to add it to the blacklist.
Or would you need to use this when you're building a custom visualization?
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 impacts two groups:
- contour core developers, when adding new functionality. I guess this is less of a problem since you "we know what we are doing" :)
- Contour lib users. If you are developing a custom visualization that calls a function defined in your part of the config object, you'll need provide a default
skip
list that includes your function, but if the user of your visualization overrides the skip list, then the viz can break without much indication as of why. For example, let's say I create a custom viz like this:
const renderer = (data, layer, options) => {
layer.selectAll('.translations').data(data);
layer.enter()
.append('div')
.text(options.translate.getTranslation);
};
renderer.defaults = {
translate: {
getTranslation: (d) => `data is ${d.x}, ${d.y}`,
},
skip: ['translate.getTranslation']
}
Contour.export('translate', renderer);
in this case, if the user of the visualization passes a different skip list, translate
viz would break.
Makes sense? maybe we can force to the functions to have a specific signature to prevent them from being materialized, but again consumers of custom viz would have to remember to follow the signature when passing a fn param.
In general my thinking was that the case were you pass a function that has a parameter by mistake is less probable than the case where you need to remember to add it to the skip list.
Another idea could be to have a "debug" mode in contour where you get debugging/console messages that tell you which functions got materialized.
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.
That makes sense - how about a whitelist of 'known materializable properties' instead of a blacklisted skip list then? This would mean there is no change in behavior for developing custom viz, unless we allow them to 'opt-in' by passing in an 'allow' flag (or maybe we just don't expose this to custom viz).
That said, you know a lot more about the different cases than I do so I'm fine going with your call either way.
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 thought about going with the whitelist route too, but then that would mean that mostly everything would be in the whitelist (as most props are fine to have functions) and if I create a custom viz, I need to add each property that can be materializable to the list except from my little lonely getTranslations
function... that seem an overkill too :)
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 meant whitelist specifically for custom visualizatons; if i'm defining a custom viz which takes in a boolean prop I wouldn't automatically expect that to be callable with a function -- or does that distinction between external/internal visualizations not exist?
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 not really a concept of internal vs external, unless we keep an internal list... as long as you have access to the Contour object, you can export
new viz to be used by anyone.
src/documentation/config/config.md
Outdated
@@ -38,6 +38,12 @@ Each configuration option can be: | |||
|
|||
columnWidth: function() { return this.rangeBand/3 * 2; } | |||
|
|||
__Note__ Function parameters will be invoked just before each render happens or in the within the render cycle with the corresponding data point in the following cases: |
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.
do you mean except the following cases, or am i mis-understanding what this is?
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 changed the text a bit to make it clearer
src/documentation/config/config.md
Outdated
__Note__ Function parameters will be invoked just before each render happens or in the within the render cycle with the corresponding data point in the following cases: | ||
* function expects a parameter (ie. `function (d) { return d.x; }`) | ||
* parameter path is in the `options.skip` | ||
* parameter path matches the RegExp `options.skipMatch` |
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.
Since you're computing the "path" anyway, in which cases would just the skip array not be good enough?
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 change the skip array to accept full paths, or regexp so you have a simple list of things to skip. I think the regexp is handy to skip global params like formatter (ie /formatter/
) without having to specify every path that you want to skip.
fa1df30
to
874e004
Compare
src/scripts/core/contour.js
Outdated
@@ -315,7 +315,7 @@ | |||
_.each(this._extraOptions, mergeExtraOptions); | |||
_.each(this._visualizations, mergeDefaults); | |||
|
|||
var opt = _.extend({}, this.originalOptions); | |||
var opt = _.nw.materialize(this.originalOptions, this, { skipMatch: /formatter/ }); |
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.
Note to self: this needs to be updated to remove param skipMatch
and merge the original skip
list
var opt = _.nw.materialize(this.originalOptions, this, { skip: [/formatter/].concat(this.originaOptions.skip) });
tests/specs/contour-utils-spec.js
Outdated
|
||
it('should skip functions that expect parameters', function () { | ||
expect(res.d.other).toEqual(input.d.other); | ||
}); |
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.
note to self:
Add test to check default call to materialize always includes forces ignore props (ie. /formatter/)
874e004
to
f76c95f
Compare
I think this is ready to be merged? |
Sorry, I thought your "note to self" meant you were going to add something else :) But now this has conflicts, can you resolve? |
f76c95f
to
fd089b1
Compare
I added the test that the note-to-self refer to :) I rebase and has no more conflicts now. |
@jaimedp I did a quick hallway usability test for materialize as a function. I asked someone to write code to toggle markers based on a checkbox, and they came up with
I then asked them to only show markers if data.length > 10, and they started with
to see if the data is passed in as an parameter, and of course, that doesn't work and it's impossible to guess why not. Admittedly sample size of 1, and you could argue calling this out prominently in the docs would mitigate this, but I think we shout at least console.warn if it's a function and it's not parsed, or whitelist. What do you think? |
Why would you expect to receive Maybe we can introduce a |
x, y was just an example -- i.e. random parameter names to test out what data is getting passed into a function, except the act of figuring it out makes it invalid. A new variation of a heisenbug :) You could do always do console.log(arguments) of course. Why not log what parameters are functions and are not materialized all the time? i.e. something like if |
It could be, I just don't think we should be polluting the console with
messages when most of the time are not useful, besides it actually could
make the `materialize` call slower
…On Wed, Dec 6, 2017 at 4:21 PM, Narendran Ranjit ***@***.***> wrote:
x, y was just an example -- i.e. random parameter names to test out what
data is getting passed into a function, except the act of figuring it out
makes it invalid. A new variation of a heisenbug :) You could do always do
console.log(arguments) of course.
Why not log what parameters are functions and are *not* materialized all
the time? i.e. something like if isFunction(object[key]) &&
object[key].length && !['formatter', ...].contains(key)
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#263 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAVimeL2ECjWdiKu9ogcKi6XRmLL37Wiks5s9y-jgaJpZM4Qc49_>
.
|
In what cases will there be a false-positive warning? |
I see, you mean only warn if you have a fn that expects parameters that is
not whitelisted in the skip list... I think that could work.
…On Wed, Dec 6, 2017 at 4:40 PM, Narendran Ranjit ***@***.***> wrote:
In what cases will there be a false-positive warning?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#263 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAVimc1KtrPMe3HuJnOuDnzpRd1f9B8Gks5s9zPwgaJpZM4Qc49_>
.
|
I added a warning when you have a function with params that is not in the skip list |
This allows to specify any configuration option as a function. This functions will be called just before the rendering starts. Some functions are not "materialized" as they expect to be called with some data within the context of the render cycle for example data formatters expect to be called for each point in the data set. These functions are not materialized before the render starts and are called in the context of the specific visualization.