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

Refactor of the coercion and validation logic #62

Merged
merged 75 commits into from
Apr 30, 2021

Conversation

ifellinaholeonce
Copy link
Collaborator

Description

Hey there 👋 !
First off, thanks a lot for this gem!

I've written a refactor of the code here in the hopes of making it a little easier to maintain. I know this is a lot of changes coming all at once. I'm wondering if this is something you'd be interested in? Happy to make changes and address any concerns you might have.

Thanks a lot for your time. Looking forward to hearing from you and getting these changes to a place that you'd be interested in merging them in.

Highlights

  • I've made it a top priority to not change any of the existing specs, to ensure behaviour doesn't change.
  • I've added unit specs for the classes I've added to maintain high code coverage.
  • Parameter class was introduced to represent the current param being processed. It takes in name, value, type and options. It knows how to check for defaults, transform and call a new Validator on itself.
  • Validator class acts as a factory for creating subclasses with validation logic.
  • Coercion class also acts as a factory for coercing values based on the provided type. The logic for the different types of coercion live in subclasses, ie. Coercion::ArrayParam.

Todos

Additional Notes

Special thanks to @ClaytonPassmore for the help

@iMacTia
Copy link
Collaborator

iMacTia commented Apr 18, 2021

Thank you @ifellinaholeonce, this work is impressive! And very much welcome 👏! I want to take the proper time to review this and decide what to do as I think it may deserve a major version shipping.

May I ask what triggered you on doing this refactoring 😮?

@ifellinaholeonce
Copy link
Collaborator Author

That's really exciting to hear @iMacTia. Looking forward to hearing your thoughts on it.

Honestly, the refactor started by digging into what I thought was a bug at the time and then it continued on as an opportunity to learn and practice some skills. I had a great time doing it.

Copy link
Collaborator

@iMacTia iMacTia left a comment

Choose a reason for hiding this comment

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

@ifellinaholeonce thanks again for the massive contribution 👏!
Overall I'm impressed with the refactoring you've been doing on the gem and I very much like the result!

After careful consideration, I'd be inclined to make this a v1.0 release.
That means we'd not be restricted in keeping the gem fully backwards-compatible, although we want to minimise disruptions in order to make life easier for those upgrading 😄
We'd also need to list all breaking changes into a new UPGRADING.md document we can add to the root of the repo. I'd suggest to add it in this PR and fill it while implementing the braking changes.

If you also like that idea, I've left a few comments that I understand were not an option before this decision, and I hope you'll have the time and will to make these changes.
If that's not the case, then I completely understand that. You've already spent extensive time on this PR, so I'll be happy to take over for you if you prefer to focus your attention elsewhere.

lib/rails_param/param.rb Outdated Show resolved Hide resolved

# validate
validate!(params[name], name, options)
validate!(parameter)

if block_given?
Copy link
Collaborator

Choose a reason for hiding this comment

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

We could forward the &block to the parameter initialiser and then rely on the ArrayParameter and HashParameter classes to take care of nested attributes

lib/rails_param/param.rb Outdated Show resolved Hide resolved
spec/spec_helper.rb Outdated Show resolved Hide resolved
@ifellinaholeonce
Copy link
Collaborator Author

That's really exciting to hear! 😃 Thanks @iMacTia.

I'm happy to carry on with this. I'll address your comments on this PR. Likely in the next day or two.

@ifellinaholeonce
Copy link
Collaborator Author

Hey @iMacTia

Just following up here. It took me a little longer than expected, it was a bit of a tough nut to crack but I managed to address your thoughts on the MockController class. Ultimately, I moved all the logic from the base param! method into a new class ParamEvaluator which allowed me to remove the MockController class.

This is the commit where that is introduced: 44d875c

I haven't added a UPGRADING.md since all of these changes so far have been backward compatible.

Looking forward to your thoughts on this approach and where we can go from here.

@iMacTia
Copy link
Collaborator

iMacTia commented Apr 30, 2021

Thank you so much @ifellinaholeonce!
I can't wait to review this, will try to do it as soon as possible.
Thanks again for all the hard work 🙏🎉!

Copy link
Collaborator

@iMacTia iMacTia left a comment

Choose a reason for hiding this comment

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

This looks great and the important comments were all addressed.
Assuming tests will be green, I feel like we can get this merged, and then it will be just a bit of polishing before we release v1.0 👍


def validate!(param)
param.validate
ParamEvaluator.new(params).param!(name, type, options, &block)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is simply beautiful ❤️

Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: this file can now be removed and this method can live in rails_param.rb

expect { get :index, prepare_params(page: ["a", "b", "c"]) }.to raise_error(
RailsParam::Param::InvalidParameterError, %q('["a", "b", "c"]' is not a valid Integer))
expect { get :index, **prepare_params(page: ["a", "b", "c"]) }.to raise_error(
RailsParam::InvalidParameterError, %q('["a", "b", "c"]' is not a valid Integer))
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: This will be a backwards-incompatible change, as users of this gem will currently be rescuing the old namespace for this exception.
We could either mark this is as a breaking change in the UPGRADING.md file, or being more accommodating and alias the exception with the old namespace 🤔

Comment on lines +38 to +50
if block_given?
if parameter.type == Array
parameter.value.each_with_index do |element, i|
if element.is_a?(Hash) || element.is_a?(ActionController::Parameters)
recurse element, &block
else
parameter.value[i] = recurse({ i => element }, i, &block) # supply index as key unless value is hash
end
end
else
recurse parameter.value, &block
end
end
Copy link
Collaborator

Choose a reason for hiding this comment

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

I still feel like this bit should be delegated to the Array and Hash validators, but this can be done on a follow-up PR 👍

@iMacTia
Copy link
Collaborator

iMacTia commented Apr 30, 2021

Tests green, I'm merging this in 🎉 !
Thanks again @ifellinaholeonce, this is definitely a monumental piece of work and I really appreciate you taking the time to help us out.

Actually I was wondering if you'd be considering becoming a maintainer of this gem?
Think about it and let me know, we could then ask @nicolasblanco if he agrees as ultimately he'll need to make you one 😄

@iMacTia iMacTia merged commit 7ef3018 into nicolasblanco:master Apr 30, 2021
@ifellinaholeonce
Copy link
Collaborator Author

Hey @iMacTia I'm interested in learning a bit more about becoming a maintainer for the gem😄

Would you be open to a bit of back and forth about it? Happy to do email, or open an issue in this repo or anything really if you have a preference.

@iMacTia
Copy link
Collaborator

iMacTia commented May 9, 2021

Absolutely! We can start having a quick chat on the next steps and in the meantime I'll try reaching out to @nicolasblanco.
Are you on Gitter? If you are in any public room I can find you there and we can have a private chat.
Open to other solutions as well if you have idea, just trying to find a way to share contact details without letting the whole internet know 😂

@ifellinaholeonce
Copy link
Collaborator Author

haha yes I struggled with that same problem. You can find me on Gitter in the rails community 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants