-
Notifications
You must be signed in to change notification settings - Fork 51
Refactoring to remove late variables and cyclic dependencies #247
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
Refactoring to remove late variables and cyclic dependencies #247
Conversation
@@ -86,6 +85,8 @@ class Initializer { | |||
sessionFile.createSync(recursive: true); | |||
sessionFile | |||
.writeAsStringSync('{"session_id": ${now.millisecondsSinceEpoch}}'); | |||
// TODO: eliasyishak, remove the below once https://github.com/google/file.dart/issues/236 is fixed | |||
sessionFile.setLastModifiedSync(clock.now()); |
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.
Are you doing this solely for test code? Let's try to avoid doing extra file system operations just for the sake of tests.
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.
Yes, a test consistently fails if we don't set the modified timestamp because it thinks there is more than one session since the last modified timestamp gets set to something like 2000-01-01 00:08:00.000
I don't think deleting a valid test because of a bug would is ideal in this case either.
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.
Actually this is not a bug, the dartdocs explicitly say that a fake clock is used for the MemoryFileSystem.test
constructor. Currently trying to refactor around this
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 moved the call to File.setLastModifiedSync
into the test... where it should have probably been the whole time. Good catch :)
@@ -94,10 +91,17 @@ abstract class Analytics { | |||
toolsMessageVersion: kToolsMessageVersion, | |||
fs: fs, | |||
gaClient: gaClient, | |||
surveyHandler: SurveyHandler(homeDirectory: homeDirectory, fs: fs), | |||
surveyHandler: SurveyHandler( | |||
dismissedSurveyFile: fs.file(p.join( |
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 see this file path being constructed twice, here on line 95 and later on line 174, can we make it a shared static function on the SurveyHandler
class that takes in the home path as an argument, that way if/when we change the path we only have to change one place? Or, if the path will always be the same relative to home, can we just pass in the home path to the constructor and have the constructor figure it out?
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 actually had this thought as well, except i wanted to do this for all of the file paths we use for the 5 files we generate.
I was thinking of making an Artifacts
class with getters for each file, simple example below
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:path/path.dart' as p;
class Artifacts {
static File dismissedSurveyFile({
required Directory homeDirectory,
required FileSystem fs,
}) =>
fs.file(p.join(homeDirectory.path, 'path', 'to', 'dismissedSurvey.json'));
static File logFile({
required Directory homeDirectory,
required FileSystem fs,
}) =>
fs.file(p.join(homeDirectory.path, 'path', 'to', 'analytics.log'));
}
void main() {
final fs = MemoryFileSystem.test();
final homeDirectory = fs.directory('home');
homeDirectory.createSync();
final dismissedSurveyFile =
Artifacts.dismissedSurveyFile(homeDirectory: homeDirectory, fs: fs);
dismissedSurveyFile.createSync(recursive: true);
final logFile = Artifacts.logFile(homeDirectory: homeDirectory, fs: fs);
logFile.createSync(recursive: true);
}
Thoughts?
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.
Or we could create an instance of Artifacts
and pass the filesystem and homedirectory in the constructor so we could simply run something like the below
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:path/path.dart' as p;
class Artifacts {
final Directory homeDirectory;
final FileSystem fs;
Artifacts({required this.homeDirectory, required this.fs});
File get dismissedSurveyFile =>
fs.file(p.join(homeDirectory.path, 'path', 'to', 'dismissedSurvey.json'));
File get logFile =>
fs.file(p.join(homeDirectory.path, 'path', 'to', 'analytics.log'));
}
void main() {
final fs = MemoryFileSystem.test();
final homeDirectory = fs.directory('home');
homeDirectory.createSync();
// Create an instance that we can pass around
final _artifacts = Artifacts(homeDirectory: homeDirectory, fs: fs);
final dismissedSurveyFile = _artifacts.dismissedSurveyFile;
dismissedSurveyFile.createSync(recursive: true);
final logFile = _artifacts.logFile;
logFile.createSync(recursive: true);
}
But either way, I was thinking of adding this in a separate PR
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 overall, I would try to avoid adding new classes unless you have a set of behaviors (functions) that:
- mutate state that they exclusively own; and/or
- you need to model a hierarchy of types (e.g. Animal, Dog, Cat)
Thus, I would prefer these just be top level functions
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 you decide to keep the dependency on package:file (which I think is reasonable), these functions could simply take one argument, Directory homeDirectory
, and then you could specify the path to the desired files via the FileSystemEntity.childDirectory
and FileSystemEntity.childFile
methods, which will ensure the appropriate path separator is used for the file system your home directory was created from.
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.
Doing this in a separate PR SGTM.
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
* Rewrite the `last_ping` key again * Apply test fix from #247 * Use timestamp instead of null * Format fix * Update version * Code review comments * Lingering comment
FYI that it looks like we'll need updates to the dart sdk repo (and google3?) in order to rev to the commit w/ this PR. |
Related to:
Changes made in this PR:
late
variables fromAnalyticsImpl
,ConfigHandler
Initializer
methods for creating files have been converted tostatic
methodsLogHandler
andUserProperty
includes instance membererrorSet
to keep track of any errors encounterederrorSet
instance variable, we are able to deleteerror_handler.dart
and handle the logic withinAnalyticsImpl
session.dart
and consolidated session logic intoUserProperty
Contribution guidelines:
dart format
.Note that many Dart repos have a weekly cadence for reviewing PRs - please allow for some latency before initial review feedback.