-
Notifications
You must be signed in to change notification settings - Fork 21
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
[cli_config] First version #45
Conversation
Pull Request Test Coverage Report for Build 4542279393
💛 - Coveralls |
@lrhn your thoughts on our APIs are always appreciated! |
CliProvider(this._cli); | ||
|
||
@override | ||
String? getOptionalString(String key) { |
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 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.
Hm, config.string('key')
vs config.readString('key')
or config.loadString('key')
. I guess we don't care about the verb so much.
Unfortunately, config.bool('key')
doesn't work, because bool
is a keyword.
config.stringValue('key')
seems more bloated than using a verb.
get
is the shortest verb.
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 like get
because this looks essentially like a map.
An alternative solution is to create a CliResult
object that operator []
returns. Then config.getOptionalString('key')
becomes config['key'].as<String>()
, .as<bool>()
and ...
.as<T>()
for an unsupported T
throws a FormatException
.
Edit: Oops, as
is also a keyword! maybe .value<String>()
?
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 API here is similar to shared_preferences
. And there we have getString
and getStringList
as well.
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 would use read
, or consider dropping the entire verb and using boolean
instead of bool
? read
is only one more character.
I don't ultimately feel super strongly for this particular case, but I do care that Dart owned/maintained packages follow effective Dart guidelines, so we don't set an example that is contrary to effective Dart. Ultimately people will imitate the types of apis we put out there, and use them as justification for their own, unaware of any deeper reasoning. In fact shared_preferences
is a perfect example of this, in this exact thread...
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.
https://github.com/flutter/packages/blame/dd8b54101c056190d15e5a1f6ad2b491657f23c2/packages/shared_preferences/lib/shared_preferences.dart I don't seen any code review for our shared_preferences initial PR that introduced this API.
Since read
is a verb that communicates not that much here (also, technically speaking, part or the reading has happened already when reading in the various sources), it is a good argument for not having the verb. And being in the case that it should be a getter even though it has is a method with arguments.
The issue here is that the no-verb solution conflicts with keywords. And choosing boolean
, integer
, and doublee
, would create confusion because they are spelled the same as the type they return.
@munificent touched this part of the style guide last, any neat ideas of what to do with conflicts with keywords?
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'd drop the verb. Changing get
to read
, which still has no semantic meaning different from "get", "fetch", "lookup" or "find", is not an improvement.
Don't use words that say nothing. Document and name getter-like methods like get getters (noun phrases).
That's what this is.
I'd use string
, stringValue
, stringValueOf
or (more dubious) stringOf
.
For bool
and int
, where we may don't want to get naming conflicts (we can, they are not reserved words, they just shadow the core types), we can use boolean
and integer
, or boolValue
/intValue
.
Or just use int
and bool
and deal with the conflicts:
import "dart:core";
import "dart:core" as core;
....
core.bool bool(String key) { ... }
(You need to use core.bool
everywhere in the class, highly annoying.)
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.
Thanks @jakemac53 and @natebosch !
CliProvider(this._cli); | ||
|
||
@override | ||
String? getOptionalString(String key) { |
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.
Hm, config.string('key')
vs config.readString('key')
or config.loadString('key')
. I guess we don't care about the verb so much.
Unfortunately, config.bool('key')
doesn't work, because bool
is a keyword.
config.stringValue('key')
seems more bloated than using a verb.
get
is the shortest verb.
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.
LGTM. Thanks @dcharkes!
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.
Initial comments, didn't get through everything (or much of anything).
|
||
static const boolStrings = { | ||
'0': false, | ||
'1': true, |
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'd recommend not accepting 0/1.
I'd accept "yes"/"no" before that, but instead of pre-determining which strings you want to accept, I'd parameterize the function below ...
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 1
and 0
come from environment variable practises. If we don't want to accept that by default from the other two sources (file/commandline-defines) maybe we should have a map for both env vars and cl defines. (The file source must provide a boolean.)
I like the idea of parameterizing, but I would want to provide a default for people.
CliProvider(this._cli); | ||
|
||
@override | ||
String? getOptionalString(String key) { |
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'd drop the verb. Changing get
to read
, which still has no semantic meaning different from "get", "fetch", "lookup" or "find", is not an improvement.
Don't use words that say nothing. Document and name getter-like methods like get getters (noun phrases).
That's what this is.
I'd use string
, stringValue
, stringValueOf
or (more dubious) stringOf
.
For bool
and int
, where we may don't want to get naming conflicts (we can, they are not reserved words, they just shadow the core types), we can use boolean
and integer
, or boolValue
/intValue
.
Or just use int
and bool
and deal with the conflicts:
import "dart:core";
import "dart:core" as core;
....
core.bool bool(String key) { ... }
(You need to use core.bool
everywhere in the class, highly annoying.)
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 gone with Lasse's approach and just went for bool
and made the rest in the file core.bool
.
It's been quiet here. If there's anymore feedback, please let me know. Otherwise I'll go ahead and merge this. |
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.0 to 4.1.1. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p> <blockquote> <h2>v4.1.1</h2> <h2>What's Changed</h2> <ul> <li>Update CODEOWNERS to Launch team by <a href="https://github.com/joshmgross"><code>@�joshmgross</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1510">actions/checkout#1510</a></li> <li>Correct link to GitHub Docs by <a href="https://github.com/peterbe"><code>@�peterbe</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1511">actions/checkout#1511</a></li> <li>Link to release page from what's new section by <a href="https://github.com/cory-miller"><code>@�cory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1514">actions/checkout#1514</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/joshmgross"><code>@�joshmgross</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1510">actions/checkout#1510</a></li> <li><a href="https://github.com/peterbe"><code>@�peterbe</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1511">actions/checkout#1511</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.1.0...v4.1.1">https://github.com/actions/checkout/compare/v4.1.0...v4.1.1</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/checkout/commit/b4ffde65f46336ab88eb53be808477a3936bae11"><code>b4ffde6</code></a> Link to release page from what's new section (<a href="https://redirect.github.com/actions/checkout/issues/1514">#1514</a>)</li> <li><a href="https://github.com/actions/checkout/commit/8530928916aaef40f59e6f221989ccb31f5759e7"><code>8530928</code></a> Correct link to GitHub Docs (<a href="https://redirect.github.com/actions/checkout/issues/1511">#1511</a>)</li> <li><a href="https://github.com/actions/checkout/commit/7cdaf2fbc075e6f3b9ca94cfd6cec5adc8a75622"><code>7cdaf2f</code></a> Update CODEOWNERS to Launch team (<a href="https://redirect.github.com/actions/checkout/issues/1510">#1510</a>)</li> <li>See full diff in <a href="https://github.com/actions/checkout/compare/8ade135a41bc03ea155e62e844d188df1ea18608...b4ffde65f46336ab88eb53be808477a3936bae11">compare view</a></li> </ul> </details> <br /> [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.0&new-version=4.1.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details>
This is an initial version for
package:cli_config
.The only class in the API is the
Config
class.This PR is to solicit feedback on multiple levels.
Our format opinions for end users.
What do we accept as valid hierarchical identifers in YAML/JSON/CLI-defines/environment variables?
We prefer underscores in identifiers. (
pubspec.yaml
andjnigen.yaml
do underscores, howeverffigen.yaml
dashes.) Should we consider something else?What about hierarchy in environment variables where we cannot use
.
. Do we use__
?What syntax do we prefer for CLI defines?
-Dkey.nested=value
(typical define style)? Or--set key.nested=value
(Helm style)? Or--key.nested=value
(typical CLI style).Programmable API for package authors
A single class with a bunch of getters. The getters define how to treat the values in the various "provider"s. For example whether to split the strings on a character (e.g. splitting
PATH
on:
). The getters also define on whether to combine the values from the various providers or not.The error flow for package authors
Currently, both creating a
Config
and failures on config values can throwFormatException
s. A YAML file could have the wrong format, but also a required config key could be missing which also makes the config the wrong format.Should we consider having a dedicated type of exception instead?
Implementation considerations.
Please do not yet review those, let's get the other concerns right first.