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

ExtractTempDriver #17168

Merged
merged 18 commits into from
Nov 25, 2024
Merged

Conversation

SandraSamardzic
Copy link
Contributor

Creating an interaction driver for RBExtractToTemporaryRefactoring

@Ducasse
Copy link
Member

Ducasse commented Sep 29, 2024

Thanks a lot!

@Ducasse
Copy link
Member

Ducasse commented Sep 29, 2024

I will try to find some time to have a look.

@SandraSamardzic
Copy link
Contributor Author

Okay, thank you @Ducasse, I appreciate it.

Copy link
Member

@balsa-sarenac balsa-sarenac left a comment

Choose a reason for hiding this comment

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

Thanks Sandra, this looks good on quick skim. To make this complete you need to make extract temp command actually use this driver.

  • Find the command class and do required changes to make it use driver instead of refactoring
  • Migrate any UI from command to driver
  • Create at least a happy path test for this driver, if already done, create a test with invalid selection or invalid variable name

Copy link
Member

@balsa-sarenac balsa-sarenac left a comment

Choose a reason for hiding this comment

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

Really nice work, left some minor comments, nothing too huge. Well done!


self setUpDriver: driver.

self should: [ driver runRefactoring ] raise: Error.
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice if we would expect as specific error as possible. I'm guessing this is RBRefactoringError?


self setUpDriver: driver.

self should: [ driver runRefactoring ] raise: Error.
Copy link
Member

Choose a reason for hiding this comment

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

Same comment as above

Comment on lines 115 to 118
firstCondition check ifFalse: [
self setErrorTextFromCondition: firstCondition onPresenter: presenter ].
secondCondition check ifFalse: [
self setErrorTextFromCondition: secondCondition onPresenter: presenter ].
Copy link
Member

Choose a reason for hiding this comment

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

If they both fail, it will only show the error text from the second one, right?

Comment on lines 49 to 55
{ #category : 'execution' }
ReExtractToTemporaryDriver >> requestSelector [

^ self requestUserInput: 'Please provide a selector'
label: 'The selector should be defined.'
validate: [ :string :presenter | self validateSelector: string onPresenter: presenter ]
]
Copy link
Member

Choose a reason for hiding this comment

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

Two things here, both are non-blocking:

  • since Driver should only be called through UI, chances that selector is invalid are close to 0, so this check is optional in the driver and will always pass
  • correct way of asking user for selector would be to use selection presenter that you fill with available selectors from the class, but again this is not needed since this will be correctly set always

This check is important when we are invoking this refactoring programatically, and it should then raise an exception, and not ask for new selector.

My suggestion might be to remove this code from driver. Would like to hear you thoughts as well @Ducasse

ReExtractToTemporaryDriver >> validateName: aName onPresenter: presenter [

| firstCondition secondCondition result |
firstCondition := ReIsValidInstanceVariableName new name: aName.
Copy link
Member

Choose a reason for hiding this comment

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

I would suggest to ask refactoring for preconditions like: refactoring preconditionValidInstVarName and refactoring preconditionCheckVariableName that way we can reduce duplication and avoid potential misconfiguration.

Comment on lines 33 to 45
SycExtractTempCommand >> asRefactorings [

^ {RBExtractToTemporaryRefactoring
extract: sourceNode sourceInterval
to: tempName
from: method selector
in: method origin}
| driver |
driver := ReExtractToTemporaryDriver new.
driver
extract: sourceNode sourceInterval
to: tempName
from: method selector
in: method origin.

^ driver runRefactoring.

]
Copy link
Member

Choose a reason for hiding this comment

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

Take a look at how were other drivers configured to run here. This does work, but there is a slightly better way:

  • override isComplexRefactoring to return false
  • this method is unused then
  • override execute with this code
    Take a look at SycRemoveClassCommand. I'm saying this since asRefactoring and isComplexRefactoring will be removed in the future and this will then not work.

…o provide better understand and woking tests. Removeing unsed or methods that are not needed.
@SandraSamardzic
Copy link
Contributor Author

I deleted the dialog related to the selector as you suggested @balsa-sarenac , before I didn't consider that there might not be a need for it, my mistake. If @Ducasse thinks otherwise, it's not a problem for me to add it again and change it so that it's an optional dialog.

src/Refactoring-UI/ReExtractToTemporaryDriver.class.st Outdated Show resolved Hide resolved
src/Refactoring-UI/ReExtractToTemporaryDriver.class.st Outdated Show resolved Hide resolved
{ #category : 'execution' }
ReExtractToTemporaryDriver >> requestVariableName [

newVariableName := self defaultRequestDialog
Copy link
Member

Choose a reason for hiding this comment

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

I think that source of your errors are the mocks. They are very tricky. Basically here we use defaultRequestDialog. We send it message title:, label: and validateAnswer:. We need mock for it that will answer for example self for first two, and on validateAnswer: it will answer newVariableName that we want.

]

{ #category : 'accessing' }
ReExtractToTemporaryDriver >> variableNameEditorPresenterClass [
Copy link
Member

Choose a reason for hiding this comment

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

Maybe this function's name should be defaultRequestDialog? Can you check how rename method did this mocking, might be helpful. If needed we can pair on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I used rename method as an example, it has methodNameEditorPresenterClass method. Rename method uses a requestDialogWith: and I just made a default one, I saw an example of it in the rename class refactoring. Now I see that my focus should be there, thank you @balsa-sarenac

Copy link
Member

Choose a reason for hiding this comment

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

I thought so, thats why I asked if you need both mocks. If you need help, reach out. Try to debug rename method to see exactly how was mock set up, and then with that knowledge create mock specific to this use-case.

Comment on lines 13 to 23
dialog := MockObject new.

dialog
on: #variableName
respond: (RBArgumentName name: 'tempVar').

dialog
on: #variableName
respond: (RBArgumentName name: #anotherVar).

driver requestDialog: dialog.
Copy link
Member

Choose a reason for hiding this comment

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

We craft these mocks based on how they are used in the driver. I'm not sure they are configured correctly. Basically what we do is create mock object. Go in the class and see exactly which messages are sent to this object. Each of them define here in on:respond:. Pay attention which message should return something meaningful, and which ones you don't care (if you don't care, return self).
For example requestDialog: receives name:, label: and validateAnswer:, right?

Main disadvantage of mocks is that they are tightly coupled to implementation and I'm not a big fan of them, but this is only way we found to test drivers.

Copy link
Member

@balsa-sarenac balsa-sarenac left a comment

Choose a reason for hiding this comment

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

Really nice work and improvement over last time!

Pay attention to these:

  • CI fails because this method is never called StVariableNameEditorPresenter>>#renameVariableAndClose:
  • there are two failing driver tests (first, second)

src/Refactoring-UI/ReExtractToTemporaryDriver.class.st Outdated Show resolved Hide resolved
src/Refactoring-UI/StVariableNameEditorPresenter.class.st Outdated Show resolved Hide resolved
src/Refactoring-UI/ReExtractToTemporaryDriver.class.st Outdated Show resolved Hide resolved
Comment on lines 82 to 86
newVariableName := self requestVariableName.
refactoring extract: sourceInterval to: newVariableName from: selector in: class.
shouldEscape ifTrue: [ ^ self ].
refactoring preconditionValidInstVarName check ifFalse: [ ^ self ].
refactoring preconditionCheckVariableName check ifFalse: [ ^ self ].
Copy link
Member

Choose a reason for hiding this comment

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

Looks like you don't have while false here anymore, and you are still using shouldEscape, is this on purpose? What happens when user types wrong name, do we offer it to give us new name with a helpful error message?

src/Refactoring-UI/StVariableNameEditorPresenter.class.st Outdated Show resolved Hide resolved
src/Refactoring-UI/StVariableNameEditorPresenter.class.st Outdated Show resolved Hide resolved
@Ducasse
Copy link
Member

Ducasse commented Oct 18, 2024

nearly there!

@Ducasse
Copy link
Member

Ducasse commented Oct 18, 2024

testFailureInvalidVariableNameWhenVariableIsAlreadyTemporary is failing

@Ducasse
Copy link
Member

Ducasse commented Oct 20, 2024

I could also integrate and skip that test. Let me know

@SandraSamardzic
Copy link
Contributor Author

@Ducasse the dialog is not working very well, so my focus is on that now. I will let you know when I fix the dialog so you can integrate it then we will see what is wrong with the tests

@Ducasse
Copy link
Member

Ducasse commented Oct 21, 2024

Super. Keep us up to date.

@Ducasse Ducasse added the Status: Need more work The issue is nearly ready. Waiting some last bits. label Oct 21, 2024
@Ducasse
Copy link
Member

Ducasse commented Nov 3, 2024

Hello do you have some update?

@SandraSamardzic
Copy link
Contributor Author

SandraSamardzic commented Nov 4, 2024

Hello @Ducasse, unfortunately I have been caught up with other commitments, which has slowed down my pace. Thankfully, these other obligations are getting resolved, and I hope that I will fix the dialog by Thursday. I apologize to keep you waiting.

@SandraSamardzic
Copy link
Contributor Author

I am sorry @Ducasse , I still haven't been able to fix the problems. There are two problems :

  • no error message is printed in the dialog when the precondition is not met,
  • refactoring does not make any changes.

It is possible that there are some other mistakes that I did not see. I don't do well with the presenter, not sure if the presenter is the problem or runRefactoring method. I will contact @balsa-sarenac , if he has time to take a look at the problems, maybe I should change the approach to the dialog. Again, apologies for the slow work.

@Ducasse
Copy link
Member

Ducasse commented Nov 9, 2024

Ok no problem.
What is the problem? Because may be we can merge and fix the rest in another PR?

@SandraSamardzic
Copy link
Contributor Author

SandraSamardzic commented Nov 11, 2024

Maybe it is possible, but I am not sure how big the problems are @Ducasse. When the refactoring is started, a dialog appears, which is created using the presenter class, if the precondition is not met, an error message should be displayed in the dialog, I set the error message to a label in the presenter but I am not sure how to print it. This is the first problem, the second problem is when the correct variable is entered and the refactoring should happen there are no changes, the variable should be created and have the value of the selected interval but that is not the case, I don't know why.

@Ducasse
Copy link
Member

Ducasse commented Nov 22, 2024

Ok @balsa-sarenac do you have some cycles for this?
Else we can integrate it and see later because I'm afraid that further changes will impact it.

@Ducasse Ducasse merged commit c1f5410 into pharo-project:Pharo13 Nov 25, 2024
1 of 2 checks passed
@Ducasse
Copy link
Member

Ducasse commented Nov 26, 2024

@balsa-sarenac when I integrated this PR it broke the build

Error: Package Refactoring-UI depends on the following classes:
RBInteractionDriver
You must resolve these dependencies before you will be able to load these definitions:
ReExtractToTemporaryDriver
ReExtractToTemporaryDriver>>#changes

I will add RBInteractionDriver to be able to load it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Need more work The issue is nearly ready. Waiting some last bits.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants