-
Notifications
You must be signed in to change notification settings - Fork 23
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
[WIP] lazy_find has hash and kwargs params: make both explicit for ruby 3 #97
Conversation
We branched 0.2.z for insights/topology work that would have broken/required significant updates to ManageIQ (specifically requiring concurrent_safe batch saving for every table, and the different method of sql batch deletion). Still a TODO of mine to get ManageIQ back on to master here but we're not ready for that yet. |
@@ -19,7 +19,7 @@ | |||
it "raises an exception when relation object is needed, but something else is provided" do | |||
expected_error = "Wrong index for key :vm_or_template, the value must be of type Nil or InventoryObject or InventoryObjectLazy, got: not_allowed_string" | |||
expect do | |||
persister.hardwares.lazy_find(:vm_or_template => "not_allowed_string") | |||
persister.hardwares.lazy_find({:vm_or_template => "not_allowed_string"}) |
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.
This is going to require a lot of changes in MIQ if we have to change the callers...
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.
True, but this is only because lazy_find
accepts a hash as the first argument and optional keyword arguments for the rest. Maybe I'm naive but I believe we generally used options hashes as the last argument and didn't really adopt keyword arguments generally.
The changes in core here are pretty small but it's possible we're not really exercising much of the code and we could find others like this in other repositories.
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 what's confusing is that the first parameter is named manager_uuid, when in fact it accepts a hash.
def lazy_find(manager_uuid, ref: primary_index_ref, key: nil, default: nil, transform_nested_lazy_finds: 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.
manager_ref
or reference
would be more accurate, and if manager_ref is only a single column you can just pass a string to lazy_find for simplicity
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 wonder if it's better to make that a keyword arg also, so something like:
def lazy_find(manager_ref:, ref: primary_index_ref, key: nil, default: nil, transform_nested_lazy_finds: false)
##############
persister.hardwares.lazy_find(manager_ref: {:vm_or_template => "not_allowed_string"})
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 wonder if it's better to make that a keyword arg also, so something like:
Maybe? Might just be lazy but vms.lazy_find("vm-123")
is pretty nice compared with having to pass vms.lazy_find(manager_ref: "vm-123")
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 we're doing a major version bump anyway...these methods have way too many options haha
Almost all of these could be handled automatically, the only one that can't is :default
but I'm not sure if that's used currently
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.
yeah agreed...the signatures here have always been pretty nasty.
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.
Is the first parameter to lazy_find
optional?
If it is non-optional, then I think ruby will be smart enough to know it is the first parameter and not part of the kwargs.
Other than that, think this is the new style of ruby and the path we need to take
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.
Is the first parameter to lazy_find optional?
No, it's a required positional argument.
def lazy_find(manager_uuid, ref: primary_index_ref, key: nil, default: nil, transform_nested_lazy_finds: false) |
EDIT by @Fryguy: my suggestion was copy-pasted as opposed to the real signature.
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 first param is already non-optional, (or can't be converted to a non-optional parameter) then LGTM
@@ -19,7 +19,7 @@ | |||
it "raises an exception when relation object is needed, but something else is provided" do | |||
expected_error = "Wrong index for key :vm_or_template, the value must be of type Nil or InventoryObject or InventoryObjectLazy, got: not_allowed_string" | |||
expect do | |||
persister.hardwares.lazy_find(:vm_or_template => "not_allowed_string") | |||
persister.hardwares.lazy_find({:vm_or_template => "not_allowed_string"}) |
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.
Is the first parameter to lazy_find
optional?
If it is non-optional, then I think ruby will be smart enough to know it is the first parameter and not part of the kwargs.
Other than that, think this is the new style of ruby and the path we need to take
EDIT: Ignore these examples, because they are wrong...see #97 (comment) old, wrong examplesPerhaps not for this PR, but to make it less clunky-looking, it might be worth it to use the double splat to our advantage and preserve the way callers originally called this. So, before this PR we had: def lazy_find(manager_uuid, ref: primary_index_ref, key: nil, default: nil, transform_nested_lazy_finds: false)
# callers
lazy_find("vm-1")
lazy_find(:ems_ref => ems_ref)
lazy_find(:name => "name", :ref => :by_name) and after this PR we have lazy_find("vm-1")
- lazy_find(:ems_ref => ems_ref})
+ lazy_find({:ems_ref => ems_ref})
- lazy_find(:name => "name", :ref => :by_name)
+ lazy_find({:name => "name"}, :ref => :by_name) However, if we change the signature instead to this: - def lazy_find(manager_uuid, ref: primary_index_ref, key: nil, default: nil, transform_nested_lazy_finds: false)
+ def lazy_find(manager_uuid = nil, ref: primary_index_ref, key: nil, default: nil, transform_nested_lazy_finds: false, **manager_uuid_hash)
+ manager_uuid ||= manager_uuid_hash then I think we can keep the same original signature and no warnings. We may also want to add a few more def lazy_find(manager_uuid = nil, ref: primary_index_ref, key: nil, default: nil, transform_nested_lazy_finds: false, **manager_uuid_hash)
raise ArgumentError, "only one of manager_uuid or manager_uuid_hash must be passed" unless !!manager_uuid ^ !!manager_uuid_hash.present?
manager_uuid ||= manager_uuid_hash
# callers (now same as the original callers, so no changes)
lazy_find("vm-1")
lazy_find(:ems_ref => ems_ref)
lazy_find(:name => "name", :ref => :by_name) |
Hmm, as a newbie to this method, I had no idea that the positional argument Making the positional argument an explicit hash seems better in the short term. It feels like fixing the method signature so variables match their contents and possibly go all kwargs or all hash would seem to be a more clear way forward. The last one is just confusing to me. |
I think that last one is supposed to be |
Yes, fixed my example.
Take another look...the general idea is change the signature (only a small localized change) so that none of the callers have to change. |
On relooking, maybe my examples are wrong...sorry about that. I was pulling some from the specs not realizing that the specs had some intentionally wrong examples 😩 Let me try to reformulate the examples, because I think the idea still has merit. |
Updated example using Before: def lazy_find_orig(manager_uuid, ref: "default_ref", key: nil, default: nil, transform_nested_lazy_finds: false)
puts manager_uuid, ref
end
def lazy_find_prop(manager_uuid = nil, ref: "default_ref", key: nil, default: nil, transform_nested_lazy_finds: false, **manager_uuid_hash)
manager_uuid ||= manager_uuid_hash
puts manager_uuid, ref
end
lazy_find_orig("vm-1")
# => vm-1
# => default_ref
lazy_find_prop("vm-1")
# => vm-1
# => default_ref
lazy_find_orig(:ems_ref => 1)
# => (pry):5: warning: Passing the keyword argument as the last hash parameter is deprecated
# => (pry):1: warning: The called method `lazy_find' is defined here
# => {:ems_ref=>1}
# => default_ref
lazy_find_prop(:ems_ref => 1)
# => {:ems_ref=>1}
# => default_ref
# NOTE^: This is fixed with the proposal such that we don't have to change any callers, and
# this is a large percentage of the changes in this PR.
lazy_find_orig({:name => "name"}, :ref => :by_name)
# => {:name=>"name"}
# => by_name
lazy_find_prop({:name => "name"}, :ref => :by_name)
# => {:name=>"name"}
# => by_name
lazy_find_orig({:name => "name"}, {:ref => :by_name})
# => (pry):7: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
# => (pry):1: warning: The called method `lazy_find' is defined here
# => {:name=>"name"}
# => by_name
lazy_find_prop({:name => "name"}, {:ref => :by_name})
# => (pry):7: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
# => (pry):1: warning: The called method `lazy_find' is defined here
# => {:name=>"name"}
# => by_name
# NOTE^: Same...we have to change the callers regardless
lazy_find_orig(:name => "name", :ref => :by_name)
# => (pry):4: warning: Passing the keyword argument as the last hash parameter is deprecated
# => (pry):1: warning: The called method `lazy_find' is defined here
# => {:name=>"name", :ref=>:by_name}
# => default_ref
lazy_find_prop(:name => "name", :ref => :by_name)
# => {:name=>"name"}
# => by_name
# NOTE^: This one is straight up broken on 2.7, but is also one that developers
# will likely do because it feels natural. Ladas noticed this and has an
# explicit test for it to prevent it.
# Proposal now allows this to work and doesn't produce wrong values, so
# the test probably can be removed. |
Interesting. I'm ok with holding off on this one. I feel like I want to see the changes needed in other repositories to see if a generalized solution like you're suggesting can be used to more surgically fix things in a way that makes sense while still fixing obviously wrong things like passing a hash as a second argument when we really wanted kwargs:
|
b92e780
to
bdde38a
Compare
Checked commits jrafanie/inventory_refresh@ca9e487~...bdde38a with ruby 2.6.3, rubocop 1.13.0, haml-lint 0.35.0, and yamllint spec/persister/local_db_finders_spec.rb
spec/persister/retention_strategies_spec.rb
spec/persister/serializing_spec.rb
spec/save_inventory/acyclic_graph_of_inventory_collections_spec.rb
|
This pull request has been automatically marked as stale because it has not been updated for at least 3 months. If these changes are still valid, please remove the Thank you for all your contributions! More information about the ManageIQ triage process can be found in the triage process documentation. |
Ruby 3 compatibility was implemented by #112 |
WIP because this is a branch direct PR... I'm not sure why master and 0.2.z diverged. See below
This is for ruby 3 support which requires we clarify when we're passing a hash or keyword arguments as the last argument in the method definition and invocation.
This PR uses 0.2.z as the baseline since rails 6 support is not on master but is on 0.2.z but master has other changes so the two branches have diverged.
If needed, we can apply missing PRs from 0.2.z onto master and apply this change there instead.
Let me know.