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
Introduce an object registry #371
base: master
Are you sure you want to change the base?
Conversation
Instead of passing around `env` absolutely everywhere, let's make it available globally in a single, well-defined place. We can't use a singleton for this, because we do sometimes need to create different, modified environments, e.g. subshells.
initialized?: true, | ||
has_modified_files?: true, | ||
has_untracked_files?: false, | ||
)) |
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.
Style/TrailingCommaInArguments: Put a comma after the last parameter of a multiline method call.
format: "%c", | ||
) | ||
prompter = Gitsh::Prompter.new(env: env) | ||
set_registered_env_value('gitsh.prompt', "%c") |
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.
Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.
initialized?: true, | ||
has_modified_files?: true, | ||
has_untracked_files?: false, | ||
)) |
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.
Style/TrailingCommaInArguments: Put a comma after the last parameter of a multiline method call.
prompter = Gitsh::Prompter.new(env: env) | ||
set_registered_env_value('gitsh.prompt', "%#") | ||
register_repo(status: double( | ||
"Status", |
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.
Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.
format: "%#", | ||
) | ||
prompter = Gitsh::Prompter.new(env: env) | ||
set_registered_env_value('gitsh.prompt', "%#") |
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.
Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.
build_env(variables: ['user.name', 'user.email', 'greeting']), | ||
register_line_editor | ||
register_env( | ||
available_variables: [:'user.name', :'user.email', :'greeting'], |
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.
Style/SymbolArray: Use %i or %I for an array of symbols.
Style/SymbolLiteral: Do not use strings for word-like symbol literals.
|
||
input_strategy.handle_parse_error('my message') | ||
|
||
expect(env).to have_received(:puts_error).with('gitsh: my message') | ||
expect(registered_env).to have_received(:puts_error).with('gitsh: my message') |
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.
Metrics/LineLength: Line is too long. [84/80]
input_strategy = build_input_strategy | ||
allow(line_editor).to receive(:readline).and_return('user input') | ||
input_strategy = described_class.new | ||
allow(registered_line_editor).to receive(:readline).and_return('user input') |
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.
Metrics/LineLength: Line is too long. [82/80]
line_editor_results = StubbedMethodResult.new. | ||
raises(Interrupt). | ||
returns('user input after interrupt') | ||
allow(line_editor).to receive(:readline) { line_editor_results.next_result } | ||
allow(registered_line_editor).to receive(:readline) { line_editor_results.next_result } |
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.
Metrics/LineLength: Line is too long. [93/80]
input_strategy = build_input_strategy | ||
allow(line_editor).to receive(:readline).and_return('user input') | ||
input_strategy = described_class.new | ||
allow(registered_line_editor).to receive(:readline).and_return('user input') |
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.
Metrics/LineLength: Line is too long. [82/80]
What?
This PR is a large refactor which replaces dependency injection with the registry pattern for shared instances, or direct references to class names that were previously passed as constructor arguments.
Previously we had a lot of classes that looked like this:
Now, those same classes look like this:
The objects moved to the registry are the shared instances of:
Gitsh::Environment
Gitsh::GitRepository
Gitsh::LineEditorHistoryFilter
(decoratingGitsh::LineEditor
)Why?
The nature of gitsh means that some amount of long-lasting, global state is inevitable. Unlike a Web application, which needs to handle multiple concurrent requests from different users, a gitsh process only runs a single command at any given time, and does so for a single user in a single session.
We get a lot of benefits from this approach, which include:
Fewer redundant arguments
There were several classes which held a reference to the shared
Gitsh::Environment
instance only because they needed to pass it along to some other object that needed to use it.Easier to break up
Gitsh::Environment
The
Gitsh::Environment
has become a bit of a junk drawer. The main reason is that it was the one object that was available everywhere, so access to any shared state was easiest if it went through this object.Introducing the registry means we are able to drop all of the
Gitsh::Environment#repo_*
methods (which delegated directly to aGitsh::GitRepository
instance) and instead share both theGitsh::Environment
andGitsh::GitRepository
via the registry.The remaining responsibilities of
Gitsh::Environment
(I/O, variable access, and access to some global config) can be broken out in future PRs.Easier to share other instances
The final straw in doing this refactor was starting work on globbing patterns (Wildcards don't seem to work correctly #145), and realising we will need to access the tab completion automaton while evaluating variable values. Adding yet another thing to the environment was not appealing, nor was passing another instance around through the whole system, or building multiple instances of a pretty large tab completion graph.
After this refactor, we'll be able to use the registry to share the tab completion automaton instance, and easily access it directly from where it's needed.
Code review
This diff is very large (~1,000 changed lines), but many of the changes are very similar and reviewers should probably focus on:
Gitsh::Registry
implementation inlib/gitsh/registry.rb
spec/support/registry.rb