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
Move meta related actions from ProjectController to its own CRUD controller #6422
Conversation
src/api/app/views/webui2/webui/projects/meta/_breadcrumb_items.html.haml
Outdated
Show resolved
Hide resolved
src/api/spec/controllers/webui/projects/meta_controller_spec.rb
Outdated
Show resolved
Hide resolved
7a33fac
to
d83b12b
Compare
src/api/app/views/webui2/webui/projects/meta/_breadcrumb_items.html.haml
Show resolved
Hide resolved
04e2d6c
to
6324bea
Compare
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.
Otherwise nice work 👍 and I like that we reduce the size of some of the big controllers step by step
39ac91b
to
fafdbed
Compare
455d0e3
to
a12dd99
Compare
39af632
to
0eb6aeb
Compare
src/api/app/services/meta_controller_service/project_updater.rb
Outdated
Show resolved
Hide resolved
src/api/app/services/meta_controller_service/request_validator.rb
Outdated
Show resolved
Hide resolved
03ab05c
to
84bf773
Compare
I very much like the approach of introducing service classes ❤️ 👍 But I would have some suggestions:
I imagine it could look something like this: module MetaControllerService
class ProjectUpdater
attr_reader :errors
def initialize(project: nil, request_data = {}, validator = ::MetaControllerService::RequestValidator)
@project = project
@request_data = request_data
@validator = validator.new(project: project, request_data: request_data)
end
def update
unless validator.valid?
errors = validator.errors
return
end
Project.transaction do
errors = project.update_from_xml(request_data)[:error]
project.store unless errors?
end
end
def valid?
validator.valid? && errors.present?
end
private
attr_writer :errors
attr_reader :project, :request_data
end
end def update
authorize @project, :update?
updater = ::MetaControllerService::ProjectUpdater.new(project: @project, request_data: @request_data)
updater.update
if updater.valid?
flash.now[:success] = 'Config successfully saved!'
200
else
flash.now[:error] = updater.errors.join("\n")
400
end
switch_to_webui2
render layout: false, status: status, partial: "layouts/#{view_namespace}/flash", object: flash What do you think? Anyway, really great work 👍 |
Thank you for the extended review. Code examples helps me a lot! <3 One issue that I have doing like that is breaking the Single Responsibility principle, coupling again the classes. In another hand the |
I don't see how this is violation Single Responsibility as this is a classic example of composition. Object orientation is all about sending messages between objects. As long as we only rely on the interface of the validator ( it 'is valid' do
validator = double("validator", valid: true)
updated = ProjectUpdater.new("foo", "bar" , validator)
...
end
it 'is invalid' do
validator = double("validator", valid: false)
updated = ProjectUpdater.new("foo", "bar" , validator)
...
end But I see your concern and to make this more clear, you could introduce an intermediate Service class which takes an updater and validator and composes them together like: class MetaService
def initialize(updater = ProjectUpdated, validator = MetaValidator)
# set instance variables
end
def save
validator.valid?
updater.update
# etc etc.
end But honestly, I think this would be overkill and the initial approach with the two classes is sufficient for now. This is just pseudo code to visualize my thoughts and is not intended to work right away. |
84bf773
to
43c0fc9
Compare
please check it @ChrisBr I renamed the I think for now it's ok as it is, I would like to try to submit in this sprint the same changes for |
@errors = [] | ||
end | ||
|
||
def call |
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.
See my other comment. I would prefer to change this method to a private validate
method:
class RequestValidator
def valid?
validate
@errors.empty?
end
private
def validate
# ... the validation code from previous call method
end
This has some advantages:
- keeping the interface as small as possible (one method less in the public interface)
- don't need to remember to call
call
before callingvalid?
- keeps the same behaviour than ActiveRecord
end | ||
|
||
def call | ||
@validator.call |
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 prefer to get rid of this additional call
call. I think this should behave similar to ActiveRecord which also does not require an additional method call before calling valid?
. See my other comment.
@validator = validator_klass.new(project: project, request_data: request_data) | ||
end | ||
|
||
def call |
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.
[nitpick] I would prefer to use save
here to keep same interface as ActiveRecord.
@@ -0,0 +1,25 @@ | |||
module MetaControllerService | |||
class RequestValidator |
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 still don't think that the class name reflects what this class is doing: MetaValidator
would make it more clear I think.
@@ -0,0 +1,22 @@ | |||
module MetaControllerService | |||
class MetaValidator |
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 don't think the class name reflects what this class is doing, it does not validate the Meta but the XML, so MetaXmlValidator
might be a better name.
end | ||
|
||
def errors | ||
@errors.is_a?(Array) ? @errors.join("\n") : @errors |
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 not always having an array? Just instantiate the error instance variable with an empty array and use <<
instead.
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.
we can hide this implementation detail out of the controller. So if its ok I would keep it where it is.
43c0fc9
to
a8ab63f
Compare
a8ab63f
to
ca91eb0
Compare
626dd56
to
5c94e8f
Compare
Move
save_meta
andmeta
fromProjectController
toProjects::MetaController
ProjectController
Projects::MetaController#update
action